NovaMonoFix
Errores PHP
X
Usuario
Password
0 FPS

Tutorial WinAPI C++ 3.3 (Creación del ObjetoEditBox)

06 de Noviembre del 2010 por Josep Antoni Bover, 0 visitas, 0 comentarios, 0 votos
Categorías : Windows, Programación, C y C++.
La web está en segundo plano, animación en pausa.
Cargando animación...
Tutorial WinAPI C++ 3.3 (Creación del ObjetoEditBox)

En este tutorial veremos cómo crear controles estándar de windows un poco en general, y en especial nos centraremos en el control EDITBOX.

Hasta ahora nos habíamos creado nuestros controles partiendo del ObjetoControl, pero ahora queremos utilizar un control de windows que ya tiene varios eventos programados, por lo que vamos a tener que enlazar a ellos. La diferencia entre ObjetoControl y ObjetoControlEstandar residirá en que ObjetoControl controla eventos que nosotros hemos programado completamente, y ObjetoControlEstandar controla eventos programados por microsoft en los cuales podemos añadir código al principio o al final según nos convenga.

Veamos la declaración de ObjetoControlEstandar :

Archivo : ObjetoControlEstandar.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Clase que hereda PlantillaEventos y se especializa en controles
class ObjetoControlEstandar : public PlantillaEventos<LRESULT, GESTOR_POR_DEFECTO> {
public : //////////////////// Miembros publicos
// -Constructor
ObjetoControlEstandar(void);
// -Destructor
~ObjetoControlEstandar(void);
// -Funcion que conecta el control estandar con esta clase
HWND Asignar(HWND hWndParent, const int ID_Control);
protected :
// -Funcion que conecta el control estandar con esta clase
void _ConectarControl(void);
private : /////////////////// Miembros privados
// -Gestor de mensajes estatico inicial
static LRESULT CALLBACK _GestorMensajes( HWND nhWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
// -WindowProcedure Orignal del control estándar
WNDPROC _GestorMensajesOriginal;
};

Podemos ver :

Veamos… cuando creamos un control propio como por ejemplo el ObjetoBoton, nos estamos creando su WindowProcedure/GestorMensajes y lo adaptamos a nuestras necesidades. En cambio si utilizamos algún control estándar de windows, este trae su WindowProcedure por defecto.

A la hora de tratar los eventos no es lo mismo responder a un evento que hemos programado nosotros, que a un evento de un control estándar, ya que ademas de responder al evento debemos llamar también al WindowProcedure original del control para que nos procese el evento y nos actualice el control tal y como lo programo microsoft.

Veamos el _GestorMensajes de ObjetoControlEstandar :

Archivo : ObjetoControlEstandar.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
LRESULT CALLBACK ObjetoControlEstandar::_GestorMensajes(HWND nhWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
ObjetoControlEstandar *Control = reinterpret_cast<ObjetoControlEstandar *>(LongToPtr(GetWindowLongPtr(nhWnd, GWL_USERDATA)));
if (Control == NULL) return FALSE;
LRESULT Ret = 0;
if (uMsg == WM_PAINT) {
return (Control->_GestorMensajesOriginal)(nhWnd, uMsg, wParam, lParam);
}
else {
Ret = Control->GestorMensajes(uMsg, wParam, lParam);
if (Ret == USAR_GESTOR_POR_DEFECTO) return (Control->_GestorMensajesOriginal)(nhWnd, uMsg, wParam, lParam);
}
return Ret;
};

Exceptuando el mensaje WM_PAINT, cuando recibimos un mensaje lo procesamos primero nosotros y luego miramos si ha devuelto USAR_GESTOR_POR_DEFECTO. En caso de devolver USAR_GESTOR_POR_DEFECTO llamaremos al GestorMensajesOriginal del control estándar para que se use el código de microsoft.

Pongamos por ejemplo que tenemos un EditBox de windows bajo la clase ObjetoControlEstandar y recibimos el Evento_Teclado_TeclaPresionada. En el caso de retornar 0 lo que estariamos haciendo es decirle a ObjetoControlEstandar que no mande el mensaje al GestorMensajesOriginal del EditBox, por lo que no se mostraría la nueva tecla presionada en el control. En cambio si retornamos USAR_GESTOR_POR_DEFECTO, además de procesar nuestro código se procesara el código que pintara la tecla presionada en el EditBox.

Teniendo esto claro ya podemos empezar con el EditBox, veamos su declaración :

Archivo : ObjetoEditBox.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Clase que hereda ObjetoControlEstandar y se centra en las funciones del editbox
class ObjetoEditBox : public ObjetoControlEstandar {
public : //////////////////// Miembros publicos
// -Constructor
ObjetoEditBox(void);
// -Destructor
~ObjetoEditBox(void);
// -Función para crear el EditBox
void CrearEditBox( HWND hWndParent, const TCHAR *nTexto, const UINT nEstilos,
const int cX, const int cY, const int cAncho, const int cAlto,
const UINT nID );
// -Función para obtener el texto del EditBox
UINT ObtenerTexto(TCHAR *nTxt, const UINT TamTxt);
// -Función para asignar el texto del EditBox
BOOL AsignarTexto(const TCHAR *nTxt);
protected : ///////////////// Miembros protegidos
HFONT _Fuente;
};

Por el momento solo necesitamos una función para crear el EDITBOX, una función para asignar su texto y otra función para devolver su texto. Además en miembros protegidos declararemos una fuente que será la que usara para imprimir el texto dentro del EditBox.

Veamos la función CrearEditBox :

Archivo : ObjetoEditBox.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
// Función para crear el EditBox
void ObjetoEditBox::CrearEditBox( HWND hWndParent, const TCHAR *nTexto, const UINT nEstilos,
const int cX, const int cY, const int cAncho, const int cAlto,
const UINT nID ) {
ObjetoIniciarCommonControls::Iniciar();
if (_hWnd != NULL) return;
_hWnd = CreateWindowEx( NULL, TEXT("EDIT"), nTexto, nEstilos, cX, cY, cAncho, cAlto, hWndParent,
reinterpret_cast<HMENU>(IntToPtr(nID)), GetModuleHandle(NULL), NULL );
_ConectarControl();
_Fuente = CreateFont( 13, 0, 0, 0, FW_NORMAL, false, false, false, DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FF_ROMAN, TEXT("Tahoma") );
SendMessage(_hWnd, WM_SETFONT, (WPARAM)_Fuente , 0);
}

Lo primero que se hace es llamar a la función ObjetoIniciarCommonControls::Iniciar. Esta función llama a la API InitCommonControlsEx para iniciar los controles estándar de windows para nuestra aplicación. Antiguamente en VC6 esto era necesario si queríamos trabajar con controles estándar, pero ahora mismo a decir verdad ya no se si es necesario, porque en mi maquina me funcionan igual los ejemplos tanto si llamo a InitCommonControlsEx como si no… y en la MSDN no dice nada.. Por lo que prefiero mantener este codigo por si las moscas.

Lo segundo que se hace es llamar a la API CreateWindowEx con el segundo parámetro “EDIT” (que es el nombre de la clase). Esto hace que el control creado se convierta en un EditBox. Para ver más nombres de control mira este enlace de la MSDN : CreateWindow (Casi al final hay una tabla que muestra los nombres de classe mas comunes)

Lo siguiente es llamar a la función _ConectarControl, que re-emplazara el WindowProcedure del EditBox por el nuestro.

Y por último creamos una fuente con la API CreateFont para el editbox, y le mandamos el mensaje WM_SETFONT al editbox con la API SendMessage, cosa que le dirá al editbox que debe usar esa fuente.

Ya solo nos queda ver las funciones ObtenerTexto y AsignarTexto :

Archivo : ObjetoEditBox.cpp
1
2
3
4
5
6
7
8
9
// Función para obtener el texto del EditBox
UINT ObjetoEditBox::ObtenerTexto(TCHAR *nTxt, const UINT TamTxt) {
return GetDlgItemText(GetParent(_hWnd), GetWindowLongPtr(_hWnd, GWL_ID), nTxt, TamTxt);
}
// Función para asignar el texto del EditBox
BOOL ObjetoEditBox::AsignarTexto(const TCHAR *nTxt) {
return SetDlgItemText(GetParent(_hWnd), GetWindowLongPtr(_hWnd, GWL_ID), nTxt);
}

Para obtener el texto del editbox utilizamos la API GetDlgItemText, y para asignarlo utilizamos la API SetDlgItemText. Además utilizamos la API GetWindowLongPtr para obtener la ID del editbox que necesitamos a la hora de asignar / obtener su texto.

Con esto ya tenemos suficiente para cumplir con los requisitos del instalador y el ensamblador, pero debéis saber que para obtener eventos del editbox debemos mirar si recibimos el mensaje WM_COMMAND en la ventana padre, y a partir de allí mirar que código de notificación nos da para determinar que acción nos está reportando. De todas formas puede que necesitemos obtener un evento que no retorne el editbox por defecto, como por ejemplo cuando el usuario presiona la tecla intro. En este caso como tenemos el objeto encapsulado de tal forma que re-emplazamos su window procedure original, bastaría con crear el Evento_Teclado_TeclaPresionada dentro del EditBox, desde dentro de la función del evento mandaríamos un mensaje al objeto EventosPadre. Luego habría que añadir una función virtual nueva en el objeto EventosPadre "EditBox_Evento_Teclado_Intro" que enlazaríamos en el GestorMensajes con el mensaje que mandamos desde ObjetoEditBox::Evento_Teclado_TeclaPresionada.

Si aun no lo veis muy claro, un buen ejemplo seria el Evento_ObjetoBoron_Click (parte del GestorMensajes):

Archivo : PlantillaEventos.h
1
2
3
// ObjetoBoton Click
case WM_BOTON_CLICK :
return this->Evento_ObjetoBoton_Click(reinterpret_cast<ObjetoBoton *>(wParam), static_cast<UINT>(lParam));

La declaración de PlantillaEventos::Evento_ObjetoBoron_Click :

Archivo : PlantillaEventos.h
1
2
3
4
5
// -Función enlazada al mensaje WM_BOTON_CLICK
virtual TIPO_DEVUELTO Evento_ObjetoBoton_Click( ObjetoBoton *BtnPresionado,
const UINT nBoton ) {
return VALOR_DEVUELTO;
};

Y como mandamos el mensaje desde el ObjetoBoton utilizando las API's PostMessage y GetParent :

Archivo : ObjetoBoton.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LRESULT ObjetoBoton::Evento_Mouse_BotonSoltado(const UINT Boton, const int cX, const int cY, const UINT Params) {
ReleaseCapture();
if (_Presionado == true) {
RECT RectaControl;
GetClientRect(_hWnd, &RectaControl);
POINT Pt = { cX, cY };
_Presionado = false;
_Estado = ObjetoBoton_EstadoNormal;
// Si el mouse esta dentro del control mandamos el mensaje WM_BOTON_CLICK a la ventana padre del botón.
if (PtInRect(&RectaControl, Pt) != 0) {
UINT IDBoton = ID();
PostMessage(GetParent(_hWnd), WM_BOTON_CLICK, reinterpret_cast<WPARAM>(this), static_cast<LPARAM>(Boton));
}
}
Repintar(true);
return 0;
}

Para el caso del evento TeclaPresionada_Intro, con que incluyamos la ID del EditBox en el WPARAM, no hace falta mucho mas, por lo que podríamos dejar el LPARAM vacio. La función virtual EditBox_Evento_Teclado_Intro solo recibiria un UINT como parametro que seria la ID que mandamos antes en el WPARAM.

Para mas información referente al control EditBox consulta el siguiente enlace de la MSDN : Edit Control.

Siguiente tutorial : 3.4 Creación del ObjetoButton.