Site hosted by Angelfire.com: Build your free website today!

Un programa MFC Simple

En este tutorial examinaremos una pieza de programa MFC simple para ganar un entendimiento de su estructura y marco conceptual. Iniciaremos mirando el MFC en sí y luego miraremos como MFC es usado para crear aplicaciones.

Una introducción a MFC

MFC es una gran jerarquía C++ que hace los desarrollos de aplicaciones de Windows significativamente más fáciles. MFC es compatible con toda la familia Windows. Con cada nueva versión MFC se modifica de forma que el código antiguosiga funcionando con el nuevo sistema. MFC se extiende, y añade nuevas capacidades a la jerarquía y lo hace más fácil crear aplicaciones completas.

La ventaja de usar MFC y C++ en lugar de accesar directamente la API desde C es que MFC ya contiene y encapsulta todo el código de los programas escritos en C. Los programas escritos en MFC son por eso mucho más pequeños que sus equivalentes en C. Por otro lado, MFC tiene buen rendimiento y no es realmente una carga que vuelva los programas más lentos.

La mejor parte de usar MFC es que hace todo el trabajo duro por usted. La jerarquía contiene miles y miles de lineas de código de Windows, correcto, optimizado y robusto. Muchas de las funciones miembro que usted llama invocan código que le hubiera tomado semanas escribir usted mismo. De esta forma, MFC acelera tremendamente el ciclo de desarrollo de su proyecto.

MFC es bastante grande. Por ejemplo, la versión 4.0 tiene una jerarquía de 200 clases. Afortunadamente, usted no tiene que usarlas todas en un programa típico. De hecho, es posible crear software espectacular usando solo alrededor de 10 clases disponibles en MFC. La jeraraquía es partida en varias categorías de clases que incluyen:

Nos concentraremos en los objetos visuales. La lista inferior muestra la porción de la jerarquía de clases que tiene que ver con soporte de aplicación y de windows.

Hay varias cosas que hay que notar en la lista. Primero, la mayoría de las clases en MFC se derivan de una clase vase llamada CObject. Esta clase contiene datos miembros y funciones miembro que son comunes a la mayoría de las clases MFC. La segunda cosa para notar es la simplicidad de la lista. La clase CWinApp es usada siempre que usted crea una aplicación y sus controles usados. La clase CWnd cuenta con todas las características comunes que tienen las ventanas, recuadros de diálogo y controles. La clase CFrameWnd es derivada de CWnd e implementa una ventana de aplicación de marco normal. CDialog maneja los dos sabores normales de diálogos: modales y no modales respectivamente. CView es usado para dar un acceso de usuario a un documento a través de una ventana. Finalmente, Windows soporta se tipo de controles nativos: texto estático, texto editable, botones, barras de scroll, listas, y recuadros de Combo (una forma extendida de lista). Una vez usted entiende esta pequeña cantidad de piezas, usted estará bien en su vía hacia de un entendimiento completo de MFC. Las otras clases en la jerarquía MFC implementan otras características tales como administración de memoria, control de documentos, soporte de bases de datos, y demás.

Para crear un programa en MFC, usted usa las clases directamente o, más comúnmente, deriva las nuevas clases de las clases existentes. En las clases derivadas usted crea un nuevas funciones miembro, que permiten que las instancias de la clase se comporten apropiadamente en su aplicación. Usted puede ver este proceso de derivación en el programa que usamos en el documento anterior. A continuación colocamos de nuevo el código fuente:

// hello.cpp

#include <afxwin.h>

// Declaramos la clase de la aplicación
class CAplicacionBasica: public CWinApp {
public:
  virtual BOOL InitInstance();
};

// Crea una instancia de la clase de aplicación
CAplicacionBasica MiPrimeraAplicacion;

// Declara la clase de la ventana Principal
class CVentanaPrincipal: public CFrameWnd {
  CStatic *cs;
public
  CVentanaPrincipal();
}

