TN026: Rutinas DDX y DDV
Nota:
La nota técnica siguiente no se ha actualizado desde que se incluyó por primera vez en la documentación en línea. Como resultado, algunos procedimientos y temas podrían estar obsoletos o ser incorrectos. Para obtener información más reciente, se recomienda buscar el tema de interés en el índice de la documentación en línea.
En esta nota se describe la arquitectura de intercambio de datos de cuadro de diálogo (DDX) y de validación de datos de cuadro de diálogo (DDV). También se describe cómo escribir un procedimiento DDX_ o DDV_, y cómo puede ampliar ClassWizard para usar las rutinas.
Información general sobre el intercambio de datos de cuadro de diálogo
Todas las funciones de datos de cuadro de diálogo se realizan con código de C++. No hay recursos especiales ni macros mágicas. El centro del mecanismo es una función virtual que se invalida en cada clase de cuadro de diálogo que realiza el intercambio de datos de cuadro de diálogo y la validación. Siempre se encuentra en este formato:
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX); // call base class
//{{AFX_DATA_MAP(CMyDialog)
<data_exchange_function_call>
<data_validation_function_call>
//}}AFX_DATA_MAP
}
Los comentarios de AFX de formato especial permiten a ClassWizard buscar y editar el código en esta función. El código que no es compatible con ClassWizard debe colocarse fuera de los comentarios de formato especial.
En el ejemplo anterior, <data_exchange_function_call> tiene el formato siguiente:
DDX_Custom(pDX, nIDC, field);
y <data_validation_function_call> es opcional y tiene el formato siguiente:
DDV_Custom(pDX, field, ...);
Puede incluirse más de un par DDX_/DDV_ en cada función DoDataExchange
.
Vea "afxdd_.h" para obtener una lista de todas las rutinas de intercambio de datos de cuadro de diálogo y rutinas de validación de datos de cuadro de diálogo proporcionadas con MFC.
Los datos del cuadro de diálogo son solo eso: datos de miembro de la clase CMyDialog
. No se almacenan en una estructura ni en nada similar.
Notas
Aunque se les llama "datos de cuadro de diálogo", todas las características están disponibles en cualquier clase derivada de CWnd
y no se limitan solo a los cuadros de diálogo.
Los valores iniciales de los datos se establecen en el constructor estándar de C++, normalmente en un bloque con comentarios //{{AFX_DATA_INIT
y //}}AFX_DATA_INIT
.
CWnd::UpdateData
es la operación que realiza la inicialización y el control de errores en torno a la llamada a DoDataExchange
.
Puede llamar a CWnd::UpdateData
en cualquier momento para realizar el intercambio de datos y la validación. De manera predeterminada se llama a UpdateData
(TRUE) en el controlador CDialog::OnOK
predeterminado y a UpdateData
(FALSE) en el valor predeterminado CDialog::OnInitDialog
.
La rutina DDV_ debe seguir inmediatamente a la rutina de DDX_ para ese campo.
Cómo funciona
No es necesario comprender lo siguiente para poder usar los datos del cuadro de diálogo. Pero comprender cómo funciona en segundo plano le ayudará a escribir su propio procedimiento de intercambio o validación.
La función miembro DoDataExchange
es muy similar a la función miembro Serialize
: se encarga de obtener o establecer datos en un formulario externo, o desde este (en este caso, los controles de un cuadro de diálogo) desde los datos de miembro de la clase o hacia estos. El parámetro pDX es el contexto para realizar el intercambio de datos y es similar al parámetro CArchive
en CObject::Serialize
. pDX (un objeto CDataExchange
) tiene una marca de dirección muy parecida a como CArchive
tiene una marca de dirección:
Si es
!m_bSaveAndValidate
, cargue el estado de datos en los controles.Si es
m_bSaveAndValidate
, establezca el estado de datos de los controles.
La validación solo se produce cuando se establece m_bSaveAndValidate
. El valor de m_bSaveAndValidate
lo determina el parámetro BOOL en CWnd::UpdateData
.
Hay otros tres miembros CDataExchange
interesantes:
m_pDlgWnd
: ventana (normalmente un cuadro de diálogo) que contiene los controles. Este miembro sirve para evitar que los autores de llamadas del funciones globales DDX_ y DDV_ tengan que pasar "this" a todas las rutinas DDX/DDV.PrepareCtrl
yPrepareEditCtrl
: prepara un control de cuadro de diálogo para el intercambio de datos. Almacena el identificador del control para establecer el foco si se produce un error en una validación.PrepareCtrl
se usa para los controles que no son de edición yPrepareEditCtrl
para los controles de edición.Fail
: se le llama después de que aparezca un cuadro de mensaje que alerte al usuario del error de entrada. Esta rutina restaurará el foco en el último control (la última llamada aPrepareCtrl
oPrepareEditCtrl
) y generará una excepción. Se puede llamar a esta función miembro desde las rutinas DDX_ y DDV_.
Extensiones de usuario
Hay varias maneras de ampliar el mecanismo de DDX/DDV predeterminado. Puede:
Agregar tipos de datos nuevos.
CTime
Agregar procedimientos de intercambio (DDX_) nuevos.
void PASCAL DDX_Time(CDataExchange* pDX, int nIDC, CTime& tm);
Agregar procedimientos de validación (DDV_) nuevos.
void PASCAL DDV_TimeFuture(CDataExchange* pDX, CTime tm, BOOL bFuture); // make sure time is in the future or past
Pasar expresiones arbitrarias a los procedimientos de validación.
DDV_MinMax(pDX, age, 0, m_maxAge);
Nota:
ClassWizard no puede editar estas expresiones arbitrarias y, por lo tanto, deben moverse fuera de los comentarios de formato especial (//{{AFX_DATA_MAP(CMyClass)).
Haga que la función miembro DoDataExchange
incluya condicionales o cualquier otra instrucción válida de C++ con llamadas a funciones de intercambio y validación entremezcladas.
//{{AFX_DATA_MAP(CMyClass)
DDX_Check(pDX, IDC_SEX, m_bFemale);
DDX_Text(pDX, IDC_EDIT1, m_age);
//}}AFX_DATA_MAP
if (m_bFemale)
DDV_MinMax(pDX, age, 0, m_maxFemaleAge);
else
DDV_MinMax(pDX, age, 0, m_maxMaleAge);
Nota:
Tal como se ha mostrado anteriormente, ClassWizard no puede editar este código y solo se debe usar fuera de los comentarios de formato especial.
Compatibilidad con ClassWizard
ClassWizard admite un subconjunto de personalizaciones de DDX/DDV permitiéndole integrar sus propias rutinas DDX_ y DDV_ en la interfaz de usuario de ClassWizard. Esto solo resulta beneficioso si tiene pensado reutilizar rutinas DDX y DDV concretas en uno o en muchos proyectos.
Para ello, se realizan entradas especiales en DDX.CLW (versiones anteriores de Visual C++ almacenaban esta información en APSTUDIO.INI) o en el archivo .CLW del proyecto. Las entradas especiales se pueden especificar en la sección [Información general] del archivo .CLW del proyecto o en la sección [ExtraDDX] del archivo DDX.CLW en el directorio \Archivos de programa\Microsoft Visual Studio\Visual C++\bin. Es posible que tenga que crear el archivo DDX.CLW si aún no existe. Si tiene previsto usar las rutinas DDX_/DDV_ personalizadas solo en un proyecto determinado, agregue las entradas a la sección [Información general] del archivo .CLW del proyecto en su lugar. Si tiene previsto usar las rutinas en muchos proyectos, agregue las entradas a la sección [ExtraDDX] del archivo DDX.CLW.
El formato general de estas entradas especiales es el siguiente:
ExtraDDXCount=n
donde n es el número de líneas ExtraDDX? que se van a seguir, del formato
ExtraDDX?=keys; vb-keys; prompt; type; initValue; DDX_Proc [; DDV_Proc; prompt1; arg1 [; prompt2; fmt2]]
donde ? es un número 1-n que indica el tipo DDX que es de la lista que se está definiendo.
Cada campo lo delimita un carácter ";". Los campos y su finalidad se describen a continuación.
keys
Lista de caracteres únicos que indican para qué controles de cuadros de diálogo se permite este tipo de variable.
Carácter Control permitido E edit C Casilla de dos estados c Casilla de tres estados R Primer botón de radio de un grupo L Cuadro de lista no ordenado l Cuadro de lista ordenado M Cuadro combinado (con editar elemento) N Lista desplegable no ordenada n Lista desplegable ordenada 1 Si se debe agregar la inserción DDX al inicio de la lista (el valor predeterminado es agregarla al final). Esto se suele usar para rutinas DDX que transfieren la propiedad "Control". vb-keys
Este campo solo se usa en el producto de 16 bits para los controles VBX (los controles VBX no se admiten en el producto de 32 bits).
prompt
Cadena que se va a colocar en el cuadro combinado Propiedad (sin comillas)
type
Identificador único del tipo que se va a emitir en el archivo de encabezado. En nuestro ejemplo anterior con DDX_Time, esto se establecería en CTime.
vb-keys
No se usa en esta versión y siempre debe estar vacío
initValue
Valor inicial: 0 o en blanco. Si está en blanco, no se escribirá ninguna línea de inicialización en la sección //{{AFX_DATA_INIT del archivo de implementación. Se debe usar una entrada en blanco para objetos de C++ (como
CString
,CTime
, etc.) que tengan constructores que garanticen una inicialización correcta.DDX_Proc
Identificador único para el procedimiento de DDX_. El nombre de la función de C++ debe comenzar por "DDX_", pero no incluya "DDX_" en el identificador de <DDX_Proc>. En el ejemplo anterior, el identificador de <DDX_Proc> sería Time. Cuando ClassWizard escribe la llamada de función al archivo de implementación en la sección {{AFX_DATA_MAP, anexa este nombre a DDX_, llegando de este modo a DDX_Time.
comentario
Comentario que se mostrará en el cuadro de diálogo para la variable con este DDX. Coloque cualquier texto que quiera aquí y, por lo general, proporcione algo que describa la operación que realiza el par DDX/DDV.
DDV_Proc
La parte DDV de la entrada es opcional. No todas las rutinas DDX tienen rutinas DDV correspondientes. A menudo, es más conveniente incluir la fase de validación como parte integral de la transferencia. Esto suele ocurrir cuando la rutina DDV no requiere ningún parámetro, ya que ClassWizard no admite rutinas DDV que no tengan parámetros.
arg
Identificador único para el procedimiento de DDV_. El nombre de la función de C++ debe comenzar por "DDV_", pero no incluya "DDX_" en el identificador de <DDX_Proc>.
arg va seguido de uno o dos argumentos DDV:
promptN
Cadena para colocar encima del elemento de edición (con y para acelerador).
fmtN
Carácter de formato para el tipo arg; es uno de los siguientes:
Carácter Tipo d int u int sin firma D long int (es decir, long) U long unsigned (es decir, DWORD) f float V doble s string