TN014: Controles personalizados
En esta nota se describe la compatibilidad de MFC con controles personalizados y de auto dibujo. También se describen las subclases dinámicas y se describe la relación entre los objetos CWnd y los manipuladores HWND
.
La aplicación de ejemplo de MFC CTRLTEST muestra cómo usar muchos controles personalizados. Consulte el código fuente del ejemplo general de MFC CTRLTEST y la ayuda en línea.
Controles y menús dibujados por el propietario
Windows proporciona compatibilidad con los controles y menús dibujados por el propietario mediante mensajes de Windows. La ventana primaria de cualquier control o menú recibe estos mensajes y llama a las funciones en respuesta. Puede invalidar estas funciones para personalizar la apariencia visual y el comportamiento del control o menú dibujado por el propietario.
MFC admite directamente los elementos dibujados por el propietario con las funciones siguientes:
Puede invalidar estas funciones en la clase derivada CWnd
para implementar el comportamiento de dibujo personalizado.
Este enfoque no da lugar a código reutilizable. Si tiene dos controles similares en dos clases CWnd
diferentes, debe implementar el comportamiento de control personalizado en dos ubicaciones. La arquitectura de control de auto dibujo compatible con MFC resuelve este problema.
Controles y menús de auto dibujados
MFC proporciona una implementación predeterminada (en las clases CWnd
y CMenu) para los mensajes estándar dibujados por el propietario. Esta implementación predeterminada descodificará los parámetros dibujados por el propietario y delegará los mensajes dibujados por el propietario en los controles o el menú. Esto se denomina autodibujado porque el código de dibujo está en la clase del control o menú, no en la ventana del propietario.
Mediante el uso de controles autodibujados, puede crear clases de control reutilizables que usen la semántica de dibujo del propietario para mostrar el control. El código para dibujar el control está en la clase de control, no en su elemento primario. Se trata de un enfoque orientado a objetos para la programación de controles personalizados. Agregue la siguiente lista de funciones a las clases autodibujadas:
Para los botones autodibujados:
CButton:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw this button
Para los menús autodibujados:
CMenu:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this menu CMenu:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this menu
Para los cuadros de lista autodibujados:
CListBox:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this list box CListBox:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this list box CListBox:CompareItem(LPCOMPAREITEMSTRUCT); // insert code to compare two items in this list box if LBS_SORT CListBox:DeleteItem(LPDELETEITEMSTRUCT); // insert code to delete an item from this list box
Para los cuadros combinados autodibujados:
CComboBox:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this combo box CComboBox:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this combo box CComboBox:CompareItem(LPCOMPAREITEMSTRUCT); // insert code to compare two items in this combo box if CBS_SORT CComboBox:DeleteItem(LPDELETEITEMSTRUCT); // insert code to delete an item from this combo box
Para más información sobre las estructuras dibujadas por propietario (DRAWITEMSTRUCT, MEASUREITEMSTRUCT, COMPAREITEMSTRUCT, y DELETEITEMSTRUCT), consulte la documentación de MFC para CWnd::OnDrawItem
, CWnd::OnMeasureItem
, CWnd::OnCompareItem
y CWnd::OnDeleteItem
respectivamente.
Uso de controles y menús autodibujados
En el caso de los menús autodibujados, debe invalidar los métodos OnMeasureItem
y OnDrawItem
.
Para los cuadros de lista y los cuadros combinados autodibujados, debe invalidar OnMeasureItem
y OnDrawItem
. Debe especificar el estilo LBS_OWNERDRAWVARIABLE para los cuadros de lista o el estilo CBS_OWNERDRAWVARIABLE para los cuadros combinados de la plantilla de diálogo. El estilo OWNERDRAWFIXED no funcionará con elementos autodibujados porque el alto del elemento fijo se determina antes de que los controles autodibujados se adjunten al cuadro de lista. (Puede usar los métodos CListBox::SetItemHeight y CComboBox::SetItemHeight para superar esta limitación).
Cambiar a un estilo OWNERDRAWVARIABLE obligará al sistema a aplicar el estilo NOINTEGRALHEIGHT al control. Dado que el control no puede calcular un alto entero con elementos de tamaño variable, se omite el estilo predeterminado de INTEGRALHEIGHT y el control siempre es NOINTEGRALHEIGHT. Si los elementos son de alto fijo, puede impedir que se dibujen elementos parciales al especificar el tamaño del control para que sea un multiplicador de enteros del tamaño del elemento.
En el caso de los cuadros de lista y los cuadros combinados con el estilo LBS_SORT o CBS_SORT, debe invalidar el método OnCompareItem
.
En el caso de los cuadros de lista y los cuadros combinados autodibujados, OnDeleteItem
normalmente no se invalida. Puede invalidar OnDeleteItem
si quiere realizar cualquier procesamiento especial. Un caso en el que esto sería aplicable es cuando se almacena memoria adicional u otros recursos con cada cuadro de lista o elemento de cuadro combinado.
Ejemplos de controles y menús autodibujados
El ejemplo general de MFC CTRLTEST proporciona ejemplos de un menú autodibujado y un cuadro de lista autodibujado.
El ejemplo más típico de un botón autodibujado es un botón de mapa de bits. Un botón de mapa de bits es un botón que muestra una, dos o tres imágenes de mapa de bits para los distintos estados. Se proporciona un ejemplo de esto en la clase CBitmapButton de MFC.
Subclases dinámicas
En ocasiones, se recomienda cambiar la funcionalidad de un objeto que ya existe. En los ejemplos anteriores era necesario personalizar los controles antes de crearlos. La subclases dinámica permite personalizar un control que ya se ha creado.
La creación de subclases es el término de Windows para reemplazar el método WndProc de una ventana por un método WndProc
personalizado y llamar al método WndProc
antiguo para la funcionalidad predeterminada.
Esto no debe confundirse con la derivación de clases de C++. A modo de aclaración, la clase base de términos de C++ y la clase derivada son análogas a la superclase y subclase en el modelo de objetos de Windows. La derivación de C++ con MFC y creación de subclases de Windows es funcionalmente similar, excepto que C++ no admite la creación dinámica de subclases.
La clase CWnd
proporciona la conexión entre un objeto de C++ (derivado de CWnd
) y un objeto de ventana de Windows (conocido como HWND
).
Hay tres maneras comunes en las que se relacionan:
CWnd
crea la claseHWND
. Se puede modificar el comportamiento en una clase derivada mediante la creación de una clase derivada deCWnd
.HWND
se crea cuando la aplicación llama a CWnd::Create.La aplicación adjunta una clase
CWnd
a un manipuladorHWND
existente. El comportamiento de la ventana existente no se modifica. Este es un caso de delegación y es posible al llamar a CWnd::Attach cuyo alias se va a establecer en un manipuladorHWND
existente en un objetoCWnd
.CWnd
está asociada a un manipuladorHWND
existente y puede modificar el comportamiento en una clase derivada. Esto se denomina creación dinámica de subclases porque vamos a cambiar el comportamiento y, por tanto, la clase de un objeto de Windows en tiempo de ejecución.
Puede lograr la creación dinámica de subclases mediante los métodos CWnd::SubclassWindow yCWnd::SubclassDlgItem.
Ambas rutinas asocian un objeto CWnd
a un manipulador HWND
existente. SubclassWindow
toma el manipulador HWND
directamente. SubclassDlgItem
es una función auxiliar que toma un identificador de control y la ventana primaria. SubclassDlgItem
está diseñado para adjuntar objetos de C++ a controles de diálogo creados a partir de una plantilla de diálogo.
Consulte el ejemplo CTRLTEST para obtener varios ejemplos de cuándo usar SubclassWindow
y SubclassDlgItem
.