// La función InitInstance es llamada cada vez que la aplicación se ejecuta
BOOL MiPrimeraAplicacion: InitInstance() {
  m_pMainWnd = new CHelloWindow();
  m_pMainWnd->ShowWindow(m_CmdShow);
  m_pMainWnd->UpdateWindow();
  return TRUE;
}

// El constructor para la Clase Ventana
CVentanaPrincipal:CVentanaPrincipal() {
  // Crea la ventana en sí
  Create(NULL,"Esta es mi primera aplicación");
  // Crea un componente Etiqueta estático
  cs = new CStatic();
  cs->Create("Hola Aplicación",WS_CHILD|WS_VISIBLE|SS_CENTER,CRect(50,80,150,150),this);
}

CAplicacionBasica y CVentanaPrincipal son derivadas de clases MFC existentes.

Diseño de un Programa

Antes de discutir el código en sí, es útil discutir brevemente el proceso de diseño de un programa bajo MFC. Como ejemplo, imagine que usted quiere crear un programa que despliega el mensaje "Hola Aplicación" al usuario. Esta es obviamente una aplicación muy sencilla pero aún así requiere algo de pensamiento.

Una aplicación de estas necesita primero crear una ventana en la pantalla que contenga las palabras "Hola aplicación". Luego necesita colocar esas palabras dentro de la ventana. Se requieren tres objetos para lograr esta tarea:

  1. Un objeto aplicación que inicializa la aplicación y se engancha con Windows. El objeto aplicación maneja todo el procesamiento de eventos a bajo nivel.
  2. Un objeto ventana que actúa como la ventana de aplicación principal.
  3. Un objeto de texto estático que mantena la etiqueta de texto "Hola Aplicación.

Todo programa que usted cree en MFC contendrá los primeros dos objetos. El tercer objeto es único para esta aplicación en particular. Cada aplicación definirá su propio conjunto de objetos de interfaz de usuario que desplieguen la salida de la aplicación así como los que pediran información al usuario.

Una vez haya completado el diseño de la interfaz de usuario y haya decidido cuales controles necesitará la interfaz, escribe el código para crear los controles en la pantalla. Usted también escribe código que maneja los mensajes generados por estos controles a medida que son manipulados por el usuario. En el caso de esta aplicación, solo un control de interfaz es necesario. Otras aplicaciones pueden contener cientos de controles organizados en la ventana principal y los diálogos.

Es importante notar que hay dos formas de crear los controles de usuario en un programa. El método descrito aquí es C++ directamente para crear los controles. En una aplicación grande, sin embargo, este método es doloroso. Crear los controles para una aplicación con 50 o 100 diálogos usando código C++ tomaría demasiado tiempo. Por eso, un segundo método usa archivos de recursos para crear los controles con un editor gráfico. Este método es mucho más rápido y funciona bien.

Entendiendo el Codigo de Nuestra aplicación

El listado inferior muestra el código del programa que usted vió en el documento anteiror. Los números de línea han sido añadidos para permitir la discusión sobre el código en la sección próxima.

1 //hello.cpp

2 #include <afxwin.h>

3 // Declara la clase aplicacion
4 class CAplicacionBasica : public CWinApp
5 {
6   public:
7     virtual BOOL InitInstance();
8 };

9 // Crea una instancia de la clase aplicación
10 CAplicacionBasica MiPrimeraAplicacion;

11 // Declara la clase de la ventana Principal
12 class CVentanaPrincipal : public CFrameWnd
13 {
14     CStatic* cs;
15   public:
16     CVentanaPrincipal();
17 };

18 // La función InitInstance es llamada cada vez
19 // que la aplicación se ejecuta
20 BOOL CAplicacionBasica::InitInstance()
21 {
22   m_pMainWnd = new CHelloWindow();
23   m_pMainWnd->ShowWindow(m_nCmdShow);
24   m_pMainWnd->UpdateWindow();
25   return TRUE;
26 }

27 // El constructor para la clase ventana
28 CVentanaPrincial::CVentanaPrincipal()
29 {
30   // Crea la ventana
31   Create(NULL,
32     "Hello World!",
33     WS_OVERLAPPEDWINDOW,
34     CRect(0,0,200,200));
35   // Create a static label
36   cs = new CStatic();
37   cs->Create("Hola Aplicación",
38     WS_CHILD|WS_VISIBLE|SS_CENTER,
39     CRect(50,80,150,150),
40     this);
41 }

Tome un momento para mirar mire el programa. El programa consiste en 6 pequeñas partes, cada una con una parte importante.

El programa primero incluye afxwin.h (línea 2). Este archivo de encabezado contiene todos los tipos, clases, fucniones y variables usadas en MFC. También incluye otros archivos de encabezado para las bibliotecas de API de Windows.

2 #include <afxwin.h>

De la Línea 3 a la línea 8 se deriva una nueva clase aplicación llamada CAplicacionBasica a partir de la clase CWinApp declarada en MFC. La nueva clase es creada de forma que la función miembro InitInstance en la clase CWinApp pueda ser sobreescrita. InitInstance es una función virtual que es llamada cuando la aplicación comienza ejecución.

3 // Declara la clase aplicacion
4 class CAplicacionBasica : public CWinApp
5 {
6 public:
7   virtual BOOL InitInstance();
8 };

En la lïnea 10 el código declara una instancia del objeto aplicación como una variable global. Esta instancia es importante porque hace que el programa pueda ejecutarse. Cuando la aplicación es cargada en memoria y comienza ejecución, la creación de esta variable global hace que el constructor de la clase CWinApp se ejecute. Este constructor automáticamente llama a la función InitInstance definida en las línea entre 18 y 26.

10 CAplicacionBasica MiPrimeraAplicacion;

En las líneas de 11 a 17, la clase CVentanarincipal es derivada de la clase CFrameWnd declarada en MFC. CVentanaPrincipal actúa como la ventana principal de aplicación. Se crea una nueva clase de forma que un constructor, destructor, y los miembros de datos puedan ser implementados.

11 // Declara la clase de la ventana Principal
12 class CVentanaPrincipal : public CFrameWnd
13 {
14     CStatic* cs;
15   public:
16     CVentanaPrincipal();
17 };

Las líneas 18 a 26 implementan la función InitInstance. Esta función crea una instancia de la clase CVentanaPrincipal, haciendo así que el constructor de la clase en las líneas 27 a 41 se ejecute. También hace que la nueva ventana aparezca en la pantalla.

18 // La función InitInstance es llamada cada vez
19 // que la aplicación se ejecuta
20 BOOL CAplicacionBasica::InitInstance()
21 {
22   m_pMainWnd = new CHelloWindow();
23   m_pMainWnd->ShowWindow(m_nCmdShow);
24   m_pMainWnd->UpdateWindow();
25   return TRUE;
26 }

Las líneas 27 a 41 implementan el constructor de la ventana. El constructor realmente crea la ventana y luego crea un control estático dentro de él.

27 // El constructor para la clase ventana
28 CVentanaPrincial::CVentanaPrincipal()
29 {
30   // Crea la ventana
31   Create(NULL,
32     "Hello World!",
33     WS_OVERLAPPEDWINDOW,
34     CRect(0,0,200,200));
35   // Create a static label
36   cs = new CStatic();
37   cs->Create("Hola Aplicación",
38     WS_CHILD|WS_VISIBLE|SS_CENTER,
39     CRect(50,80,150,150),
40     this);
41 }

Una cosa interesante a notar en este programa es que no hay funcion main o WinMain, y no hay ningún loop de eventos aparente. Sin embargo, sabemos que este programa procesa eventos. La ventana puede ser minimizada, maximidaza, movida y demás. Toda esta actividad es escondida en la clase principal de la aplicación CWinApp y no tenemos por eso que preocuparnos acerca del manejo de evento que es automático e invisible en MFC.

Las siguientes secciones describen las diferentes piezas de este programa en más detalle. Es probable que toda esta información tendrá sentido completamente ahora: es mejor leerlo de una sola vez para que capte los conceptos de primera vez. En el siguiente documento, donde se discuten un número de ejemplos específicos, las diferentes partes estarán juntas y se clarificarán.

El Objeto Aplicación

Todo programa que usted crea en MFC contendrá un objeto de aplicación simple que usted deriva de la clase CWinApp. Este objeto debe ser declarado globalmente (línea 10) y puede existir solo una vez en un programa dado.

Un objeto derivado de la clase CWinApp maneja la inicialización de la aplicación, así como el loop principal de eentos para el programa. La clase CWinApp tiene varios datos miembro, y un número de funciones miembro. Por ahora, casi todos no son imporantes. Si usted quiere ojear algunas de estas funciones, sin embargo, busque por CWinApp en el archivo de ayuda MFC. En el programa anterior, hemos sobreescrito una sola función virual en CWinApp, que es la función InitInstance.

El propósito del objeto aplicación es inicializar y controlar su aplicación. Puesto que Windows permite múltiples instancias de la misma aplicación, MFC rompe el proceso de inicialización en dos partes y usa dos funciones: InitApplication e InitInstance. Aquí solo hemos usado la función InitInstance por la simplicidad de la aplicación. Es llamada cada vez que una nueva instancia de la aplicación es invocada. El código en la línea 3 a 8 crea una clase llamada CAplicacionBasica derivada de CWinApp.

3 // Declara la clase aplicacion
4 class CAplicacionBasica : public CWinApp
5 {
6 public:
7   virtual BOOL InitInstance();
8 };

Dentro de la función InitInstance sobreescrita en las líneas 18 a 26, el programa crea y despliega la ventana usando los datos miembro de CAplicacionBasica llamados m_pMainWnd:

18 // La función InitInstance es llamada cada vez
19 // que la aplicación se ejecuta
20 BOOL CAplicacionBasica::InitInstance()
21 {
22   m_pMainWnd = new CHelloWindow();
23   m_pMainWnd->ShowWindow(m_nCmdShow);
24   m_pMainWnd->UpdateWindow();
25   return TRUE;
26 }

La función InitInstance retorna un valor verdadero para indicar que la inicialización fue completada exitosamente. Si la función retorna un valor FALSE, la aplicación terminaría inmediatamente.

Cuando la aplicación es creada en la línea 10, sus datos miembro (heredados de CWinApp) son automáticamente inicializados. Por ejemplo, m_pszAppName, m_lpCmdLine, y m_nCmdShow todos contienen valores apropiados.

El Objeto Ventana

MFC define dos tipos de ventanas: 1) ventanas de marco, que son totalmente funcionales que pueden ser redimensionadas, minimizadas y demás. y 2) ventanas de diálogo, que no son redimensionables. Una ventana de marco es típicametne usada para la ventana de aplicación principal de un programa. En el código mostrado la clase CVentanaPrincipal es derivada de CFrameWnd :

11 // Declara la clase de la ventana Principal
12 class CVentanaPrincipal : public CFrameWnd
13 {
14     CStatic* cs;
15   public:
16     CVentanaPrincipal();
17 };

La derivación contiene un nuevo constructor, junto con los datos miembro que apuntarán al control de interfaz de usuario usado en el programa. Cada aplicación que usted crea tendrá un conjunto único de controles que residen en la ventana principal de la aplicación. Por eso, la clase derivada tendrá un constructor sobreescrito que crea todos los controles requeridos en la ventana principal. Típicamente esta clase también tendrá un destructor sobreescrito para borrarlos cuando la ventana se cierra, pero el destructor no es usado aquí. Luego veremos que la clase derivada declarará un manejador de mensajes para manejar los mensajes que estos controles producen en respuesta a los eventos de los usuarios.

Típicamente, cualquier aplicación que usted crea tendrá una ventana de aplicación principal. La clase de aplicación CAplicacionBasica por ello define un miembro dato llamado m_pMainWnd que puede apuntar a esta ventana principal. Para crear la ventana principal de esta aplicación la función InitInstance (lineas 18 a 26) crea una instancia de CVentanaPrincipal y usa m_pMainWnd para apuntar a la nueva ventana. Nuestro objeto CVentanaPrincipal es creada en la línea 22:

18 // La función InitInstance es llamada cada vez
19 // que la aplicación se ejecuta
20 BOOL CAplicacionBasica::InitInstance()
21 {
22   m_pMainWnd = new CHelloWindow();
23   m_pMainWnd->ShowWindow(m_nCmdShow);
24   m_pMainWnd->UpdateWindow();
25   return TRUE;
26 }

Crear simplemente una ventana marco (frame window) no es suficiente. Se requieren otros dos pasos para asegurarse que la nueva ventana aparece en la ventana correctamente. Primero, el código debe llamar la función ShowWindow de la ventana para hacer que aparezca la ventana en la línea 23. Segundo, el programa debe llamar a la función UpdateWindow para asegurarse que cada control, y que cualquier dibujo al interior de la ventana, sea pintando correctamente en la pantalla (line 24).

Usted puede preguntarse donde están definidas las funciones ShowWindow y UpdateWindow. Por ejemplo, si usted quisiera buscarlas y aprender más sobre ellas podría mirar en el archivo de ayuda de MFC en la descripción de la clase CFrameWnd. Sin embargo, CFrameWnd no contiene ninguna de estas funciones miembro. CFrameWnd a su vez hereda sus comportamientos de la clase CWnd. Si usted se refiere a CWnd en la documentación MFC, encontrará que es una clase inmensa que contiene cerca de 200 funciones diferentes. Obviamente, usted no va a manejar esta clase en particular en un par de minutos, pero dentro de muchas funciones útiles se encuentran ShowWindow y UpdateWindow.

Puesto que estamos hablando del tema, tomese un minuto para mirar la función CWnd::ShowWindow en el archivo de ayuda MFC. Note que ShowWindow acepta un parámetro simple, y que el parámetro puede ser asignado a uno de diez valores diferentes. Hemos usado uno de estos valores en nuestro programa en la línea 23: m_nCmdShow. La variable m_nCmdShow es inicializada basada en las condiciones asignadas por el usuario al iniciar la aplicación. Por ejemplo, el usuario puede haber iniciado la aplicación desde el Administrador de ARchivos y haberle dicho que lo iniciara en un estado minimizado asignando el checkbox en el diálogo de propiedades de la aplicación. La variable m_nCmdShow será asignada a SW_SHOWMINIMIZED, y la aplicación iniciará en un estado icónico. La variable m_nCmdShow es una forma para comunicarse con el mundo exterior al iniciar la aplicación. Si usted quiere experimentar, puede tratar reemplazando m_nCmdShow en la llamada a ShowWindow con los diferentes valores constantes definidos para ShowWindow. REcompile el programa y vea qué hace.

La línea 22 incializa la ventana. Localiza la memoria para ella llamando la función new. En este punto de la ejecución del programa se llama el contructor de CVentanaPrincipal. El constructor es llamado cuando una instancia de la clase se creada. Dentro del constructor del aventana, la ventana debe crearse a sí misma. Esto lo hace llamando a la función Create para la clase CFrameWnd en la línea 31:

27 // El constructor para la clase ventana
28 CVentanaPrincial::CVentanaPrincipal()
29 {
30   // Crea la ventana
31   Create(NULL,
32     "Hello World!",
33     WS_OVERLAPPEDWINDOW,
34     CRect(0,0,200,200));
35   // Create a static label
36   cs = new CStatic();
37   cs->Create("Hola Aplicación",
38     WS_CHILD|WS_VISIBLE|SS_CENTER,
39     CRect(50,80,150,150),
40     this);
41 }

Se pasan cuatro parámetros a la función de creación. Mirando en la documentación de MFC puede ver los diferentes tipos. El parámetro NULL inicial indica que un nombre de clase por defecto será usado. El segundo parámetro es el título de la ventana que aparecerá en la barra de título. El tercer parámetro es el atributo de estilo para la ventana. ESte ejemplo indica que debería crearse una ventana normal sobrelapable. El cuarto parámetro especifica que la ventana debería ser puesta en la pantalla con su esquina superior izquierda en los puntos 0,0, y que el tamaño de la ventana inicial debería ser 200 x 200 pixeles. Si se usa el valor rectDefault como cuarto parámetro, Windows colocará y redimensionará la ventana automáticamente para usted.

Puesto que este es un programa extremadamente sencillo, crea un control de texto estático dentro de la ventana. En este ejemplo en particular el programa usa una etiqueta estática de texto como su único control, si es creada en las líneas 35 a 40.

El Control de Texto Estático

El control de Texto Estático se deriva la clase CVentanaPrincipal de la clase CFrameWnd (Líneas 11 a 17). Al hacer eso declara un miembro de datos privado de tipo CStatic *, así como un constructor.

11 // Declara la clase de la ventana Principal
12 class CVentanaPrincipal : public CFrameWnd
13 {
14     CStatic* cs;
15   public:
16     CVentanaPrincipal();
17 };

Como se vió en la sección previa, el constructor CVentanaPrincipal hace dos cosas. Primero crea la ventana de la aplicación al llamar a la función Create (línea 31), y luego localiza y crea los controles que pertenecen a la ventana. En este caso una etiqueta estática es usada como único control. La creación de Objetos es siempre un proceso de dos pasos en MFC. Primero, se localiza la memoria para la instancia de la clase, llamando al constructor que inicializa cualquier variable. Luego, una función Create explícita es llamada para realmente crear el objeto en pantalla. El código localiza, construye y crea un objeto de texto estático simple usando un proceso de dos pasos en las líneas 36 a 40:

27 // El constructor para la clase ventana
28 CVentanaPrincial::CVentanaPrincipal()
29 {
30   // Crea la ventana
31   Create(NULL,
32     "Hello World!",
33     WS_OVERLAPPEDWINDOW,
34     CRect(0,0,200,200));
35   // Create a static label
36   cs = new CStatic();
37   cs->Create("Hola Aplicación",
38     WS_CHILD|WS_VISIBLE|SS_CENTER,
39     CRect(50,80,150,150),
40     this);
41 }

El constructor del item CStatic es llamado cuando se localiza la memoria para él, y luego se llama una función Create explícita para crear la ventana de control CStatic. Los parámetros usados en la función Create aquí son similares a la e ventana en la línea 31. El primer parámetro especifica el texto a ser desplegado por el control. El segundo parámetro especifica los atributos de estilo. Aquí hemos pedido que el control sea una ventana hija (desplegada dentro de otra ventana), que debería ser visible, y que el texto dentro del control debería estar centrado. El tercer parámetro determina el tamaño y posición del control estático. El cuarto indica la ventana padre para la cual este control es un hijo. Habiendo creado el control estático, aparecerá en la ventana de aplicación y desplegará el Texto adecuado.

Conclusion

Al mirar este código la primera vez, parecerá poco familiar y por esto potencialmente molesto. No se preocupe. La única parte del programa que importa desde la perspectiva del programa es el código de creación de CStatic entre las líneas 36 a 40. El resto usted lo escribirá y luego lo ignorará.