Você está na página 1de 90

INTRODUCCIN: El entorno de desarrollo de VC++ 6.

0 ofrece muchas posibilidades al programador, desde crear aplicaciones de diversos formatos y caractersticas, pasando adems por la creacin de libreras DLL, iconos, bitmaps, cursores, etc. Todo el IDE, (entorno de desarrollo), en conjunto es una gran "cocina" para desarrollar las aplicaciones ms verstiles tanto para entorno Windows como DOS. Es importante aclarar que cuando se pretende crear una aplicacin que corra bajo Windows con VC++ se debe tener en cuenta si la misma estar basada en MFC o si ser una aplicacin WIN 32 estndar. Microsoft Foundation Class (MFC): Visual C++ se suministra junto con la biblioteca MFC que es un conjunto de clases predefinidas de C++. Estas clases han pasado a ser un estndar de desarrollo para aplicaciones Windows y poseen clases que facilitan mucho la programacin en C++, por ejemplo al incluir un tipo de dato CString para declarar cadenas; cosa que no existe en C y siempre es un dolor de cabeza. Tambin se pueden crear programas Windows que no usen MFC, as se obtendra una aplicacin con la cual no hara falta distribuir la librera MFC. "HOLA VISUAL C++": Para introducirse en un nuevo lenguaje no hay como un buen ejemplo y ste histricamente fue y es el famoso "Hola Mundo". Para comenzar a desarrollar esta pequea aplicacin ingresamos en VC++, luego seleccionamos NEW en el men FILE y aparecer la siguiente pantalla:

Esta pantalla muestra los distintos proyectos que uno puede desarrollar con VC++. Como queremos hacer una aplicacin basada en MFC seleccionaremos MFC AppWizard (exe). En VC++ como en VB se trabaja con PROYECTOS, a su vez stos en VC++ se agrupan en WORKSPACES ("espacios de trabajo"). Entonces en Project Name escribimos el nombre de nuestro proyecto, HolaVC, en Location por defecto se establecer la ruta de nuestro nuevo WORKSPACE y ser ARCHIVOS DE PROGRAMA\Microsoft Visual Studio\MyProjects\HolaVC. (esta opcin se puede modificar).

Una vez ingresado el nombre del proyecto veremos que el botn OK ya est activo si lo pulsamos damos inicio al ASISTENTE DE APLICACIONES MFC el cual nos crear un armazn de programa que despus habr que adecuar (algo as como la ventana por defecto que trae VB cuando uno crea un proyecto). Nos aparece la siguiente ventana:

Una aplicacin de Windows puede ser SDI (Single Document Interface), MDI (Multiple Document Interface) o Dialog Based (basada en dialogos) . Single Document es muy similar a Dialog Based, salvo que por defecto VC le agrega menes y MDI es una aplicacin como por ejemplo Word, con una ventana principal que puede contener a muchas otras en su interior (como un contenedor). Ahora seleccionaremos Dialog Based y como lenguaje de recursos (recursos es algo que ms adelante se explica en detalle) dejaremos por defecto Spanish, salvo que pretendamos que nuestra aplicacin se use en Alemania entonces seleccionaramos German..., : ) ste no ser nuestro caso. Luego de todo esto presionamos NEXT para pasar a la siguiente pantalla: Esta pantalla nos pregunta que caractersticas le queremos agregar a nuestra aplicacin, About Box, Context Sensitive Help (un botn que llame a un archivo de ayuda), 3D Controls, dejemos todo como est, y pasemos a la siguiente ventana pulsando nuevamente NEXT.

Aqu se nos est diciendo que se crear un proyecto MFC Standard y adems se nos pregunta si deseamos o no que el cdigo fuente generado automticamente por ste asistente est comentado; obviamente dejamos YES, PLEASE porque esos comentarios son muy tiles para no perderse. Tambin debemos especificar como queremos usar la librera MFC con el ejecutable: como DLL compartida (dinmica) o como librera esttica. Dejemos la primer opcin pues es comn en Windows que las aplicaciones funcionen de esta forma, no obstante si seleccionamos como librera esttica, obtendremos un ejecutable ms grande. Si pulsamos NEXT accedemos a la ltima pantalla de este asistente que nos ofrece la posibilidad de cambiar los nombres de las clases que est a punto de crear, esto habitualmente no se modifica. Pulsamos FINISH y se muestra una listado con las caractersticas del armazn de aplicacin que el asistente est por hacer, OK y listo. El asistente nos gener la base de una aplicacin que consta de una ventana con dos botones (aceptar y cancelar) con un texto en el centro y adems otra ventana (ms pequea), el Acerca de.... Este "armazn" es totalmente funcional, podemos ejecutar el programa haciendo click en el icono con el signo de exclamacin ! o precionando Ctrl + F5, nos dir que el subdirectorio DEBUG de nuestra aplicacin no existe y si deseamos crearlo, decimos que YES y tenemos el programa en ejecucin. Nos inform de la falta de la carpeta DEBUG por ser la primera vez en ejecutar el programa y esta carpeta se crea ya que por defecto todas la aplicaciones en desarrollo en VC++ contienen informacin de depuracin, que nos permite depurar el programa poniendo puntos de interrupcin, ejecutando paso a paso, etc., pero cuando terminemos nuestra aplicacin y se la entreguemos al usuario sta no debe contener informacin de depuracin pues har que descienda su performance y el ejecutable sea ms grande. Para quitar la informacin de DEBUG (slo cuando tengamos la aplicacin terminada), vamos a la opcin SET ACTIVE CONFIGURATION.. del men BUILD y seleccionamos RELEASE. En el entorno de desarrollo se nos presenta a la izquierda, una ventana que tiene 3 solapas: Class View, Resource View y File View. Seleccionando la solapa Class View, podremos ver que dice HolaVC classes, si expandimos esta descripcin haciendo click en l +, se desplegarn las clases,

(generadas por el asistente) que forman mi aplicacin: CAboutDlg, la clase de la ventana AboutBox, CHolaVCApp, la clase aplicacin que existe en todas las aplicaciones y CHolaVCDlg, la clase de la ventana principal (la que contiene los botones Aceptar y Cancelar, y donde luego agregaremos un botn ms). Adems hay una carpeta llamada Globals donde se agrupan las variables globales. Las clases a su vez contienen ms cosas: Funciones miembro y variables miembros. Precisamente gran parte del cdigo que uno escribe en VC++ se trata de nuevas funciones miembros que uno agrega a las clases existentes. En todo lenguaje de programacin visual, sea VB o VC++, se tiene un lugar del IDE(Entorno de desarrllo) donde uno puede disear el aspecto visual de la aplicacin y otro donde uno puede escribir el cdigo necesario para los objeto diseados de manera tal que las cosas respondan de acuerdo a lo que uno quera. En VC++ se accede al lugar donde se lleva a cabo el diseo seleccionando la solapa RESOURCE (recursos) de la ventana WORKSPACE, (la de la izquierda donde adems estn todas las clases). Al seleccionar RESOURCE se podr observar varias carpetas Dialog, Icon, String Table y Version. Expandiendo la carpeta Dialog veremos IDD_ABOUTBOX y IDD_HOLAVC_DIALOG estas son las dos ventanas "dilogos" creados automticamente por el Asistente de Aplicaciones. Nosotros modificaremos IDD_HOLAVC_DIALOG, entonces hacemos doble click en el mismo y se nos mostrar la ventana con los dos botones por defecto "Aceptar" y "Cancelar" lista para modificar. La pantalla debera presentar ms o menos lo siguiente:

Primero marcamos con el mouse el texto "A HACER:..." y lo eliminamos presionando la tecla suprimir. Luego de la barra de herramientas seleccionamos un botn y lo colocamos en algn lugar del cuadro de dilogo. (por lo pronto no toque los botones Aceptar y Cancelar que estn por defecto).

Luego que tenemos un nuevo botn, ahora hay que modificar algunas de sus propiedades, por ejemplo la propiedad Caption que por defecto es button1. Para acceder a las propiedades de los controles hacemos click con el derecho sobre el control, en este caso el botn, y seleccionamos del men contextual, "propiedades".

En la solapa General de la pantalla propiedades podremos modificar, entre otras cosas, la propiedad Caption (que es el texto que muestra el botn), y la propiedad ID, la cual es muy importante ya que con ese valor se reconocer al botn al escribir cdigo. Primero modifique la propiedad Caption y escriba "Pulseme"; tambien modificaremos el ID, pues el que tiene por defecto no es muy representativo de lo que el botn realizara. Escriba IDC_PULSEME (por convencin se recomienda dejar el prefijo IDC_ y luego escribir algo que represente la utilidad del control. No altere el resto de las propiedades, pues por ahora no las necesitamos, an as si lo desea puede seleccionar las restantes solapas y observar que otras propiedades puede modificar. Nuestra idea es que, al pulsar el botn "Pulsame", se muestre el mensaje: "HolaVC", por ejemplo a travs de un cuadro de mensaje. Para esto hay que tener en cuenta que se necesita una funcin que maneje la accin de "pulsar el botn", en Windows esta funcin as como muchas otras (mover el mouse, tocar una tecla del teclado, hacer doble click, etc), se denominan "Mensajes", pues estas acciones son precisamente mensajes entre las ventanas, que uno tiene que interceptar de alguna forma. Existen muchas formas de crear un manejador de mensajes, pero habitualmente se hace click con el derecho del mouse sobre el control al cual se quiere crear un manejador y se selecciona la opcin "ClassWizard". Tambin se puede acceder a ClassWizard en cualquier momento presionando Ctrl + W. ClassWizard es un asistente para la creacin de Mapas de Mensajes (manejadores de mensajes), funciones miembros, variables miembros, clases, etc.

En la lista Object Ids del ClassWizard selecciones IDC_PULSAME, pues queremos crear un mapa de mensajes para este control, y en la lista Messages selecciones

BN_CLICKED. Luego pulse Add Function y aparecer un mensaje alertando que est por crear la funcin miembro OnPulsame; si lo desea puede modificar este nombre, pero no es recomendable, entonces slo presione OK. La nueva funcin se habr sumado a la lista de Member Functions, pulse Edit Code para poder escribir el cdigo de esta nueva funcin. Aparecer lo siguiente: void CHolaVCDlg::OnPulseme() { // TODO: Add your control notification handler code here } Entre las llaves tendremos que escribir nuestro cdigo que permita mostrar el mensaje "HolaVC". La lnea en color verde es un comentario que nos indica que en esa seccin tiene que escribir uno el cdigo que actuar al pulsar el botn. Esta funcin, OnPulseme, forma parte de la clase base, CHolaVCDlg. Podemos cerciorarnos de su existencia si en la ventana Workspace seleccionamos la solapa ClassView; all veremos todas las funciones y variables miembros de esta y otras clases. Bien, escribamos entonces el cdigo para mostrar nuestro mensaje: MessageBox("HolaVC", "Mi Mensaje", MB_ICONINFORMATION); Slo esa lnea de cdigo es necesario escribir. Presione Ctrl + F5 y se ejecutar la aplicacin; si pulsa el botn aparecer el mensaje:

Los parmetros de la funcin MessageBox son: Texto a mostrar, Ttulo del mensaje e icono + botones a mostrar (en este caso como no se especifican los botones muestra por defecto OK). Botones posibles MB_ABORTRETRYIGNORE MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_YESNO MB_YESNOCANCEL Iconos posibles MB_ICONEXCLAMATION MB_ICONINFORMATION MB_ICONQUESTION MB_ICONSTOP

CUADROS DE EDICIN: Otro control importante en Windows (adems del botn), son los cuadros de edicin. En estos controles se basa prcticamente el 70% del ingreso de datos

en una aplicacin Windows. Por lo tanto es despus del botn, el control ms comn. Ahora realizaremos un programa que utilizar un cuadro de edicin, y el objetiv ser que, mientras escribimos algo en el cuadro de edicin, simultneamente se escribir lo mismo en el ttulo del dilogo (ventana). Entonces, vaya al men FILE NEW y seleccione, como en el ejemplo anterior, MFC AppWizard (exe) y ponga Edicion como Project Name. Contine con el Asistente de aplicaciones como en el primer ejemplo hasta crear la estructura bsica del programa. Luego que tenga en pantalla el dilogo con los dos botones por defecto Aceptar y Cancelar, agregue, desde la barra de controles, un cuadro de edicin (icono ab| ), y acceda a sus propiedades haciendo click con el derecho del mouse sobre el control. Verifique que la propiedad ID sea IDC_EDIT1 (por defecto tiene que ser esa), no va hacer falta que la modifique. Cierre el cuadro de propiedades.

Bien, como queremos que al momento de estar escribiendo cada carcter del texto que ingresamos en el cuadro de edicin, se muestre adems en la barra de ttulo de la ventana, debemos escribir cdigo para el mensaje EN_CHANGE del cuadro de edicin. Para crear este gestor de mensaje, debemos acceder al ClassWizard, esto se logra haciendo click con el derecho del mouse sobre el control de edicin y seleccionando la opcin ClassWizard del men contextual. En ClassWizard verifique que en la lista Object ID se encuentre seleccionado IDC_EDIT1 y en la lista Messages, EN_CHANGE. Presione Add Function, EN_CHANGE pasar a formar parte de la lista Member Functions, seleccinela y presione Edit Code. Escriba entonces el siguiente cdigo: void CEdicionDlg::OnChangeEdit1() { // TODO: If this is a RICHEDIT control, the control will not // send this notification unless you override the CDialog::OnInitDialog() // function and call CRichEditCtrl().SetEventMask()

// with the ENM_CHANGE flag ORed into the mask. // TODO: Add your control notification handler code here CEdit* edTexto; (1) CString strDato; (2) edTexto = (CEdit*) GetDlgItem(IDC_EDIT1); (3) edTexto->GetWindowText (strDato); (4) SetWindowText(strDato); (5) } Los comentarios que estn al principio (en verde), los coloc ClassWizard automaticamente y hacen referencia en el caso que se tratara de un control de texto enriquecido, que no enva directamente este mensaje. Esto por lo pronto no nos interesa. Antes de continuar analizando las lneas de cdigo enumeradas, conviene destacar lo siguiente: "TODOS LOS CONTROLES (OBJETOS) DE WINDOWS SON UNA VENTANA", esto quiere decir que todos los controles son derivados de la clase CWnd. El control de edicin es un objeto CWnd, y en muchas ocasiones dejaremos esto as, pero a veces es conveniente convertir el tipo de los controles a su clase correspondiente, en nuestro caso CEdit. Por esto en la linea (1) se declara la variables edTexto como un puntero a la clase CEdit, pues la intencin es asociar el cuadro de edicin con esta variable, para as poder hacer uso de las funciones miembro de la clase CEdit. En la lnea (2) se declara un variable de tipo CString (cadena), donde se almacenaran los caracteres que se van ingresando. Luego de las declaraciones de variables, debemos asociar el puntero con el cuadro de edicin. Esto hace la lnea (3) a travs de la funcin GetDlgItem a la cual se le pasa el ID del objeto a asociar con la variable; el problema es que GetDlgItem retorna un puntero a CWnd y yo quiere uno a CEdit, por eso se utiliza el operador: (Cedit*), que permite realizar la conversin al tipo deseado. Una vez que uno tiene la variable asociada con el control se pueden usar las funciones miembro de esa clase. En la lnea (4) se obtiene el texto del cuadro de edicin con GetWindowText a la cual se le pasa como parmetro la variable CString. Note que se llama la funcin por medio del operador: -> pues la variable es un puntero a CEdit y no una variable CEdit. En la ltima lnea, (5), se establece el ttulo del cuadro de dilogo por medio de la funcin SetWindowText a la cual se le pasa la variable CString que desde la lnea anterior viene cargada con el contenido del cuadro de edicin. Se escribe SetWindowText directamente porque por defecto actuar sobre la clase principal que es la que da origen al cuadro de dilogo, tambin se podra haber usado el puntero this que es el que apunta al objeto por defecto, (quedara: this -> SetWindowText(strDato). ) AGREGAR VARIABLES MIEMBROS: Para el caso de los controles que permiten ingreso de datos es muy importante el uso de esa informacin que probablemente se pasarn a diferentes gestores de mensajes que los procesar. Por ejemplo se ingresa una edad y en algn momento hay que validarla. Los datos contenidos en un control se transfieren a variables que luego el cdigo manipula, despus se pueden volver a volcar al control.

Para ejemplificar esto modificaremos la aplicacin anterior agregando un botn en el cuadro de dilogo. Escribiremos el cdigo adecuado para cuando se pulse el botn se invierta el contenido del cuadro de dialogo. En vez de agregar un nuevo botn, utilicemos el botn CANCELAR. Seleccinelo, vaya a Propiedades, modifique la propiedad Caption a Invertir y cambie el ID por IDC_INVERTIR. El Cuadro de dilogo debera quedar ms o menos as:

Primero crearemos una variable miembro para el cuadro de edicin; para esto accedemos a ClassWizard y seleccionamos la solapa Members Variables. Una vez ah se marca de la lista Controls ID, el ID del cuadro de edicin, (IDC_EDIT1), y se pulsa el botn Add Variable. En la ventana que aparece ingrese m_strDato como Member Variable Name, Category Value y Variable Type Cstring (puesto que el valor ingresado ser una cadena). Acepte esta ventana. Y acepte la ventana del ClassWizard. Bien, ya tenemos una variable para almacenar el contenido del cuadro de edicin. ClassWizard relaciona automticamente las variables miembro con sus clases, as que slo debemos escribir el cdigo que invierta lo ingresado y lo deje nuevamente en el mismo cuadro de edicin. Seleccione el botn Invertir y acceda a ClassWizard. Seleccione la solapa Messages Map, IDC_INVERTIR en Objects ID y BN_CLICKED en Messages. Presione Add Function. Acepte el nombre sugerido y la funcin se agregar a la lista Member Functions. Presione Edit Code. Estamos entonces en la funcin OnInvertir de la clase CedicionDlg donde escribiremos el cdigo que invertir lo ingresado. Escriba: void CEdicionDlg::OnInvertir() { // TODO: Add your control notification handler code here UpdateData(TRUE); (1) if (m_strDato.IsEmpty ()== FALSE) (2) { MessageBox("Ahora se invertir: " + m_strDato); (3) m_strDato.MakeReverse (); (4) UpdateData(FALSE); (5) } } Lo primero que hay que hacer es que el contenido del cuadro de edicin pase a la variable miembro. Esto hace la lnea (1) con la funcin UpdateData(TRUE) luego de esta linea la variable m_strDato contiene lo ingresado en el control.

En la lnea (2) se verifica que no est vaca (lo que inplica que el cuadro de edicin se dej en blanco). En caso de haber ingresado algo, entonces hay que darlo vuelta, se muestra un mensaje advirtiendo esto: lnea (3). En (4) se hace uso de la funcin MakeReverse() de la clase Cstring, que invierte la cadena sobre s misma. En lnea (5) se transfiere el contenido de la variable a el control. Resumiendo: Se declaran variables miembro para los controles cuando se necesita pasar el contenido de los mismos a funciones que los procesen. Los contenidos de los controles se transfieren a las variables por medio de la funcin UpdateData(TRUE). Y luego para transferir los valores de las variables a los controles se usa UpdateData(FALSE): CONTROLES: RADIO BUTTON, CHECK BOX Y GROUP BOX. Cree una aplicacin MFC basada en dilogo, tal como se hizo en los ejemplos anteriores y pngale autos como Project Name. La aplicacin har uso de controles de opcin (radio button), controles de veirficacin (check box) y marcos (GROUP BOX). Los controles de opcin son esos "circulitos" que uno selecciona y que automticamente otro queda sin efecto (sin seleccionar). Esto ocurre cuando los controles de opcin se consideran agrupados. En VC se agrupan de acuerdo al orden en que fueron colocados en el cuadro de dilogo, partiendo de un primer control con la propiedad GROUP activada y hasta que encuentre otro objeto con la misma propiedad GROUP activada. Para pasar en claro esto ltimo disee la aplicacin de acuerdo a los siguientes pasos. Seleccione un control GROUP BOX de la barra de herramientas. Escriba Marcas en la propiedad Caption del GROUP BOX. Luego coloque 3 radio buttons dentro del GROUP BOX Las propiedades de los tres botones de opcin sern: El primero: ID = IDC_BMW, CAPTION = BMW y tildar GROUP. El segundo: ID = IDC_AUDI, CAPTION = Audi y no tildar GROUP. El tercero: ID = IDC_OPEL, CAPTION = Opel y no tildar GROUP.

Se tilda GROUP slo en el primero para especificar que a partir de ese control en adelante (de acuerdo al orden de tabulacin), estarn relacionados los botones de opcin hasta encontrar un nuevo control con GROUP tildado. Agregue otro GROUP BOX a la izquierda de Marcas con la propiedad CAPTION = Tipos. Coloque 3 radio buttons dentro del GROUP BOX y especifquele las siguientes propiedades: El primero: ID = IDC_SEDAN, CAPTION = Sedan, tildar GROUP y en STYLE tildar PUSH LIKE. El segundo: ID = IDC_COUPE, CAPTION = Coup, no tildar GROUP y en STYLE tildar PUSH LIKE.

El tercero: ID = IDC_BREAK, CAPTION = Break, no tildar GROUP y en STYLE tildar PUSH LIKE. Aqu el primero como tiene la propiedad GROUP tildada da por finalizado el agrupamiento anterior y comienza el nuevo de los Tipos.

Por ltimo coloque 2 CHECK BOX a la izquierda: El primero ID = IDC_AIRE, Caption = con aire y GROUP tildado (da por finalizado el grupo anterior). El segundo ID = IDC_CD, Caption = con CD y GROUP sin tildar. Ms o menos as quedara el diseo:

Una vez diseada la ventana y los controles con sus respectivas propiedades especificadas; hay que crear las variables asociadas a los controles que manipularn los datos. Aqu los datos son, bsicamente, si un botn de opcin est activado o no. Se necesitar una variable por grupo de botones de opcin: una para el grupo "autos", asociada a la primer opcin: BMW y otra para el grupo "tipos", asociada a la primer opcin: SEDAN. Crear variables miembro: Seleccione el primer radio button del grupo Autos, (BMW) y acceda a ClassWizard. En ClassWizard vaya a la solapa Member Variables y seleccione IDC_BMW. Presione Add Variable; aparecer la ventana Add Member Variable. All ingrese m_Marca como Member Variable Name; seleccione Value en Category y Variable Type = int.

Haga lo mismo para el primer radio button del grupo Tipos (Sedn) y llame a la variable m_Tipo. La idea es que al presionar Aceptar, de acuerdo a las opciones seleccionadas muestre un mensaje informativo. Antes debemos asignarles a las variables recin creadas un valor inicial, para que as, al iniciar la aplicacin, estn seleccionados por defecto los primeros radio buttons de cada grupo. Esto se hace editando el constructor de la clase CAutosDlg: En la ventana WorkSpace, seleccione la solapa ClassView. Expanda la rama de la clase CAutosDlg. Haga doble click en la primer funcin: CAutosDlg(CWnd *pParent = NULL). Ver que las variables creadas tienen el valor 1, cmbielo por 0. Esto establece como seleccionada la primer opcin, (BMW en el primer grupo y Sedan en el segundo). Ahora escribiremos el cdigo del botn Aceptar. Puede acceder al mismo por el ClassWizard o simplemente haciendo doble click sobre el botn. Escriba: void CAutosDlg::OnOK() { // TODO: Add extra validation here CWnd *wndAuto; (1) CWnd *wndTipo; (2) CString strMensa; (3) CString strAuto, strTipo, strExtra; (4) CButton* bAire; (5) CButton* bCD; (6) //Convierto los dos Check que originalmente son de //tipo CWnd a tipo CButton para poder hacer uso de //las funciones de CButton. bAire = (CButton*) GetDlgItem(IDC_AIRE); (7) bCD = (CButton*) GetDlgItem(IDC_CD); (8) strExtra= " -"; (9) if (bAire->GetCheck () && bCD->GetCheck ()) (10) strExtra = " con Aire y CD."; (11) else if (bAire->GetCheck() && !bCD->GetCheck()) (12) strExtra = " con Aire."; (13) else if (!bAire->GetCheck() && bCD->GetCheck()) (14) strExtra = " con CD."; (15) UpdateData(); (16) wndAuto=GetDlgItem(IDC_BMW + m_Marca); (17) wndTipo=GetDlgItem(IDC_SEDAN + m_Tipo); (18) wndAuto->GetWindowText (strAuto); (19) wndTipo->GetWindowText (strTipo); (20) strMensa = "Su auto es un/a: " + strTipo + " " + strAuto + strExtra; (21) MessageBox(strMensa); (22) } Parecer un poco extenso y complejo este cdigo, pero no desespere, veamos lnea por lnea.

En las lneas (1) y (2) se declaran dos punteros a CWnd para luego asociarlos a los primeros radio buttons de cada grupo y poder obtener el texto (ver lneas 17, 18 19 y 20). En las lneas (3) y (4) se declaran algunas variables de cadena donde se almacenarn, por un lado los textos de los radio buttons y check box. Como antes se coment, todos los objetos de Windows son ventanas, o sea de la clase CWnd. La clase CButton posee funciones que vamos a necesitar, por ejemplo GetCheck(), entonces en (5) y (6) se declaran dos punteros a CButton que luego en (7) y (8) se usan para relacionarlos con los checks box. De 10 a 15 se averigua, por medio de GetCheck(), que check box est marcado para as preparar el texto correspondiente. Con UpdateData(TRUE) en (16) se transfieren los datos de los controles a las variables. En (17) y (18) se relacionan los radio buttons con punteros CWnd para despus, por medio de GetWindowText() se obtiene el texto de los controles seleccionados (19 y 20). Nos detenemos un instante en (17) y (18) y vemos que es precisamente en esas lneas donde se determina que radio button est seleccionado, pues el parmetro de GetDlgItem es el ID del primer control del grupo ms el contenido de la variable miembro. En (21) se prepara una variable CString que luego en (22) se muestra por pantalla. Resumiendo: Para saber si un Check Box est marcado o no se debe usar la funcin GetCheck() de la clase Cbutton, por lo tanto de debe convertir el puntero CWnd que retorna GetDLgItem() a CButton. Es bueno tener en cuenta eso de poder convertir de una clase a otra en caso de necesitar alguna funcin en especial. Se conoce un Radio Button seleccionado perteneciente a un grupo a travs de pasar el ID del primer control del grupo ms la variable miembro a GetDlgItem(). CONTROL: LISTBOX. Las listas son otros de los objetos ms importantes en Windows. Existen: listas, listas desplegables, lista de archivos, listas de directorios, etc. Todas funciones y caractersticas propias pero funcionan bsicamente igual. Veamos un ejemplo. - Cree un nuevo proyecto basado en dilogos con el MFC AppWizard y llmelo listas1. Coloque 2 controles ListBox un al lado del otro y dos botones entre ellos.

Acceda a la ventana de propiedades de la lista de la izquierda, (la izquierda suya) (puede dejar visible la ventana de propiedades si presiona el icono "alfiler" del extremo superior izquierdo), y como ID ponga IDC_NUMEROS. En la solapa STYLES desmarque la opcin SORT, pues en este caso no pretendemos que la lista se ordene automticamente.

Para la otra lista escriba IDC_PARES como ID, tambin quite la marca en STYLES a SORT y en EXTENDED STYLES marque MODAL FRAME, (ver como cambian los borde de la lista). Seleccione el botn de arriba, acceda a sus propiedades, como ID escriba IDC_PASAPARES y CAPTIOS = >>. El botn de abajo tendr > como CAPTION e ID = IDC_PASA1. An falta agregar un par ms de objetos, pero antes agregaremos variables miembro a las listas. Seleccione nuevamente la lista de la izquierda, presione Ctrl + W, acceder a ClassWizard. Vaya a la solapa Member Variables, marque IDC_NUMEROS y presione Add Variable. Aparecer la ventana Add Member Variable; como NAME escriba m_lstNumeros, como CATEGORY seleccione Control y TIPO CListBox. Realice los mismo para la otra lista, (la de la derecha), pero como Member Variable Name escriba m_lstPares. Bien, ahora agregaremos unos controles ms para terminar el diseo de la aplicacin. Coloque un pequeo cuadro de edicin abajo a la derecha de IDC_NUMEROS y escriba en la propiedad ID, IDC_CANT. Coloque un botn debajo de IDC_NUMEROS con la propiedad ID = IDC_CARGAR y CAPTION = Cargar. Elimine el botn Cancelar y mueva el botn Aceptar para ubicarlo en el extremo inferior izquierdo del cuadro de dilogo. Coloque un Static Text a la derecha del cuadro de edicin, con el texto (propiedad CAPTION), "Ingrese cantidad de nmeros". Cree una variable miembro para el cuadro de edicin de TIPO int, CATEGORY values y de nombre m_Cant.

El objetivo del programa ser hacer uso de diferentes eventos y propiedades de las listas. Se ingresar un valor numrico mayor a 0 en el cuadro de edicin, luego se presionar el botn Cargar y se cargar la lista IDC_NUMEROS con los nmeros naturales menores al contenido del cuadro de edicin. Tambin ser posible seleccionar un elemento de la lista y mostrarlo en un mensaje. Al presionar el botn >> se pasarn todos los valores pares de IDC_NUMEROS a IDC_PARES y presionando > , se pasar el valor seleccionado siempre y cuando sea par. Finalmente presionando Aceptar se terminar la aplicacin, pero antes preguntando al usuario si es realmente lo que desea.

Primero codificaremos el botn Aceptar; haciendo doble click vamos directamente al mensaje por dedfecto OnOk(), ah escriba: void CListas1Dlg::OnOK() { // TODO: Add extra validation here if (MessageBox("Desea salir?","Salir",MB_ICONQUESTION+MB_YESNO)==IDYES) CDialog::OnOK(); } Lo nico que se agreg fue la lnea if, donde se averigua que botn del MessageBox se ha pulsado; si fue YES se termina el programa, caso contrario contina todo igual. En este caso se hace uso del valor que retorna MessageBox(), que pueden ser varios de acuerdo a los botones usados en el mensaje. Si probamos la aplicacin y pulsamos el botn Aceptar veremos que ahora aparece un mensaje preguntando si efectivamente queremos salir; mensaje tpico de todo programa Windows. Seleccione el botn Cargar y acceda a ClassWizard. All seleccione Message Maps, luego en Objects IDs marque IDC_CARGAR, en MESSAGE marque BN_CLICKED y entonces presione ADD FUNCTION e inmediatamente EDIT CODE. Aparecer la funcin OnCargar(), all escriba: void CListas1Dlg::OnCargar() { // TODO: Add your control notification handler code here CString c; (1) int i; (2) UpdateData(TRUE); (3) m_lstNumeros.ResetContent (); (4) for (i=1; i<=m_Cant; i++) (5) { c.Format ("%s%i",c, i); (6) //formateo i como String m_lstNumeros.AddString(c); (7) //lo agrego a la lista c=""; } } El contenido de una lista es de tipo Cstring, por eso se declara una variable de este tipo en (1), luego en (2) se declara un valor entero para recorrer todos los valores menores al ingresado en el cuadro de edicin. Con UpdateData(TRUE) en (3) se transfiere el contenido del cuadro de edicin a la variable miembro m_Cant. En (4) se limpia el contenido de la lista; y en (5) se realiza un bucle variar para i =1 hasta m_Cant, lo que da a lugar que por cada vuelta el elemento que debemos cargar en la lista es el ndice i pero convertido ha cadena, que es lo que hace (6) con la funcin Format de la clase CString dejando el valor formateado en la variable c que en (7) es agregada a la lista con la funcin AddString de la clase CListBox. Ya tenemos cargada la lista IDC_NUMEROS, ahora vamos a escribir el cdigo que al seleccionar un elemento lo muestre en un mensaje.

Para esto seleccionamos la lista y accedemos a ClassWizard, all en Message Maps con IDC_NUMEROS marcado en Object IDs seleccionamos LBN_SELCHANGE en Message, pulsamos Add Function y luego Edit Code. Escriba: void CListas1Dlg::OnSelchangeNumeros() { // TODO: Add your control notification handler code here CString strTexto; (1) m_lstNumeros.GetText (m_cboNumeros.GetCurSel (), strTexto); (2) MessageBox(strTexto); (3) } El elemento seleccionado si bien es un nmero porque precisamente cargamos nmeros son de tipo CString, o sea cadenas, por eso se declara en (1) la variable CString strTexto. Los elementos de una lista se encuentran dentro de ella en una posicin determinado empezando por el primero que se ubica en posicin 0 el segundo en 1 as sucesivamente. La funcin GetCurSel() retorna la ubicacin de el elemento seleccionado, o sea si est marcado el primero retorna 0; y GetText() que tiene 2 parmetros (ubicacin, contenido), deja en el segundo parmetro el valor ubicado en lo indicado en el primer parmetro; por eso en (2) se usa esa combinacin de funciones: obtener el texto del seleccionado y dejarlo en la variable strTexto. En la lnea (3) se muestra el elemento seleccionado. Escribiremos ahora el cdigo para el botn IDC_PASAPARES. Cree la funcin OnPasapares(), a travs de ClassWizard o simplemente haciendo doble click en el control. void CListas1Dlg::OnPasapares() { // TODO: Add your control notification handler code here int i, n, ce; (1) CString strC; (2) ce=m_lstNumeros.GetCount (); (3) if (ce > 0) (4) for (i=0; i< ce; i++) (5) { m_lstNumeros.GetText (i, strC); (6) //obtengo elemento n=atoi(strC); (7) //lo convierto a numrico if(n%2==0) (8) //averiguo si es par m_lstPares.AddString(strC); (9) //lo paso a la otra } } Primero (1) se declaran variables enteras; i ser el ndice del variar, ce la cantidad de elementos de la lista IDC_NUMEROS y n donde se almacenar cada elemento convertido a numrico para poder averiguar si es par o no. En strC se almacenar cada elemento. En (3) por medio de GetCount() se obtiene la cantidad de elementos en IDC_NUMEROS. Si la cantidad de elementos es mayor a 0 (4) comienzan a recorrerse todos los elementos de la lista, desde el primero, 0, hasta el ltimo, ce-1. Por cada

elemento obtengo el contenido a travs de la funcin GetText (5) a la cual se le pasa el ndice i y la variable donde dejar el valor. Como tengo que ver si ese valor es par o no, debo pasarlo a entero, pues es una cadena; esto hace la lnea (7) por medio de la funcin atoi (array to integer), dejando en la variable n el valor en formato integer. Si el resto de dividir n por 2 es 0, es porque es par, entonces, lo paso a IDC_PARES, as sucesivamente, elemento por elemento hasta recorrer toda la lista IDC_NUMEROS. El botn IDC_PASA1 es ms fcil, pues solamente hay que averiguar si es par el que est seleccionado. Cree una funcin de gestin para el mensaje OnPasa1, tal como lo hizo para el botn anterior, y escriba: void CListas1Dlg::OnPasa1() { // TODO: Add your control notification handler code here int n; CString strC; m_lstNumeros.GetText (m_lstNumeros.GetCurSel (), strC); n=atoi(strC); if (n%2==0) m_lstPares.AddString (strC); } Estas lneas de cdigo son exactamente las misma que estn en el botn IDC_PASAPARES, (detalladas anteriormente), slo que aqu no hace falta ningn bucle pues solamente interesa el elemento seleccionado. Se obtiene el valor del elemento seleccionado, se lo convierte a entero, se averigua si es par y en caso positivo se lo pasa a la otra lista. Resumiendo: Las listas, sean ListBox o ComboBox poseen la funcin miembro AddString() con la cual incorporan elementos. Los elementos de una lista tienen una posicin dentro de ella donde el primer elemento es 0. Este ndice se obtiene por medio de la funcin miembro GetCurSel(), la misma retorna la posicin del elemento seleccionado. La cantidad de elementos de una lista se obtiene con la funcin GetCount(). El elemento seleccionado se obtiene por medio de GetText() cuyos parmetros son: el ndice en la lista donde se encuentra el elemento y una variable CString donde se almacenar el valor. A una lista se le asocia una variable de Category Control y Type CListBox..

DIALOGOS MODALES Y NO MODALES: Hasta ahora hemos visto aplicaciones que slo usaban un solo cuadro de dilogo (una sola ventana); a pesar que AppWizard nos genera un AboutBox. Desde ya que una aplicacin no se basa siempre en un solo dilogo sino que a veces necesitaremos incorporar ms ventanas a nuestros programas. Crearemos entonces un proyecto al cual llamaremos Dialogos y como siempre ser Dialog Based. Una vez generada la aplicacin por AppWizard ver el dilogo cotidiano con los botones Aceptar y Cancelar. Antes de incorporar un nuevo dilogo modificaremos nuestra pantalla principal. Coloque el botn Cancelar en el extremo inferior izquierdo del cuadro de dilogo. Acceda a las propiedades de este botn, cambie su ID por IDC_LLAMA y CAPTION por "Llamar al otro dilogo". Ahora agregaremos un nuevo dilogo, para esto en la ventana WorkSpace debe estar activa la solapa ResourceView (seguramente est activa sta pues estuvo modificando el botn). Haga click con el botn derecho del mouse sobre la carpeta Dialog y seleccione Insert Dialog del men contextual. Aparecer un nuevo dilogo con los botones Aceptar, Cancelar y por defecto ID = IDD_DIALOG1. Si lo desea puede modificar las propiedades del dilogo, pero en este caso dejaremos todo como est. Cuando un agrega un dilogo slo se incorpora el recurso (archivo de recurso), que sera el aspecto visual, pero para nada est an vinculado con la aplicacin, pues necesita de una clase la cual traer consigo las funciones miembro y los datos miembro. Por eso cada vez que inserte un cuadro de dilogo cree la clase del mismo, cmo se hace esto?; simplemente presione Ctrl + W y aparecer automticamente antes de entrar a ClassWizard la siguiente ventana:

Se detect que IDD_DIALOG1 es un nuevo recurso y que probablemente necesitaremos una clase para el mismo, entonces no ofrece crear una nueva o seleccionar una existente; elegiremos Create a new class y pulsamos OK. Aparecer una ventana pidiendo que ingresemos un nombre para la clase, all ingresaremos CDialogo1, (por convencin todas las clases comienzan con la letra C).

Automticamente en el campo File Name aparecer Dialogo1.cpp, (aqu se encontrar la definicin de la

clase) y en la lista Base Class verifique que se encuentre seleccionada Cdialog. Presione OK, pasar a ClassWizard, presione nuevamente OK para cerrar el asistente. Ya tenemos entonces un nuevo cuadro de dilogo y una clase para el mismo, obviamente esto no hace que nuestra nueva ventana sea til en nuestro programa, ahora deberemos escribir el cdigo necesario para poder mostrarla. Regresemos a la ventana principal, pues el cdigo que llamar al nuevo dilogo estar al presionar el botn IDC_LLAMA. Pero el dilogo donde est este botn reconocer la clase del nuevo dilogo si agregamos en el archivo de implementacin del dilogo principal el archivo de cabecera del nuevo dilogo que fue generado por ClassWizard, el cual tiene el mismo nombre que el de implementacin pero con extensin .h (en nuestro ejemplo sera dialogo1.h ). No se olvide, entonces, de agregar la lnea #include "Dialogo1.h" en DialogosDlg.cpp. Cuando uno muestra un cuadro de dilogo de forma MODAL, hasta tanto no se cierre el dilogo llamado con su respectivo botn cerrar o desde el men de sistema NO SE PODR ACCEDER AL RESTO DE LA APLICACIN y si se lo muestra NO MODAL en cambio PODRA ACCEDER A OTROS PUNTOS DE LA APLICACIN. Por ejemplo: en VC++ la barra de herramientas, donde estn los controles, es un dilogo NO MODAL, pues uno puede acceder a diferentes secciones de VC sin necesidad de cerrar la barra. Por otro lado ClassWizard es un dilogo, ventana, MODAL porque mientras no pulsemos OK, CANCEL o el botn de cerrar de la ventana no podremos utilizar el resto de VC++. En principio veremos como llamar al cuadro de dilogo IDD_DIALOG1 como MODAL. DIALOGOS MODALES: Haga doble click en el botn IDC_LLAMA, (generar automticamente el evento OnLlama) y escriba: void CDialogosDlg::OnLlama() { // TODO: Add your control notification handler code here int r; (1) CDialogo1 dlgDialogo1(this); (2) r=dlgDialogo1.DoModal (); (3) } Este cdigo simplemente se ajusta a llamar al dilogo de forma modal. En (2) se crea un objeto derivado de la clase CDialogo1 especificando Clase variable(this), this es un puntero que hace referencia al objeto actual, o sea el dilogo principal. Y en (3) a travs de DoModal(), del objeto recin creado, se llama al dilogo, y es ms, se le pasa en este punto el control del programa, de manera tal que cuando se cierre el nuevo cuadro de dilogo el flujo del programa continuar despus de esta lnea. Por eso DoModal() retorna un valor entero que puede ser IDOK si el nuevo dilogo al momento de cerrarlo con EndDialog(), (ver ms adelante), se le pas el parmetro IDOK. Por esto extienda el cdigo de IDC_LLAMA como sigue: void CDialogosDlg::OnLlama() { // TODO: Add your control notification handler code here

int r; CDialogo1 dlgDialogo1(this); r=dlgDialogo1.DoModal (); //al volver verificar como se cerr if (r == IDOK) AfxMessageBox("Usted cerr con Aceptar o Si"); else AfxMessageBox("Usted cerr con Cancelar o NO"); } En el nuevo dilogo agregue un botn con las siguientes propiedades: ID = IDC_CERRAR CAPTION = CERRAR. Y escriba en OnCerrar: void CDialogo1::OnCerrar() { // TODO: Add your control notification handler code here CString Mensa; Mensa="Seleccione para cerrar SI o No"; if (MessageBox(Mensa,MB_YESNO+ MB_ICONINFORMATION)==IDYES) (1) EndDialog(IDOK); else EndDialog(IDCANCEL); } Cuando se presione el botn Cerrar, para cerrar el dilogo, se le informa al usuario que seleccione como quiere salir, presionando OK o CANCEL, de acuerdo a lo que se pulse.

Resumiendo: Para insertar un dilogo, hacer click con el botn derecho del mouse sobre la carpeta Dialog del ResourceView y seleccionar Insert Dialog. Luego de agregar un dilogo hay que crear la clase del mismo, esto se hace accediendo a ClassWizard inmediatamente despus de haber insertado el dilogo. No olvidar agregar la lnea de inclusin de archivo de cabecera para el nuevo dilogo en el archivo de implementacin del dilogo que lo llama.

Un dilogo Modal se llama por medio de la funcin DoModal() perteneciente al objeto derivado de la clase del nuevo dilogo, previamente definido en el mismo evento. DoModal() retorna un valor entero que sirve para verificar como fue cerrado el dilogo con la funcin EndDialog(). DIALOGOS NO MODALES: Ahora veamos como llamar a dilogos NO MODALES. Cree un nuevo proyecto basado en dilogos y llmelo Nomodal. En ResourceView haga click con el botn derecho del mouse sobre la carpeta Dialog y seleccione Inser Dialog. Llame a ClassWizard, (por ejemplo haciendo Ctrl + W), aparecer una ventana detectando que se agreg un nuevo dilogo, deje marcado Create a new class y pulse OK. En la ventana New Class en el cuadro de edicin Name escriba CDialogoNoModal y acepte. Escriba la lnea #include "DialogoNoModal.h" en el archivo de implementacin NoModalDlg.cpp. En el dilogo principal agregue 2 botones y establezca las siguientes propiedades: ID = IDC_LLAMAR y CAPTION = Llamar a dilogo no modal. ID = IDC_CERRAR y CAPTION = Cerrar dilogo no modal. En el evento OnLlamar escriba: CDialogoNoModal* dlgNoModal =NULL; (1) void CNomodalDlg::OnLlamar() { // TODO: Add your control notification handler code here if (dlgNoModal == NULL) (2) dlgNoModal = new CDialogoNoModal(this); (3) } En la lnea (1) antes de comenzar la funcin se declara un puntero a la clase del nuevo dilogo (global) y por defecto se le asigna el valor NULL. Este puntero servir para crear una instancia de la clase que ser la forma por la cual manipularemos luego el dilogo. Precisamente en (2) si el puntero es NULL se crea la instancia por medio del operador new (3) el cual reserva memoria para el objeto descendiente de this o sea el dilogo que lo llama. Con esto la aplicacin no va a funcionar, hace falta llamar a la funcin Create() y ShowWindow() desde el constructor del nuevo formulario. Lo escrito en el constructor ser los primero en ejecutarse al ocurrir la lnea (3). Al constructor del nuevo dilogo se accede por medio de la solapa ClassView de la ventana WorkSpace. Expanda la clase del nuevo dilogo y haga doble click donde dice: CDialogoNoModal(CWnd* pParent =NULL); y escriba: CDialogoNoModal::CDialogoNoModal(CWnd* pParent /*=NULL*/) : CDialog(CDialogoNoModal::IDD, pParent) { //{{AFX_DATA_INIT(CDialogoNoModal) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT

//Crear la ventana del cuadro de dilogo no modal if (Create(CDialogoNoModal::IDD, pParent)) (1) ShowWindow(SW_SHOW); (2) //Si se pudo crear, se muestra } Cuando, en el dilogo principal, se pulsa el botn que llama al nuevo dilogo (usando new ), se ejecuta el cdigo del constructor del objeto dlgNoModal, donde habitualmente se inicializan, por ejemplo variables miembros, pero en este caso se intenta crear el dilogo (1) y de ser posible lo muestra (2). Si ejecuta la aplicacin y pulsa el botn que dice: "Llamar a dilogo no modal" acceder al nuevo dilogo no modal que permite acceder indistintamente a sus controles como a los del dilogo principal.

Faltara escribir el cdigo en el botn que permite cerrar el dilogo no modal desde la ventana que lo llam. Escriba el siguiente cdigo en IDC_CERRAR del dilogo principal:

void CNomodalDlg::OnCerrar() { // TODO: Add your control notification handler code here if (dlgNoModal != NULL) { delete dlgNoModal; dlgNoModal = NULL; } } Ac se averigua si la variable objeto CnomodalDlg no contiene NULL, porque si no es NULL es porque ha sido referenciada ya una vez, lo que implica que se est mostrando an el diologo, entonces elimina la memoria reservada y le vuelve a asignar NULL INTERCAMBIO Y VALIDACIN DE DATOS EN UN CUADRO DE DILOGO: Siempre que se asocien variables miembro a controles mediante ClassWizard se genera automticamente un cdigo para efectuar el intercambio de valores de las variables desde y hacia los controles. Este cdigo se encuentra en la funcin DoDataExchange() del cuadro de dilogo. Esta funcin es responsable de transferir los datos en ambas direcciones, desde y hacia los controles del cuadro de dilogo.

El proceso de transferencia se denomina Data Exchange (Intercambio de datos) y se inicia mediante una llamada a la funcin UpdateData(). A UpdateData() se le puede pasar un parmetro Boolean, (TRUE o FALSE) denominado bSaveAndValidate. Se puede pasar o bien FALSE para dar valor a los controles a partir de las variables miembro, o bien TRUE para transferir a las variables miembro los valores actuales de los controles. UpdateData() es invocada automticamente por la funcin OnInitDialog() de la clase CDialog y la funcin OnOK() del botn Aceptar que AppWizard pone por defecto en nuestras aplicaciones tambin llama de forma automtica a UpdateData(). Cuando uno utiliza UpdateData() pasando TRUE en el parmetro bSaveAndValidate en cualquier funcin de cualquier control; se hace un llamado a DoDataExchange(). Si los datos eran vlidos, UpdateData() valdr TRUE caso contrario se emite el aviso oportuno para el usuario y UpdateData() valdr FALSE. Para ver esto cree un nuevo proyecto MFC basado en dilogos y llmelo ValidarDatos. Objetivo del proyecto: La aplicacin siguiente constar de 2 dilogos, uno es el que crea el AppWizard ms otro que agregaremos. Del principal se llamar al nuevo formulario, all habr dos cuadros de edicin donde se ingresar un Apellido y una edad. Al momento de presionar Aceptar se validar si la edad est entre 0 y 110 y si no es 30; de ser vlida se pasan los parmetros a unas etiquetas (cuadro de texto esttico) del dilogo principal. Ya que conoce el objetivo del programa continuaremos con el diseo de las pantallas: En el dilogo principal agregue un control de texto esttico y uno de edicin con las siguientes propiedade ID= IDC_NOMBRE, CAPTION= en blanco y en STYLE tilde SUNKEN. ID=IDC_ANIOS y en STYLE tilde READ ONLY. Ahora le asociaremos variables a estos textos estticos: Pulse con el derecho sobre IDC_NOMBRE y seleccione la opcin ClassWizard del men contextual. Luego elija la solapa Member Variables, marque IDC_NOMBRE y pulse el botn ADD VARIABLE. Especifique en el campo nombre m_strNombre, CATEGORY value y TYPE CString. Acepte todas al ventanas. Realice la misma operacin para IDC_ANIOS pero como nombre de variable escriba m_nAnios, CATEGORY value y TYPE int. Elimine el botn Cancelar y coloque otro con ID = IDLLAMA y CAPTION = Llama a otra ventana. El dilogo principal debera quedar ms o menos as:

Antes de escribir el cdigo del botn "Llamar a otra ventana" agregaremos el nuevo dilogo. En ResourceView haga click con el derecho sobre la carpeta Dialog y selecciones

Insert Dialog. Esto dar a lugar a un nuevo recurso de dilogo. Pngale como ID, IDD_DATOS. Pulse Ctrl + W para acceder a ClassWizard y crear la clase para este dilogo. Como nombre de clase escriba CdatosDlg y acepte todas las ventanas. Agregue la lnea #include "DatosDlg.h" en el archivo de implementacin ValidarDatosDlg.cpp. Diseemos ahora el dilogo DatosDlg. Abra haciendo doble click en la carpeta Dialog de ResourceView, el dilogo IDD_DATOS. Coloque dos cuadros de edicin con los siguientes ID: IDC_APELLIDO , IDC_EDAD. A un lado de los cuadro de edicin coloque controles de texto esttico que sirvan para identificar lo que hay que ingresar en cada campo: CAPTION: Apellido y Edad. El dilogo IDD_DATOS quedara:

Asciele a los cuadros de edicin variables: para IDC_APELLIDO: m_strApellido, CATEGORY value y TYPE CString. Y especifique en la ventana principal de ClassWizard, Maximum Characters = 20. Para IDC_EDAD: m_Edad, CATEGORY value y TYPE int. Minimum Value = 0 y Maximum Value = 110. Bien, ahora regresemos al dilogo principal para codificar el botn que llama a IDD_DATOS. Vamos a llamar a nuestro nuevo dilogo de manera tal que por defecto los cuadros de edicin tengan valores. Escriba en OnLlama: void CValidarDatosDlg::OnLlama() { // TODO: Add your control notification handler code here CDatosDlg dlgDatos(this); (1) //antes de mostrar el formulario le asigno a las variables //de los cuadros de texto valores iniciales dlgDatos.m_strApellido = "Francescoli"; (2) dlgDatos.m_Edad = 34; (3) dlgDatos.DoModal (); (4) } Llamaremos al dilogo de forma Modal, por lo tanto creamos el objeto dlgDatos derivado de CDatosDlg (1), entonces antes de usar DoModal(), le asignamos a las variables miembro de los cuadros de edicin valores por defecto (2) y (3). La transferencia de los valores desde la variables a los controles se realizar, en este caso, de forma automtica cuando el flujo del programa pase por OnInitDialog() del dilogo que acaba de ser invocado.

Si prueba el programa, ver cuando llama al nuevo dilogo que los controles de edicin muestran los valores por defecto. Nos queda escribir el cdigo que valide la edad y que pase los datos de los cuadros de edicin del dilogo IDD_DATOS a los controles del principal, al presionar Aceptar. Cuando se aaden cierto tipo de variables miembro a un cuadro de dilogo mediante ClassWizard, se especifica adems del nombre, una categora, un tipo y algunas validaciones, como por ejemplo para m_Edad se especific que el valor mnimo sera 0 y el mximo 110. Estas validaciones generan cdigo en la funcin DoDataExchange(), (del dilogo correspondiente, en este caso IDD_DATOS), con lneas como esta: DDV_MinMaxInt(pDX, m_Edad, 0, 110); Ver que esa lnea est escrita en un color gris, pues nos permite saber que fueron generadas por ClassWizard. Esta es una funcin DDV (funcin de validacin de datos) propia de VC++ que en este caso en particular verifica que el contenido de la variable m_Edad se encuentre entre 0 y 110. CMO CREAR FUNCIONES DE VALIDACIN PERSONALIZADAS: Es posible crear nuestras propias funciones de validacin personalizadas para comprobar casos especiales escribiendo una funcin del estilo DDV. Nuestra funcin debera admitir el puntero del objeto CDataExchange (pDX), la variable miembro que se desea validar y otros parmetros adicionales que consideremos necesarios. La primera condicin que tiene que verificar nuestra funcin es que el objeto CDataExchange sea guardar y validar (m_bSaveAndValidate); esto se hace comprobando que pDX->m_bSaveAndValidate sea TRUE, pues esto implica que ocurri una llamada UpdateData(TRUE) que transfiere los datos de los controles a las variables. Luego deberamos validar nuestra variable en s, de acuerdo al criterio elegido. Si es vlida la variable, entonces la funcin puede retornar normalmente; en caso contrario ser preciso visualizar un mensaje aclaratorio para el usuario y llamar a la funcin Fail() de DoDataExchange() para indicarle a la funcin UpdateData() que haya llamado que ha fracasado. Bueno, en nuestro ejemplo queramos comprobar que la edad est entre 0 y 110 pero que no sea 30, entonces haga click con el botn derecho del mouse sobre la clase CdatosDlg y seleccione Add Member Function y en la ventana que aparece escriba en Function Type: void y en Function Declaration: ValidarEdad(CDataExchange *pDX, int& Edad), acepte y se presentar en pantalla la funcin. All escriba: void CDatosDlg::ValidarEdad(CDataExchange *pDX, int& Edad) { //Esta funcin me valida la edad //Si va a guardar los datos validando y Edad == 30...

if (pDX->m_bSaveAndValidate && Edad ==30) { AfxMessageBox("La edad debe estar entre 0 y 110 pero no igual a 30!"); pDX->Fail(); //Reseteo el UpdateData() } } Falta hacer la llamada a esta funcin; que la ubicaremos en DoDataExchange() del dilogo IDD_DATOS. All escriba: void CDatosDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CDatosDlg) DDX_Text(pDX, IDC_APELLIDO, m_strApellido); DDV_MaxChars(pDX, m_strApellido, 20); DDX_Text(pDX, IDC_EDAD, m_Edad); DDV_MinMaxInt(pDX, m_Edad, 0, 110); //}}AFX_DATA_MAP ValidarEdad(pDX, m_Edad); //Llamo a la funcin que valida la edad } Puede probar el programa y comprobar que cuando llama al dilogo IDD_DATOS por defecto trae en los cuadros de dilogos los valores iniciales que habamos especificado Apellido=Francescoli y edad = 34. Adems podr verificar que si ingresa una edad fuera del rango 0 a 110 o igual a 30 mostrar el mensaje: "La edad debe estar entre 0 y 110 pero no igual a 30!". Para terminar la aplicacin debemos escribir el cdigo que permita pasar los datos a los controles del formulario principal. Tericamente sera asignarle a las variables de los controles del dilogo principal, el contenido de las variables de los controles del dilogo IDD_DATOS, luego que hayan sido validados. Bastara agregar un else en el if de la funcin ValidarDatos(), pero el problema consiste en cmo hacer referencia a variables de otro dilogo. La solucin es pasarle un puntero de la clase del dilogo principal al constructor del dilogo IDD_DATOS, para que luego desde cualquier gestor de mensajes del dilogo llamado se pueda acceder a travs del puntero a cualquier funcin o dato miembro del dilogo "llamador". Parece un poco complejo, pero siga estos pasos: En primer lugar declararemos una variable miembro de la clase CDatosDlg de tipo CValidarDatosDlg, pues es la clase del dilogo "llamador". Seleccione entonces en ClassView la clase CDatosDlg y pulse el botn derecho del mouse. Del men contextual elija Add Member Variable; aparecer una ventana pidiendo el tipo de variable (Variable Type) all escriba CValidarDatosDlg*, pues ser un puntero, como nombre escriba m_pPrincipal y marque la opcin Protected. Esto dar a lugar a un icono con una llave en el ClassView, lo que indica que se trata de una variable protegida.

El siguiente paso es cambiar los parmetros del constructor que originalmente es: CDatosDlg::CDatosDlg(CWnd* pParent /*=NULL*/) : CDialog(CDatosDlg::IDD, pParent) El parmetro pParent es un puntero a la clase CWnd (ventana) y efectivamente es una referencia a la ventana predecesora, pero lo que nos interesa a nosotros es pasar un puntero a CValidarDatosDlg entonces donde CWnd escribimos CValidarDatosDlg. Y adems agregamos m_pPrincipal(pParent). El constructor quedara: CDatosDlg::CDatosDlg(CValidarDatosDlg* pParent /*=NULL*/) : CDialog(CDatosDlg::IDD, pParent), m_pPrincipal(pParent) (en azul lo modificado y aadido). Ahora hay que hacer la misma modificacin en la declaracin, el paso anterior era la definicin. Para acceder a la declaracin haga click con el botn derecho del mouse en ClassView sobre el constructor y seleccione Go to Declaration all encontrar lo siguiente: // CDatosDlg dialog class CDatosDlg : public CDialog { // Construction public: void ValidarEdad(CDataExchange* pDX, int &Edad); CdatosDlg(CWnd* pParent = NULL); // standard constructor .... por razones de espacio se omite el resto del cdigo. Aqu hay que hacer la misma modificacin que en la definicin; donde dice CWnd* pParent escriba CValidarDatosDlg* y para que la clase datos reconozca la clase predecesora se agrega antes del comentario //CDatosDlg dialog la lnea #include "ValidarDatosDlg.h" que permite incluir el archivo de cabecera que contiene la definicin de la clase CValidarDatosDlg. Resumiendo: Cada vez que se hace un llamado a la funcin UpdateData() sta llama a la funcin DoDataExchange() que es la encargada de validar los datos de acuerdo a los criterior especificados al crear las variables miembros. Cuando se llama a UpdateData() la variable miembro m_bSaveAndValidate de la clase CDataExhange que se accede a travs del puntero pDX. Para poder acceder a las funciones y variables de un dilogo predecesor se debe agregar una variable miembro puntero a la clase del dilogo, modificar adems la definicin y la declaracin del constructor del dilogo desde el cual se quiere usar el predecesor, cambiando el puntero CWnd por el de la clase dilogo requerida incluyendo adems el archivo de cabecera pertinente. EVENTOS DEL MOUSE: Seguramente necesitar en alguna ocasin saber cundo se ha pulsado o soltado un botn del mouse y dnde se ha pulsado.

Windows maneja tres botones del mouse: los botones izquierdo, central y derecho. Para cada botn el programa recibir dos sucesos: uno para el momento en que el usuario pulsa el botn y otro para el momento en que suelta el botn. Cree un proyecto MCF basado en dilogos con el nombre EventosMouse . Pulse el botn derecho del mouse sobre el texto esttico: "A HACER.." y seleccione del men contextual la opcin Properties. Modifique la propiedad Caption por: "Objetivo: Hacer click donde x e y son iguales". Mueva el texto esttico y reubquelo bien arriba al centro del dilogo:

El objetivo del programa ser ir mostrando en la barra de ttulo del dilogo las coordenadas x e y relativas el puntero del mouse, a medida que uno lo mueve. Adems si justamente se pulsa el botn izquierdo del mouse cuando las coordenadas x e y son iguales, se mostrar un mensaje y se realizar un pequeo circulo con centro en esas cooredenadas. Son dos los mensajes del mouse que deberemos codificar. Uno es el que ocurre con el solo hecho de moverlo y el otro cuando pulsamos el botn izquierdo. Presione Ctrl + W para llamar a ClassWizard. Verifique que en la lista Objects IDs est CEventosMouseDlg marcado y seleccione de la lista Messages el suceso WM_MOUSEMOVE. Presione Add Function y luego Edit Code. Aparecer la definicin del mensaje OnMouseMove(), all escriba: void CEventosMouseDlg::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CString strPos; (1) strPos.Format ("Puntero en: x=%d, y=%d", point.x , point.y); (2) SetWindowText(strPos); (3) CDialog::OnMouseMove(nFlags, point); } En principio ver que la funcin recibe dos parmetros el primero un entero sin signo nFlags indica que otros botones del teclado se han mantenido pulsados, (este parmetro por ahora no nos interesa), y el otro de tipo CPoint son las coordenadas donde anda el puntero, es la estructura point y da a lugar a point.x , point.y.

En (1) se declara un string donde se almacenar la cadena a mostrar en la barra de ttulo, la misma que en (2) formateo a travs de su funcin Format() y como parmetros los valores x e y. Con SetWindowText() en (3) se establece como ttulo del dilogo el texto recin formateado. Con esto ya logramos que a medida que movemos el mouse se van mostrando las coordenadas de la posicin del puntero en la barra de ttulo. Ahora presione Ctrl + W para acceder a ClassWizard; marque en Object IDs CeventosMouseDlg y en Messages selecciones WM_LBUTTONDOWN (este es el suceso para capturar cuando se presion el botn izquierdo del mouse), pulse Add Function y luego Edit Code. Escriba: void CEventosMouseDlg::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC* pDC; (1) pDC = GetDC(); (2) if (point.x == point.y ) (3) { MessageBox("Perfecto!", "Acert",MB_ICONEXCLAMATION); pDC->Ellipse (point.x -5 , point.y -5 , point.x + 5, point.y +5); (4) } else MessageBox("Intentelo nuevamente", "Fall", MB_ICONERROR); CDialog::OnLButtonDown(nFlags, point); } Lo que se quiere lograr es que si al presionar el botn izquierdo del mouse sobre el dilogo coinciden el valor de la coordenada x con el de y, se muestre un mensaje y se dibuje un circulo pequeo con centro en x, y. Como hay que dibujar sobre el dilogo habr que hacer uso de un Contexto de dipositios, por eso en (1) se declara un puntero a la clase CDC (ms adelante se detallar el tema de Contextos de Dispositivos). En (2) se obtiene el dispositivo por defecto (el dilogo) y en (3) me fijo si los parmetros x e y son iguales. En caso de ser iguales se muestra el mensaje "Perfecto!" y en (4) se dibuja el crculo por medio de la funcin Ellipse() de la clase CDC, cuyos parmetros son los puntos del rectngulo en el cual est inscripta la elipse. En este caso para que point.x, point.y sea el centro, se rest 5 para obtener el puntos superior izquierdo del rectngulo y se sum 5 para el punto inferior izquierdo. Resumiendo: Los sucesos del mouse que se pueden capturar reciben como parmetros las coordenadas x e y del puntero (el puntero del mouse), en la estructura point de la clase Cpoint y el botn o la combinacin de botones pulsados como una entero sin signo UINT. Lo referente a el Contexto de Dispositivo (clase CDC) se ver ms adelante. TEMPORIZADOR: Quienes programan en Visual Basic habrn usado alguna vez el control Timer. Este control permite estipular un intervalo de tiempo para el cual ejecutar una determinada porcin de cdigo. Digamos por ejemplo que quiero mostrar el

mensaje "Hola" cada 5 segundos; bueno, en VB bastaba con agregar un control Timer en el formulario, especificar la propiedad Interval en 5000 (milsimas de segundos) y escribir el cdigo del mensaje en el evento Timer del Timer. Bien, nada de esto hay en VC++. Mirando la barra de herramientas no encontramos ningn control Timer o Temporizador, pero esto no implica que no exista el concepto de temporizador en VC++. Hay un mensaje para la clase CDialog llamado WM_TIMER, este mensaje es el encargado de ejecutar peridicamente el cdigo que queramos. No funciona solo, sino que primero hay que hacer una llamada a la funcin SetTimer() pasndole como parmetro el intervalo en milsimas de segundos, (esta funcin, se podra decir, activa el Timer), despus hay que usar la funcin KillTimer() para desactivarlo, (hay que recordar que un Timer activo ocupa recursos del sistema). Bien, vamos a realizar una aplicacin que incremente en 1 una variable pblica y en 2 otra, cada un segundo. El programa utilizar el mensaje WM_TIMER y se activar cuando se pulse un botn y desactivar pulsando otro. Cree un nuevo proyecto con el nombre "tempo" basado en dilogos. Coloque dos cuadros de edicin en el centro del dilogo los dos con las propiedades Read Only y Number tildadas y Border sin tildar. Con los ID: IDC_VECES e IDC_VECES2. Coloque dos botones con los siguientes ID: IDC_PONER_TIMER e IDC_TERMINAR_TIMER y como Caption escriba: "Poner timer" y "Terminar timer". Elimine el botn Cancelar. La pantalla debera quedar as:

Ahora vamos a asociarles variables a los cuadro de edicin: Presione Ctrl + W para acceder a ClassWizard. Seleccione la solapa Member Variables. En Control IDs seleccione IDC_VECES y presione el botn Add Variable. Como nombre escriba m_eVeces, Category = Value y Type = UINT. Acepte la ventana. Realice lo mismo para IDC_VECES2 pero la variable con el nombre m_eVeces2. Creamos ahora la variable pblica contador. En ClassView, con el botn derecho del mouse pulse sobre la clase CTempoDlg; del men contextual elija Add Member Variable.

Aparece la ventana Add Member Variable, all, en Variable Type escriba UINT y en nombre m_nVeces y acepte la ventana. Esto declar la variable m_nVeces de tipo entero largo, debemos darle el valor inicial 0; y el lugar correcto para hacer esto es el constructor del dilogo. Haga doble click en CTempoDlg::CTempoDlg(CWnd* pParent = NULL), el constructor. Se editar el cdigo: CTempoDlg::CTempoDlg(CWnd* pParent /*=NULL*/) : CDialog(CTempoDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTempoDlg) m_eVeces = 0; m_eVeces2 = 0; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_nVeces=0; //Esta es la inicializacin de la variable pblica } Ya que tenemos todo preparado, es hora de agregar el mensaje WM_TIMER al dilogo: Presione Ctrl + W para abrir ClassWizard. Seleccione la solapa Message Maps y CTempoDlg en la lista Object IDs. En Messages seleccione WM_TIMER y pulse Add Function. Esto dar lugar al evento OnTimer(). Pulse Edit Function y podr aadir cdigo en el evento. Escriba: void CTempoDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default m_eVeces= m_nVeces; (1) m_eVeces2=m_nVeces*2; (2) UpdateData(FALSE); (3) m_nVeces++; (4) CDialog::OnTimer(nIDEvent); } Simplemente aqu se igualan las variables asociadas a los cuadros de edicin con la variable pblica contador, en (1) para que se vaya mostrando de saltos de a 1 y (2) se lo multiplica por 2 al contenido, entonces va pasando de a 2. En (3) transfiero los datos desde las variables a los contadores por medio de la funcin UpdateData(FALSE), para que se muestren por pantalla; y (4) incremento la variable pblica. Ahora hay que escribir el cdigo, en el botn "Poner Timer", que llame a funcin SetTimer(), que es precisamente la que activa el Temporizador. Entonces haga doble click sobre el botn, se crear automticamente la funcin OnPonerTimer(), all escriba: void CTempoDlg::OnPonerTimer()

{ // TODO: Add your control notification handler code here SetTimer(1,1000, NULL); (1) } A la funcin SetTimer() se le pasa como parmetros, primero un valor distinto de 0 que identificar a ese Timer en particular, en segundo lugar el intervalo de tiempo en milsimas de segundos, (en este caso 1000 equivale a 1 segundo, o sea que cada un segundo se ejecutar el cdigo del evento OnTimer()) y por ltimo se pasa un valor que significa a que objeto pertenece el temporizador, NULL porque se trata del dilogo actual;(por lo general va siempre NULL aqu). Falta el cdigo para terminar el Timer, escriba para el botn "Teminar Timer" lo siguiente: void CTempoDlg::OnTerminarTimer() { CRect recDlg; CString strH, strW; // TODO: Add your control notification handler code here KillTimer(1); (1) } Para terminar el temporizador simplemente hay que llamar a la funcin KillTimer() pasndole como parmetro el mismo valor distinto de 0 que representa el Timer. Resumiendo: Un temporizador, Timer, es un mensaje de Windows que se ejecuta cada vez que se cumple el lapso de tiempo especificado en la llamada SetTimer(). Para utilizar el Timer, hay que agregar la funcin miembro del dilogo, WM_TIMER y luego activarlo, (slo una vez), usando la funcin SetTimer(). CMO TRABAJAR CON MENS: Los mens, (o menes), en las aplicaciones Windows son objetos, prcticamente omnipresentes. El Application Wizard que uno usa para generar la base de la aplicacin permite agregarles a los dilogos mens, siempre y cuando la eleccin nuestra haya sido desarrollar un proyecto SDI o MDI, AppWizard no genera mens para los proyectos basados en dilogos. Pero aqu haremos las cosas ms rebuscadas, crearemos un proyecto basado en dilogo, con el nombre de menu y a "manopla" crearemos el men. El programa constar de un men "Principal" con los submens "Uno", "Dos", "Tres", una separacin y "Salir". Adems estar la opcin "Ayuda" con el submen "Acerca de..." que nos permitir acceder al dilogo Acerca de que AppWizard nos gener. Cuando se seleccione el tem "Uno" se incrementar en 1 un campo de una estructura pblica, si se selecciona "Dos" se incrementar en 2 otro campo de la misma estructura, si se selecciona "Tres" se incrementar en 3 y cuando se pulse el submen "Sumar" se mostrar un mensaje con el resultado de la suma de los tres campos de la estructura. Primero crearemos el men, que se trata de un recurso. Una vez generado el proyecto, seleccione la solapa Resource View de la ventana WorkSpace, all haga click con el botn derecho del mouse sobre la carpeta menu resources, del men contextual seleccione Insert...

De la lista de recursos elija Menu y presione New. En la ventana de la derecha aparecer una barra horizontal donde uno ir diseando el men. Presione el botn derecho del mouse sobre el rea punteada y seleccione properties. Aparecer la ventana de propiedades de ese tem en particular, que en este caso se trata del nivel ms superior del men por lo que no lleva ID, pero como Caption escriba "Principal", ver como automticamente se extiende el men para poder agregar otros tems. Agregue los siguientes elementos al men "Principal": ID= IDC_MUNO, CAPTION=Uno. ID= IDC_MDOS, CAPTION=Dos. ID=IDC_MTRES, CAPTION=Tres. ID= IDC_MSUMAR, CAPTION= Sumar. Marque la opcin SEPARATOR para crear un elemento que divida el men. ID=IDC_MSALIR, CAPTION=Salir. Del mismo nivel que Principal cree "Ayuda" y como submen de ste: IDC_MACERCA, CAPTION=Acerca de...

Solamente esto hace falta para crear el aspecto visual del men; ahora hay que asociarlo al dilogo que lo presentar...algo sumamente complejo!. Seleccione con dos click el dilogo principal y luego dirjase a sus propiedades. Como propiedades del dilogo, adems de su ID y CAPTION, ver una lista desplegable que dice MENU, pues all seleccione IDR_MENU1 que es el men que acabamos de crear. Listo el pollo cocinada la gallina, tenemos nuestra aplicacin con men y todo!. Al fin de cuentas no es para tanto VC++, no?. Puede probar el programa presionando Ctrl + F5, ver que el dilogo tiene ahora el men que creamos, pero hay un problema, todava no hace nada. Habamos quedado que de acuerdo al tem seleccionado del men Principal se iran incrementando campos de una estructura pblica del dilogo. Bueno, lo primero que vamos a hacer es crear la estructura: Seleccione la solapa ClassView y luego presione el botn derecho del mouse sobre la clase CMenuDlg. Elija el tem Go to definition. Se editar en la ventana de la derecha el archivo de cabecera menuDlg.H que contiene la definicin de la clase CMenuDlg, deberemos modificar esta definicin, ya que vamos a agregar un dato miembro a la clase, entonces en la seccin public escriba: // CMenuDlg dialog

class CMenuDlg : public CDialog { // Construction public: struct numeros (1) //Se define la estructura numeros { int m_uno, m_dos, m_tres; (2) }n; (3) //variable n de tipo numeros CMenuDlg(CWnd* pParent = NULL); // standard constructor ......... Se ha abreviado el cdigo especificando slo las lneas agregadas. En (1) se declara una estructura con el nombre de numeros. Que contiene tres campos enteros, muno, m_dos y m_tres (2). Y en tres de declara la variable n de tipo numeros. Ahora le daremos valores iniciales a los campos y el lugar indicado para esto es el constructor de la clase. Pulse dos veces sobre el elemento CMenuDlg::CMenuDlg(CWnd* pParent =NULL) de la clase CMenuDlg. Agregue las ltimas tres lneas: // CMenuDlg dialog CMenuDlg::CMenuDlg(CWnd* pParent /*=NULL*/) : CDialog(CMenuDlg::IDD, pParent) { //{{AFX_DATA_INIT(CMenuDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); n.m_uno =0; (1) n.m_dos =0; (2) n.m_tres =0; (3) } (1), (2) y (3) son las lneas agregadas, las que inicializan en 0 los campos de la estructura. Finalmente hay que escribir el cdigo por cada evento del men, o sea por pulsar cada elemento: Presione Ctrl +W para acceder a ClassWizard. Pulse la solapa Message Maps. Seleccione IDC_MUNO en la lista Object Ids. Seleccione COMMAND en la lista Messages. Pulse Add Function y luego Edit Code. Se editar el mensaje OnMuno() y escriba: void CMenuDlg::OnMuno() { // TODO: Add your command handler code here n.m_uno = n.m_uno +1; (1) }

Simplemente se incrementa en 1 el campo m_uno de la estructura. Realice los mismos pasos anteriores para crear las funciones OnMdos(), OnMtres(), OnMsumar(), OnMsalir y OnMacerca(). Cuyos cdigos son: void CMenuDlg::OnMdos() { // TODO: Add your command handler code here n.m_dos = n.m_dos +2; } void CMenuDlg::OnMtres() { // TODO: Add your command handler code here n.m_tres = n.m_tres +3; } void CMenuDlg::OnMsumar() { // TODO: Add your command handler code here CString strSuma; strSuma.Format ("%s%i", strSuma, n.m_uno + n.m_dos +n.m_tres ); MessageBox(strSuma); n.m_uno =0; n.m_dos =0; n.m_tres =0; } void CMenuDlg::OnMsalir() { // TODO: Add your command handler code here EndDialog(IDOK); } void CMenuDlg::OnMacerca() { // TODO: Add your command handler code here CAboutDlg dlgAcerca; dlgAcerca.DoModal (); } Resumiendo: Para crear un dilogo con un men, primero hay que disear el men insertando un nuevo recurso (men en este caso) en la solapa Resource View de la ventana WorkSpace. Luego en las propiedades del dilogo se especifica que men utilizar. Se deben crear todos los mensajes de los tems del men. Una variable miembro pblica de una clase se puede crear de dos formas: haciendo click con el derecho del mouse sobre la clase y selecconando Add Member Variable o editando la definicin de la clase y escribiendo directamente en ella.

ICONOS Y MAPAS DE BITS: En Visual C++, adems disear y escribir el cdigo de una aplicacin, puede crear los icono y archivos BMP (mapas de bits), que la aplicacin utilice. Tanto los icono como los mapas de bits son recursos. Crearemos una aplicacin que manipular dos controles Picture, que permitirn visualizar los iconos y archivos BMP que crearemos. Cree una nueva aplicacin con el nombre "imgenes" Agregue dos controles Picture con las siguientes propiedades: ID = IDC_ICONOS, TYPE = ICON, deje vaca la lista IMAGE y tilde en la solapa Style la propiedad NOTIFY, (esta propiedad permite que el control acepte la accin de pulsar el botn del mouse). ID= IDC_BMP, TYPE = Bitmap, IMAGE = [vaco], NOTIFY=tildado. Agregue dos cuadros de texto esttico cerca de los controles Picture, para identificarlos: CAPTION= Icono y CAPTION = Imagen BMP. El dilogo debera quedar ms o menos as:

Bien, ahora hay que disear los iconos e imgenes. Vaya a la solapa ResourceView de la ventana WorkSpace y sobre la carpeta ICON pulse con el botn derecho del mouse. Seleccione el tem Insert Icon. Esto da a lugar a un icono en blanco. Dibuje, usando la barra de herramientas de dibujos, un rectngulo rojo. Deje el ID por defecto. Cree otro icono y dibuje un circulo rojo: Deje el ID por defecto. Pulse con el botn derecho del mouse sobre la carpeta "imgenes resource" y seleccione Insert. De la ventana de opciones de recursos seleccione Bitmap. Esto da a lugar a un bitmap en blanco. Dibuje el siguiente bitmap: Deje el ID por defecto. Cree otro bitmap, como el siguiente: Deje el ID por defecto. Una vez creados los iconos y los bitmaps, regresamos al dalogo para asociarle variables a los controles picture: Presione Ctrl+W para acceder a ClassWizard. Pulse la solapa Member Variables. Seleccione en Objects ID , IDC_ICONOS y pulse Add Variable.

En Member Variable Name escriba: m_imgIcono y en Value seleccione Control. Acepte la ventana. Seleccione en Objects ID, IDC_BMP y pulse Add Member Variable. En Member Variable Name escriba: m_imgBmp y en Value seleccione Control. Acepte todas las ventanas. De esta forma se crearon dos variables de tipo CStatic relacionadas a los controles de imagen. El programa consistir en pulsar el icono o imagen que se est visualizando y cambiar al otro icono o imagen. Tendremos que crear dos variables globales para poder saber que icono y/o que imagen se est visualizando al momento de hacer click sobre el control. Pulse la solapa ClassView de la ventana WorkSpace. Haga click con el botn derecho del mouse sobre la clase del dilogo y seleccione Add Member Variable. Escriba en Variable Type: int y en Variable Name: MostrarIcono. Cree otra variable de la misma forma de tipo int y con nombre MostrarBmp. Pulse dos veces sobre el constructor de la clase para escribir en l los valores iniciales de la variables recin creadas. Escriba al final: MostrarIcono = 1; MostrarBmp = 1; Una tercer variable global necesitaremos cuando carguemos los BMP. Entonces, aprovechamos y la creamos ahora. Igual que en los pasos anteriores agregue una nueva variable esta vez de tipo CBitmap y nombre m_bmp. (esta variable no hay que inicializar). Bueno, ahora hay que conectar por cdigo los iconos e imgenes con sus respectivos controles. Al iniciar la aplicacin se cargar por defecto en uno de los controles Picture el icono IDI_ICON1 (el rectngulo) y en el otro la imagen IDB_BITMAP1 (el icono de RIVER). Escriba entonces en el mensaje OnInitDialog(), las siguientes lneas numeradas: BOOL CImagenesDlg::OnInitDialog() { CDialog::OnInitDialog(); HICON hIcono; (1) //Tomo un puntero a la clase CWinApp CWinApp* pApp = AfxGetApp(); (2) // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here //Cargo la imagen por defecto del control imagen VERIFY(m_bmp.LoadBitmap (IDB_BITMAP1)); (3)

m_imgBmp.SetBitmap (m_bmp); (4) //Cargo el icono por defecto del otro control imagen hIcono= pApp->LoadIcon (IDI_ICON1); (5) m_imgIcono.SetIcon (hIcono); (6) return TRUE; // return TRUE unless you set the focus to a control } En (1) se declara una variable de tipo HICON, esto es un manejador para el objeto icono, que ser necesario luego. En (2) se obtiene un puntero a la aplicacin, portadora de la funcin para cargar un icono. Primero cargamos el BitMap de River (IDB_BITMAP1) por medio de la funcin LoadBitmap() (3) del objeto CBitmap, se lo asignamos al manejador. Luego en (4) con la funcin SetBitmap() del control Picture le asignamos la imagen recin cargada. En (5) y en (6) se cargar el icono por defecto y se lo asigna al control. Listo, si ejecuta la aplicacin ver que ahora los controles Picture vienen con una imagen por defecto. Falta escribir el cdigo que permita al pulsar el botn izquierdo del mouse sobre las imgenes cambiar los iconos y BitMaps. Entonces pulse dos veces sobre la primer imagen, (IDC_ICONOS), se crear el mensaje OnIconos(), all escriba: void CImagenesDlg::OnIconos() { // TODO: Add your control notification handler code here HICON hIcono; CWinApp* pApp = AfxGetApp(); if (MostrarIcono==1) { hIcono= pApp->LoadIcon (IDI_ICON2); m_imgIcono.SetIcon (hIcono); MostrarIcono=2; } else { hIcono= pApp->LoadIcon (IDI_ICON1); m_imgIcono.SetIcon (hIcono); MostrarIcono=1; } } Bsicamente se realiza el mismo procedimiento que en InitDialog() con la salvedad que aqu se verifica si la variable global MostrarIcono tiene un 1, porque de ser as se cargar el otro icono y se establece esa variable a 2. En caso de ser 2 se carga la primera y se establece MostrarIcono a 1. Ahora pulse dos veces sobre el otro control Picture para generar el mensaje OnBmp(), all escriba: void CImagenesDlg::OnBmp()

{ // TODO: Add your control notification handler code here m_bmp.DeleteObject (); //Elimino una referencia anterior if (MostrarBmp==1) { VERIFY(m_bmp.LoadBitmap (IDB_BITMAP2)); m_imgBmp.SetBitmap (m_bmp); MostrarBmp=2; } else { VERIFY(m_bmp.LoadBitmap (IDB_BITMAP1)); m_imgBmp.SetBitmap (m_bmp); MostrarBmp=1; } } Tambin aqu es el mismo procedimiento que en InitDialog(), pero con dos salvedades. Una, es la de verificar que BMP se est mostrando, esto se hace en la sentencia IF y la otra salvedad es que lo primero que se hace es eliminar la referencia anterior de la variable CBitmap m_bmp. La misma variable tiene el mtodo DeleteObject() que se encarga de esto. Resumiendo: En ResourceView se pueden insertar nuevos recursos de iconos y mapas de bits. All tambin se le asigna un ID, necesario para usar el recurso por cdigo. A un control Picture se le puede asignar en tiempo de diseo un icono o imagen por defecto, en la ventana Propiedades. Para cargar un icono hay que usar la funcin LoadIcon() de la clase CWinApp, para esto hay que definir un puntero a esta clase y una variable de tipo HICON. Para cargar un mapa de bits hay que, primero declarar una variable pblica de tipo CBitmap con la cual se carga un mapa de bits con la funcin LoadBitmap(). A veces es necesario eliminar referencias anteriores con DeleteObject(). GRFICOS EN VISUAL C++: Existe en Windows un concepto denominado Contexto de dispositivo. Un contexto de dispositivo proporciona el medio sobre el cual se dibujan todos los puntos, lneas, figuras, fuentes y todo lo que se ve. La palabra dispositivo se refiere al lugar donde se dibujar, o sea, la pantalla, impresora, plotter o cualquier otro dispositivo de cualquier fabricante, sin necesidad de tener un excesivo conocimiento del modelo del mismo. Algo positivo que ha hecho Microsoft para la industria del software ha sido estandarizar el soporte para todos los dispositivos en el sistema Windows. TIPOS DE CONTEXTO DE DISPOSITIVOS: Existe un contexto de dispositivo estndar global y adems hay otros para ocasiones especiales y tareas concretas. Todos son objetos GDI (interfaz de dispositivo grfico). El GDI son funciones grficas de la librera GDI32.DLL y proporcionan el enlace con los controladores de dispositivos y el propio hardware. La MFC (Microsoft Foundation Classes) proporciona clases que simplifican la interaccin con los GDI. La clase del contexto de dispositivo global es la CDC.

Esta clase proporciona funciones de dibujo, de correspondencia entre coordenadas y otras para implementar la visualizacin de grficos. Todas las dems clases de contexto de dispositivo, ms especializadas, se basan en sta y la extienden. La clase CDC: Para ejemplificar el uso de CDC vamos a crear una aplicacin que pinte toda la pantalla, para esto entonces, cree un proyecto MFC basado en dilogos, con el nombre graf1. Reduzca las dimensiones del dilogo un poco y al botn por defecto Cancelar cmbiele el ID por IDC_DIBUJAR y Caption= Dibujar. Tiene que quedar un dilogo con slo dos botones: Aceptar y Dibujar. Pulse dos veces sobre el botn Dibujar para crear el manejador de mensajes OnDibujar() all escriba: void CGraf1Dlg::OnDibujar() { // TODO: Add your control notification handler code here CDC* pDC; (1) CWnd* pEscritorio; (2) int x, y; pEscritorio = GetDesktopWindow(); (3) pDC= pEscritorio->GetWindowDC (); (4) for(x=0; x<800; x++) for(y=0; y<600; y++) pDC->SetPixel (x, y, RGB(x*y,0,0)); (5) pEscritorio->ReleaseDC (pDC); (6) } En la primer lnea se declara una variable puntero, pDC, a CDC, por lo general siempre se declara el futuro objeto CDC como un puntero porque el retorno de la funcin que los crea es un puntero. Como se desea utilizar toda la pantalla y no slo la regin ocupada por nuestro dilogo deberemos relacionar el contexto de dispositivo con el escritorio, cosa que en Windows equivale a toda la pantalla, entonces en la lnea (2) se declara un puntero a la clase CWnd y en (3) se toma el manejador, (puntero CWnd), del escritorio por medio de la funcin global GetDesktopWindow(). Una vez que tengo el manejador, que sera la salida de mi contexto de dispositivo, creo el objeto CDC en la lnea (4) por medio de la funcin GetWindowDC() perteneciente a la clase CWnd, que en nuestro caso est apuntando a la ventana que representa el escritorio. Una vez que uno tiene referenciado el objeto CDC, en nuestro caso por medio del puntero pDC, se pueden acceder a todas las funciones GDI disponibles haciendo pDC->funcin, (note que se utiliza el operados -> y no el punto ya que la variable es un puntero). El dibujo que vamos a hacer ser recorrer todo el escritorio dibujando de un color en particular cada pxel, por eso se usan dos ciclos variar para el ancho y el alto, (mi resolucin de video es de 800 x 600, en caso de tener una resolucin diferente hay que cambiar estos valores). En la lnea (5) se usa la funcin GDI SetPixel() cuyos parmetros son los puntos x e y donde se dibujar el pxel y el otro parmetro es el color del mismo. Para el color usamos la funcin RGB() donde los parmetros representan el grado de rojo, verde y azul, entre 0 y 255. La intensidad de verde y azul la dejamos en 0 y el rojo ser

el producto de las coordenadas x e y lo que da un efecto interesante llamado tramas moir. La ltima lnea libera la referencia al objeto CDC del escritorio, esto es importante hacerlo ya que ocupa memoria. Antes de ejecutar la aplicacin, hay que hacer otra cosa importante, que es limpiar la pantalla cuando la aplicacin termine, porque sino todo el escritorio quedar dibujado de forma permanente hasta que se pulse F5 que se encarga en Windows de re-dibujar. Entonces utilizaremos el evento, (mensaje), WM_DESTROY que se ejecuta cuando la aplicacin termina, para re-dibujar el escritorio y dejar todo como antes. Hay que agregar el mensaje WM_DESTROY, para esto pulse CTRL.+ W para acceder a ClassWizard. All en Objects Ids seleccione CGraf1Dlg y en messages WM_DESTROY, luego pulse Add Function y luego Edit Code, se editar el mensaje OnDestroy(), all escriba: void CGraf1Dlg::OnDestroy() { CDialog::OnDestroy(); // TODO: Add your message handler code here // re-dibujar todo lo anterior CWnd* pEscritorio; pEscritorio=GetDesktopWindow(); pEscritorio->RedrawWindow(NULL,NULL, RDW_ERASE+RDW_INVALIDATE+RDW_ALLCHILDREN+RDW_ERASENO W); } Nuevamente se toma el manejador del escritorio con un puntero CWnd y luego se usa la funcin RedrawWindow(). Los dos primer parmetros hacen referencia a la regin a re-dibujar, como en nuestro caso pretendemos redibujar todo el escritorio estos parmetros sern NULL y el tercer parmetro son combinaciones de constantes que indican como se re-dibujar, RDW_ERASE va junto con RDW_INVALIDATE donde est ltima si el segundo parmetro es NULL, (nuestro caso), indica que se re-dibujar todo incluyendo las aplicaciones descendientes tal como lo especifica RDW_ALLCHILDREN. Pruebe escribiendo este cdigo en el mensaje WM_MOUSEMOVE y obtendr un prototipo de salva pantalla. Ahora si ejecute la aplicacin y pulse Dibujar, obtendr el siguiente dibujo en toda la pantalla:

Para cerrar la aplicacin tendr que adivinar donde est el botn Aceptar detrs del dibujo. En caso de querer usar como objeto de salida el dilogo lea el siguiente ejemplo de este captulo. Resumiendo:

Las funciones grficas de Windows forman parte de una librera llamada GDI, a la cual se puede acceder desde Visual C++ haciendo uso de la clase CDC que es la clase principal para el uso de contextos de dispositivos (pantalla, impresora, casco de realidad virtual, etc). Para usar toda la pantalla como lugar donde dibujar, (la pantalla en Windows equivale al escritorio), se debe declarar un puntero a la clase CDC y otro a la clase CWnd. Entonces con la funcin GetDesktopWindow() uno puede almacenar el manejador del escritorio en el puntero CWnd antes declarado y luego tomarlo como dispositivo de contexto con la funcin GetWindowDC() del puntero CWnd declarado. Despues puede hacer uso de muchas funciones de grficos: SetPixel(), LineTo(), Ellipse(), etc. Luego se debe liberar la memoria utilizada para el contexto usando la funcin ReleaseDC() del objeto CWnd pasandole como parmetro el puntero CDC usado. No hay que olvidarse de re-dibujar el escritorio por ejemplo en el evento WN_DESTROY para as tener todo lindo como antes. Dibujar en un dilogo cliente: En el ejemplo anterior dibujamos sobre el escritorio utilizando la clase principal para definir un contexto de dispositivo, CDC. Usando esta misma clase tambin se puede establecer como contexto de dispositivo un dilogo, al cual se le llama cliente. En este caso no hara falta obtener ningn manejador de ventana por medio de una funcin, pues el puntero a un dilogo activo ya se encuentra referenciado con la palabra clave this, por lo que slo hara falta definir el puntero CDC y obtenerlo desde el cliente: CDC* pDC; pDC=this->GetWindowDC(); De esta forma puede uno, luego, usar la funcin SetPixel() o cualquier otra del puntero pDC y el efecto se producir en toda la ventana del dilogo, incluyendo la barra donde est el ttulo de la ventana y los botones de cerrar, maximizar y minimizar, (para que el dibujo se realice slo dentro del dilogo; en lugar de GetWindowDC() habra que utilizar GetDC()). Bien, VC++ nos da otra posibilidad ms de hacer lo mismo usando una clase descendiente de CDC que se llama CClientDC y se dedica pura y exclusivamente a definir como DC el dilogo cliente (sin incluir la barra de ttulo). Para probar esto con un ejemplo cree un proyecto MFC basado en dilogo con el nombre graf2 y realice los cambios en el dilogo de manera tal que quede exactamente igual que el del ejemplo anterior, o sea con un botn Aceptar y el otro Dibujar, con ID=IDC_DIBUJAR. Pulse dos veces el botn dibujar para generar el evento OnDibujar(), all escriba: void CGraf2Dlg::OnDibujar() { // TODO: Add your control notification handler code here //Se construye un DC cliente a partir de la ventana de dilogo CClientDC dlgDC(this); (1)

int x,y; for (x=0; x<=200; x++) for(y=0; y<=200; y++) dlgDC.SetPixel (x,y,RGB(x*y,0,0)); (2) } Listo!, simplemente se crea el DC para el dilogo segn la sentencia (1), (se define y se crea todo de un saque). Luego con la variable dlgDC tengo acceso a todas las funciones grficas (2) y el efecto se produce sobre el dilogo. Aqu no es necesario liberar la memoria utilizada usando ReleaseDC(), ni limpiar nada, pues al descargar el dilogo se soluciona el tema. Ejecute el programita y obtendr una salida como la siguiente:

Ver que el dibujo est limitado al interior del dilogo, en caso de querer dibujar sobre la barra de ttulo hay que usar la clase CDC tal como se explic al principio de este ejemplo. Resumiendo: Se puede dibujar sobre un dilogo, (cliente), usando la clase CClientDC, construyendo el objeto DC simplemente al pasarle el puntero del dilogo cliente (this). La clase CDC es la clase principal para crear un contexto de dispositivo. DIBUJAR Y REDIBUJAR (la clase CPaintDC, el mensaje WM_PAINT): La clase CPaintDC es un contexto de dispositivo especial, derivada de CDC, que sirve de ayuda para manejar el mensaje WM_PAINT procedente de Windows. El mensaje WM_PAINT se enva a las ventanas cuando dejan de estar cubiertas totalmente o en parte por otra ventana; indica que la aplicacin debe redibujar la regin descubierta. En lugar de volver a pintar toda la ventana cada vez que se descubre alguna parte, WM_PAINT nos pasa un rectngulo con las coordenadas del pedacito descubierto. Entonces se puede emplear esta informacin para volver a dibujar solamente la parte afectada. Para ver esto crearemos un proyecto MFC, basado en dilogos llamado graf3. Realice las misma modificaciones al dilogo igual que en el ejemplo del captulo anterior; esto es cambie el botn Cancelar por Dibujar con ID=IDC_DIBUJAR. El cdigo que vamos a escribir ser muy similar al escrito en el ejemplo anterior, pero en principio no lo escribiremos directamente en el evento OnDibujar de botn Dibujar, sino que crearemos una funcin que luego

llamaremos al pulsar el botn y adems en el evento OnPaint, para manejar el mensaje WM_PAINT. Para crear la funcin en ClassView pulse con el botn derecho sobre la clase CGraf3Dlg y seleccione del men contextual la opcin Add Member Function, luego en Function Type escriba void y en Function Declaration escriba Dibujar. Pulse OK y se editar la funcin, all escriba: void CGraf3Dlg::Dibujar() { CPaintDC paintDC(this); (1) //DC para pintar sobre el dilogo. RECT *pRect; (2) //Puntero a la clase RECT para almacenar //los puntos de la regin a redibujar. int x,y; //Se asocia el puntero pRect a la variable m_ps, miembro //de CPaintDC, que tiene las coordenadas de la regin. pRect=&paintDC.m_ps.rcPaint; (3) //Se recorre para pintar, unicamente la regin descubierta. for (x=pRect->left; x<pRect->right; x++) for (y=pRect->top; y<pRect->bottom; y++) paintDC.SetPixel(x,y,RGB(x*y,0,0)); } Bien, en primero lugar se declara una variable objeto CPaintDC para pintar sobre el dilogo (1). En (2) se declara una variable de tipo RECT, (tambin existe una clase CRect), para almacenar los puntos de la regin a redibujar. RECT es una estructura predefinida en VC cuyos campos son: typedef struct tagRECT { LONG left; LONG top; LONG right; LONG bottom;} RECT; left indica la coordenada x del punto superior izquierdo. top indica la coordenada y del punto superior izquierdo. right indica la coordenada x del punto inferior izquierdo. bottom indica la coordenada y del punto inferior izquierdo. Lo siguiente a hacer es cargar la estructura pRect con los puntos de la regin, (rectngulo), a redibujar; esto se hace en la lnea (3) ya que en el mismo objeto CPaintDC creado existe una variable miembro pblica llamada m_ps de tipo PAINTSTRUCT que contiene informacin que le sirve a la aplicacin para pintar un rea cliente de un dilogo, (ventana), asociado con un objeto CPaintDC. Una estructura PAINTSTRUCT contiene los siguientes campos: typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[16];} PAINTSTRUCT

hdc identifica el dispositivo de contexto usado para pintar. fErase especifica si el fondo de la ventana necesita redibujarse. Si es distinto de 0, es porque la aplicacin debera redibujar el fondo. rcPaint especifica las esquinas superior izquierda e inferior derecha del rectngulo que necesita ser redibujado. Este es el campo que usamos en la lnea (3). fRestore campo reservado. Windows lo usa internamente. fIncUpdate campo reservado. Windows lo usa internamente. rgbReserved[16] bloque de memoria reservado que usa Windows internamente. Lo que resta es recorrer el rectngulo pRect por medio de unos bucles y pintarlo nuevamente con la funcin SetPixel(). Esto es todo lo que respecta a la funcin Dibujar, ahora hay que llamarla desde el evento OnDibujar() y desde OnPaint(). Entonces pulse dos veces sobre el botn Dibujar, aparece un aviso indicando que est por crear un manejador de mensajes llamado OnDibujar(), acepte el aviso y escriba: void CGraf3Dlg::OnDibujar() { // TODO: Add your control notification handler code here Dibujar(); //Aqu est la llamada a nuestra funcin. } Para escribir la llamada en OnPaint(), pulse dos veces en dicha funcin en el ClassView. void CGraf3Dlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting //Resumiendo un poco el cdigo .... // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { Dibujar(); //Esta es la llamada. CDialog::OnPaint(); } } Ejecute la aplicacin y obtendr ms o menos la siguiente salida:

Pruebe superponer ciertas zonas de la ventana con otra, (ejemplo la

calculadora), y ver que cuanto ms pequea sea la zona que cubre y descubre ms rpido ser repintada. Resumiendo: La clase CPaintDC es una clase derivada de CDC como contexto de dispositivo para pintar y permite capturar los mensajes BEGIN_PAINT y END_PAINT (de WM_PAINT). Los datos correspondientes a los vrtices de la regin a redibujar se pasan a una variable de tipo RECT igualandol al campo rcPaint de la variable miembro m_ps de tipo PAINTSTRUCT perteneciente a la clase CPaintDC. (pRect=&paintDC.m_ps.rcPaint;) El evento OnPaint(), (mensaje WM_PAINT), es el lugar encargado de manipular todo el cdigo referente a redibujar, repintar una ventana. LPICES Y PINCELES (las clases CPen y CBrush): El GDI de Windows proporciona adems de cientos de funciones grficas para dibujar lneas, crculos, elipses etc., herramientas con las cuales dibujarlas y pintarlas. Estas herramientas son los lpices y los pinceles, las clases CPen y CBrush. Cundo usar lpices y/o pinceles?. Bueno, si uno quiere, por ejemplo, dibujar una lnea roja de un cierto espesor debe usar un lpiz y especificarle que ser de color rojo y que el trazo ser de determinado espesor. Si se quiere pintar el interior de un circulo de color verde, se debe crear un pincel, (Brush), y especificarle tal color. En la prctica, el mtodo operativo consiste en definir una variables CPen para el nuevo lpiz y un puntero CPen para almacenar el lpiz actual, (esto siempre es conveniente hacerlo, y debe ser un puntero la variable puesto que la funcin que permite guardar el lpiz retorna un puntero). Luego se crea el lpiz, (CreatePen()), especificndole las caractersticas deseadas, (estilo, ancho, color); finalmente se guarda el lpiz actual y selecciona el nuevo, (estos dos pasos lo hace la funcin SelectObject() de un saque). El ejemplo con el que vamos a ver todo esto tendr como objetivo que el usuario pueda dibujar lneas con slo pulsar el botn izquierdo en un punto de la ventana y luego en otro para que se realice el trazo entre ellos. Adems cada lnea que se dibuje tendr un color aleatorio.

Se podrn dibujar cuantas lneas uno quiera y si se desea limpiar la ventana, (que desaparezcan todas las lneas) bastar con slo pulsar el botn derecho. Para ver el tema de los pinceles haremos que al hacer doble click con el botn izquierdo del mouse se pinte la ventana con un color aleatorio.

Cree, entonces, con el AppWizard un proyecto MFC basado en dilogos con el nombre lineas. Anlisis del programa: En principio necesitamos agregar funciones para gestionar los mensajes WM_LBUTTONDOWN, WM_LBUTTONDBLCLK y WM_RBUTTONDOWN que permitirn dibujar, pintar la ventana y limpiar. As que agregue estas funciones siguiendo los pasos acostumbrados con el ClassWizard, (CTRL+W y agregar las funciones). Para poder realizar el trazo entre los puntos indicados por el usuario se necesitar una variable global que almacene el primer punto. Esta variable ser de tipo CPoint, la cual tiene dos campos miembros: x e y, donde almacenaremos las coordenadas. Tambin necesitaremos otra variable global para poder detectar si es la primera vez que se pulsa el mouse sobre la ventana y en consecuencia se guarda el punto y no se dibuja la lnea. Los primeros pasos entonces son: Pulse CTRL+W para acceder a ClassWizard y agregue las funciones WM_LBUTTONDOWN, WM_LBUTTONDBLCLK y WM_RBUTTONDOWN a la clase CLineasDlg. Pulse con el botn derecho del mouse sobre la clase CLineasDlg en ClassView y seleccione la opcin Add member variable. Cree una llamada ptoAnterior de tipo CPoint y otra llamada Primero de tipo BOOL. Pulse dos veces sobre el constructor de la clase CLineasDlg, (el primer tem al expandir la rama de la clase). All escriba: Primero = TRUE; (valor inicial para la variable Primero). Luego de estos primeros pasos estamos en condicin de escribir el cdigo correspondiente al mensaje WM_LBUTTONDOWN, el mismo que se encargar de dibujar la lnea. Expanda la clase CLineasDlg en el ClassView y pulse dos veces sobre la funcin miembro OnLButtonDown, se editar la funcin, escriba: void CLineasDlg::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CClientDC dlgDC(this); (1) CPen* oldLapiz; (2) CPen Lapiz; (3) int r, v, a; //Valores aleatorios para los colores rojo, verde y azul. r=rand()%255; (4) v=rand()%255; (5) a=rand()%255; (6) Lapiz.CreatePen (PS_SOLID,2,RGB(r,v,a)); (7) oldLapiz = dlgDC.SelectObject (&Lapiz); (8) if (Primero) //Si es la primera vez que se pulsa el mouse. {

ptoAnterior=point; Primero=FALSE; } else { dlgDC.MoveTo (ptoAnterior); (9) dlgDC.LineTo (point); (10) ptoAnterior=point; (11) } dlgDC.SelectObject (oldLapiz); (12) CDialog::OnLButtonDown(nFlags, point); } Tenemos que dibujar, y la salida ser sobre el dilogo, entonces en (1) creamos un contexto de dispositivo para nuestro dilogo. En la lnea (2) se define un puntero a la clase CPen para luego guardar el lpiz actual y en (3) una variable CPen para el nuevo. Como la idea es que cada lnea que se dibuje tenga un color diferente en (4), (5) y (6) se toman valores aleatorios para las variables r, (rojo), v, (verde) y a, (azul), que luego sern utilizadas en la funcin RGB(). La funcin rand() devuelve un valor aleatorio entre 0 y 1, si tomamos el resto de dividir este nmero por 255 obtendremos un valor entre 0 y 255. Un tema a tener en cuenta es que, obtendremos valores aleatorios para cada variable durante la ejecucin del programa, pero estos mismos valores aleatorios se repetirn en cada ejecucin, a menos que de alguna forma le indiquemos a la mquina que regenere, (en VB esto se hace con Randomize), en cada ejecucin la lista de nmeros aleatorios. Los nmeros aleatorios se obtienen haciendo una cuenta con la hora del sistema, entonces para lograr que en cada ejecucin se obtengan nuevos valores aleatorios hay que escribir una sentencia en el mensaje OnInitDialog(), que ms adelante se presenta. Continuando con el anlisis del cdigo, en (7) utilizamos la funcin CreatePen() de la clase CPen, cuyos parmetros son: el estilo, el ancho y el color. BOOL CreatePen( int nPenStyle, int nWidth, COLORREF crColor ); nPenStyle: Especifica el estilo del trazo del lpiz, de acuerdo a las siguientes constantes: PS_SOLID: Trazo slido. PS_DASH: Trazo discontinuo en lneas, (el ancho debe ser 1 o menos). PS_DOT: Trazo discontinuo en puntos, (el ancho debe ser 1 o menos). PS_DASHDOT: Trazo discontinuo alternando lneas y puntos, (el ancho debe ser 1 o menos). PS_DASHDOTDOT: Trazo discontinuo alternando lneas con dos puntos, lnea+punto+punto+lnea+..., (el ancho debe ser 1 o menos). PS_NULL: Crea un lpiz nulo.

PS_INSIDEFRAME: Crea un lpiz que escribe slo dentro de los lmites de alguna figura dibujada con alguna funcin GDI, como: Ellipse(), Rectangle(), Chord(), etc. El color del lpiz lo establecemos con la funcin RGB(), combinando los valores aleatorios para r, v y a anteriormente tomados. La funcin del contexto de dispositivo, (en nuestro caso el contexto est representado por la variable dlgDC), SelectObject() se encarga, por un lado de seleccionar como activo para el contexto de dispositivo el objeto que se le pase como parmetro, en este caso, el nuevo lpiz y adems retorna el lpiz actual, el cual guardamos para luego, con esta misma funcin volver a activarlo. Esto es lo que hace la lnea (8). Se verifica si es la primer pulsacin del botn izquierdo del mouse, ya que de ser as guardamos como punto anterior, (ptoAnterior), el punto donde fue pulsado el mouse en el dilogo, el cual est almacenado en el parmetro point de tipo CPoint del evento OnLButtonDown(). En caso contrario, (no es la primera vez que se pulsa el mouse), es porque por lo menos una vez ya ha sido pulsado, lo que implica que la variable ptoAnterior ya tiene almacenado un valor, (el ltimo punto donde fue pulsado el mouse), entonces en (9) movemos el cursor grfico a dicho punto anterior y desde all tiramos una lnea en (10) hasta el nuevo punto que es la ltima pulsacin y se encuentra almacenada en el parmetro point, (la funcin LineTo() de la clase CClientDC dibuja una lnea desde el punto donde se encuentra el curso grafico, el cual es obviamente invisible, hasta el punto pasado como parmetro), el cual en (11) almacenamos como punto anterior, para la siguiente pulsacin. En (12) seleccionamos nuestro lpiz anterior. An nos falta escribir el cdigo que se encarga de limpiar la pantalla al pulsar el botn derecho, el cdigo que se encarga de pintar con diferentes colores el fondo de la ventana al hacer doble click y una pequea porcin de cdigo en OnInitDialog() para mostrar un mensaje informativo antes de empezar y regenerar los nmeros aleatorios. Veamos el cdigo a escribir en OnInitDialog(). BOOL CLineasDlg::OnInitDialog() { CDialog::OnInitDialog(); CString Texto; (1) Texto="DBLCLICK cambia el color de fondo\n"; (2) Texto=Texto+ "CLICK dibuja una lnea\n"; (3) Texto= Texto+"CLICK CON EL DERECHO limpia la pantalla"; (4) // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here srand( (unsigned)time(NULL)); (5)

MessageBox(Texto, "Graficos", MB_ICONINFORMATION); (6) return TRUE; // return TRUE unless you set the focus to a control } En (1) se declara una variable de tipo CString a la que en (2), (3) y (4) se le asigna un texto para mostrar en (6) con la funcin MessageBox(). En (5) se llama a la funcin srand() pasndole como parmetro la funcin time() lo que permite regenerar la cuenta para la obtencin de nmeros aleatorio. Ahora pulse dos veces en ClassView sobre el mtodo miembro de CLineasDlg, OnRButtonDown() para editar el evento y all escribiremos el cdigo que permite limpiar la ventana, o sea borrar todas las lneas que se han dibujado. Escriba: void CLineasDlg::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default Primero=TRUE; //Como se borra el dibujo, se vuelve a desde el principio RedrawWindow(NULL, NULL, RDW_ERASE|RDW_INVALIDATE); (1) CDialog::OnRButtonDown(nFlags, point); } Simplemente se le vuelve a asignar a la variable booleana Primero el valor TRUE, puesto que se va a empezar de nuevo a dibujar y adems, (1), se usa la funcin RedrawWindow() que ya fue explicada en el captulo anterior aunque aqu slo se limpia el dilogo cliente y no todo el escritorio. Todavia falta escribir el cdigo en el evento OnLButtonDblClk(), pero puede probar la aplicacin y experimentar dibujando varias lneas.

Pinceles o Brochas (la clase CBrush): Para terminar nuestro pequeo programa de dibujo, tenemos que escribir el cdigo que permita, al hacer doble click, pintar el dilogo de un color aleatorio. Qu se necesita?. Primero que todo crear el objeto CClientDC, luego declarar una variable CBrush, que ser nuestro pincel y crearlo por medio de la funcin CreateSolidBrush(). Un pincel pinta una figura o una regin, entonces, de alguna manera debemos indicarle cual es nuestra regin. Esta no es ni ms ni menos que el rectngulo que da forma a nuestro dilogo cliente; para obtener sus dimensiones se declara una variable de tipo CRect, (ver pgina 68),que luego rellenamos con la funcin GetClientRect(). Vaya a ClassView y pulse dos veces sobre el procedimiento miembro OnLButtonDlbClk(), all escriba: void CLineasDlg::OnLButtonDblClk(UINT nFlags, CPoint point) {

// TODO: Add your message handler code here and/or call default CClientDC dlgDC(this); CBrush Pincel; (1) CRect dlgRect; //Objeto CRect para identificar la ventana int r, v, a; r=rand()%255; //Valores aleatorios para los colores v=rand()%255; (2) a=rand()%255; Pincel.CreateSolidBrush (RGB(r,v,a)); (3) GetClientRect(dlgRect); (4) dlgDC.FillRect (dlgRect,&Pincel);(5) CDialog::OnLButtonDblClk(nFlags, point); } Bien, adems de crear nuestro objeto CClientDC, declaramos una variable de tipo CBrush para el pincel, una de tipo CRect para almacenar las dimensiones de nuestra ventana y algunas enteras para guardar los nmeros aleatorios (2). En (3) creamos el pincel con la funcin CreateSolidBrush() miembro de CBrush. El nico parmetro que necesita es el color. Como para pintar se necesita una figura o una regin especfica, debemos obtener las dimensiones del dilogo y guardar los resultados, (las coordenadas de los dos puntos en diagonal que forman un rectngulo), en la variable CRect. Esto lo realiza la funcin GetClientRect() cuyo parmetro es una variable de la clase CRect o una estructura RECT donde almacena los datos. Con la funcin FillRect() de la clase CClientDC, finalmente pintamos. Los parmetros son: la variable CRect, o sea la regin a pintar, (puesto que FillRect() slo pinta rectngulos) y la direccin de memoria de la variable CBrush, o sea, nuestro pincel. Listo, pruebe la aplicacin y ver que haciendo doble click se pinta el dilogo siempre con un color diferente. Resumiendo: Se puede dibujar una lnea por medio de la funcin LineTo() de la clase CDC, (o CClientDC, esta ltima es una derivada de la primera). La lnea se dibujar partiendo del punto actual donde se encuentre el cursor grfico hasta el punto, (variable CPoint), pasado como parmetro. Para cambiar la posicin del cursor grfico se utiliza la funcin MoveTo() cuyo parmetro es el punto a donde debe desplazarse. El color de una lnea los especifica el lpiz actual, para utilizar otro se define una variable CPen para el nuevo lpiz y un puntero CPen para guardar temporalmente el lpiz actual. Luego se crea el lpiz con CreatePen(), funcin de CPen, y se lo selecciona con SelectObject(), funcin de CDC. SelectObjet() adems permite guardar el lpiz anterior. Luego de dibujar, nuevamente con SelectObject() recuperamos el lpiz anterior.

Para pintar una figura se utilizan los pinceles o brochas, (clase CBrush), y simplemente hay que declarar una variable CBrush, luego crearla con CreateSolidBrush() y listo. En caso de pintar un rectngulo se utiliza la funcin FillRect(). APLICACIONES SDI, (SINGLE DOCUMENT INTERFACE): Como habr visto mientras utiliza algunos programas de Windows, existen notables diferencias entre las aplicaciones que venimos haciendo y esos programas. Ejemplo: el Wordpad es bien diferente, por lo menos en aspecto, a los ejemplos que he presentado hasta ahora, y ms diferente es an Excel. Bsicamente existen 3 tipos de aplicaciones Windows: - Basadas en dilogos: que es lo que hemos hecho hasta ahora. Simplemente ventanas. - SDI, (Single Document Interface): Aplicaciones de interfaz de un nico documento. Dentro de esta categora est por ejemplo el Wordpad y el Paint. - MDI, (Mltiple Document Interface): Aplicaciones de interfaz de mltiples documentos. Excel, FrontPage, etc. En una aplicacin SDI hasta que no se guarde el trabajo no se puede abrir otro documento. Por ejemplo en Worpad hasta que no se guarde, o se cancele, lo que se est escribiendo, no se puede abrir uno nuevo o uno existente. Arquitectura Documento - Vista: En una aplicacin SDI uno tiene la informacin, (documento), y diferentes formas de presentarla, (vista). Todo esto contenido de alguna forma, (marco). Bien, cuando uno crea con el ApplicationWizard una aplicacin SDI se generan 3 clase muy importantes, adems de la clase para la aplicacin y para el dilogo Acerca de. Estas 3 clases son derivadas de CDocument, CView y CFrameWnd. CDocument: La clase CDocument provee la funcionalidad bsica para clases documentos definidas por el usuario. Un documento representa la informacin que por lo general el usuario manipula cuando abre o guarda un archivo utilizando el men Archivo. CView: La clase CView provee la funcionalidad bsica para las clases vistas definidas por el usuario. Una vista es la intermediaria entre el documento y el usuario. La vista se encarga de representar la informacin contenida en el documento. Existen varias vistas derivadas de CView: - CListView. - CCtrlView. - CDaoRecordView. - CEditView. - CRecordView. - CFormView. - CRichEditView. - CScrollView.

- CTreeView. (para ms informacin sobre las vistas vea la ayuda de Visual C++). CFrameWnd: Esta clase es la esencia de una aplicacin SDI. Es la ventana contenedora de la vista y el documento. Bueno, ahora vamos a crear una aplicacin SDI. La aplicacin tendr como objetivo algo parecido a lo que hicimos en el captulo anterior. Se dibujarn lneas con slo pulsar el botn izquierdo del mouse y con el derecho se limpia la pantalla. Ingrese a Visual C++, vaya al men FILE - NEW y en la solapa PROJECTS seleccione MFC AppWizard (exe). En Project Name escriba "lineas2", pulse OK. En el siguiente paso seleccione SINGLE DOCUMENT.

Pulse NEXT. Deje como estn por defecto las opciones de las siguientes dos pantallas, hasta que llegue al paso 4, all pulse el botn ADVANCED:

En esta ventana le decimos a AppWizard que extensin tendrn los archivos que manipule nuestra aplicacin, (cuando sta pueda grabar y abrir). Entonces en File extension escriba "lin", esta ser la extensin de nuestros archivos. Pulse Close, la ventana del paso

4 indica que tendr por defecto nuestra aplicacin SDI, como por ejemplo "barra de estado", "barra de herramientas", etc. Esa opciones djelas como estn por defecto y pulse FINISH. AppWizard crear el armazn de nuestra primer aplicacin SDI, que es totalmente funcional, puede si lo desea probar el programa pulsando CTRL-F5 y ver que funciona, aunque an no pasa nada si se pulsa algn elemento del men.

Ahora analizaremos un poco que es lo que AppWizard nos gener automticamente. Mire algo curioso; vaya a la solapa ResourceView de la ventana Workspace y expanda la carpeta Dialog. Donde est el diseo de la aplicacin?, esa ventana con men que aparece cuando ejecut la aplicacin, (imagen sobre estas lneas). En la carpeta Dialog solamente encontrar el diseo del dilogo Acerca de, y solamente eso. Lo que ocurre es que la ventana con el men, la barra de herramientas y la barra de estado que UD ve cuando ejecuta el programa es la ventana marco creada por la clase CFrameWnd, (siga leyendo que ms abajo se aclara todo). La regin en blanco es la vista donde se mostrar la informacin del documento, en nuestro caso esa informacin sern lneas. Ahora seleccione la solapa ClassView de la ventana Workspace all ver las clases que forman nuestra aplicacin:

CAboutDlg: Es la clase del dilogo Acerca de. CLineas2App: Es la clase aplicacin presente en todos los programas, sean Basados en dilogos, SDI o MDI. CLineas2Doc: Es la clase documento, la cual se encarga de manejar los datos, (la informacin), del programa.

CLineas2View: Es la clase vista que se encargar de mostrar la informacin en pantalla o en impresora. CMainFrame: Es la ventana - marco, contenedora de la vista. Nuestros datos, o informacin, en el documento sern una lista de puntos, (pares x, y), de acuerdo a las pulsaciones del botn izquierdo del mouse en diferentes lugares de la vista. Por ejemplo: si el usuario pulsa 3 veces el botn izquierdo del mouse en diferentes lugares de la vista se obtendra dos lneas uniendo esos puntos, lo que implica que son 3 pares x,y, y esos pares son nuestra informacin y los guardamos en una lista dinmica, (CList), como dato pblico perteneciente a nuestra clase documento. El dibujo de las lneas estar a cargo de la vista, o sea, el cdigo que dibuja las lneas y va guardando los pares x, y, se escribir en el mensaje WM_LBUTTONDOWN de la vista. Entonces lo primero que vamos a hacer es agregar ese dato miembro de tipo CList en la definicin de la clase CLineas2Doc. Pulse dos veces donde dice CLineas2Doc en ClassView, se editar la definicin de la clase: (a continuacin se transcribe el principio de la clase). class CLineas2Doc : public CDocument { protected: // create from serialization only CLineas2Doc(); DECLARE_DYNCREATE(CLineas2Doc) // Attributes public: CList<CPoint, CPoint> m_ListaPuntos; variable de tipo CList. // Operations public:

//Esta lnea declara una

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CLineas2Doc) public: .... } Marcada en amarillo se encuentra la declaracin de la variable CList. La clase CList permite crear objetos que se comportan como las listas doblemente enlazadas de C. Para decirlo de forma sencilla, son listas dinmicas en las cuales uno puede ir agregando elementos sin estar limitados por una cantidad fija a almacenar. Imagine que en lugar de una variable CList que permita almacenar variables tipo CPoint hubiramos declarado una array, (vector), de por ejemplo 50 posiciones de tipo CPoint. Estaramos limitados a guardar slo 50 puntos. Esta, obviamente no es la idea, por eso se usa la clase CList cuya definicin se encuentra en el archivos "afxtempl.h" el que tendremos

que incluir en el archivo de cabecera StdAfx.h. Entonces en la solapa FILES de la ventana Workspace expanda la carpeta Header Files y pulse dos veces sobre StdAfx.h. Agregue luego de la ltima sentencia #include la siguiente lnea: #include "afxtempl.h". La definicin de CList es la siguiente: template< class TYPE, class ARG_TYPE > class CList : public CObject TYPE: Es el tipo de dato, (clase), que guardar la lista. En nuestro ejemplo es CPoint. ARG_TYPE: Es el tipo usado para referenciar los objetos de la lista. Habitualmente este argumento es igual que el anterior. Los valores de la lista se obtienen por medio de una variable de tipo POSITION. Esta lista nos permitir reconstruir nuestro dibujo, (simples trazados de lneas), cuando por ejemplo tenemos que redibujar la vista en caso que se minimice la aplicacin u otra ventana se superponga sobre la nuestra. El siguiente paso es crear una variable miembro en el documento. Esta variable ser de tipo BOOL y no servir para saber si el usuario a pulsado por primera vez el mouse, lo que implica que est comenzando a dibujar lneas. Esta variable le daremos el valor TRUE cada vez que se inicia un nuevo documento, puesto que el mismo est en blanco, disponible para dibujar. Cuando pulse por primera vez el botn del mouse sobre nuestra vista estableceremos su valor a FALSE para saber que ya se est dibujando. Para crear la variable pulse con el botn derecho del mouse, en ClassView, sobre la clase CLineas2Doc y luego seleccione ADD MEMBER VARIABLE. En el cuadro de dilogo que aparece escriba en Variable Type: BOOL y en Variable Name: m_Nuevo. Acepte este dilogo. El valor inicial de esta variable se lo especificaremos cada vez que se crea un nuevo documento, esto es el mensaje OnNewDocument() de nuestro documento. Entonces pulse dos veces sobre OnNewDocument() y se editar la funcin: BOOL CLineas2Doc::OnNewDocument() { if (!CDocument::OnNewDocument()) return FALSE; // TODO: add reinitialization code here // (SDI documents will reuse this document) m_Nuevo=TRUE; return TRUE; } //Aqu se inicializa la variable.

Por ahora este es el cdigo necesario que le incumbe a nuestro documento. Como la vista es la encargada de mostrar los datos y sirve de intermediaria entre el usuario y los datos que se almacenan en el documento, ser en ella, (la vista), donde escribiremos el resto del cdigo de nuestra aplicacin. Qu nos falta programar?. Hay que escribir el cdigo que permita al pulsar el botn izquierdo del mouse, que se guarden en la lista los puntos donde se hizo click y adems dibujar una lnea. Tambin escribiremos cdigo para el botn derecho del mouse, ya que queremos limpiar la vista, (borrar todas las lneas). Un documento puede tener varias vistas, por ejemplo: Excel muestra la informacin en las celdas de una rejilla o bien en un grfico. Estas son dos vistas diferentes de un mismo documento. Por otro lado hay que decir que para una vista existe solamente un solo documento. En nuestro caso tenemos slo una vista para nuestro documento, y es la vista por defecto, (la misma que usa WordPad o Paint). Como se habr percatado, ser necesario acceder a los datos del documento desde la vista, para poder representarlos o trabajar con ellos. Existe una funcin miembro de la vista que nos permite acceder a los datos del documento. Expanda en ClassView la clase CLineas2View y se encontrar con una funcin miembro llamada GetDocument(); pulse dos veces en ella para poder ver su definicin: CLineas2Doc* CLineas2View::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLineas2Doc))); return (CLineas2Doc*)m_pDocument; } Esta funcin, mientras nuestra aplicacin sea de versin DEBUG, estar fuera de la definicin de la clase CLineas2View, pero si es versin RELEASE pasar a ser una funcin InLine, o sea estar definida dentro de la misma clase. Como podr ver en el encabezado y en la sentencia Return, la funcin retorna un puntero a nuestro documento, y precisamente a travs de este puntero accederemos a los datos del documento. La funcin es muy cortita, (por eso en la versin RELEASE ser InLine, pues simplemente retorna una variable), la nica lnea que vale la pena explicar un poco es: ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLineas2Doc))); En "cristiano" esta sentencia dice: "Contina en caso que la variable m_pDocument se una instancia de la clase CLineas2Doc".

La variable m_pDocument es una variable miembro protegida de la clase CView y como CLineas2View es una clase derivada de CView, por herencia, tambin es miembro de ella. Esta variable es un puntero a un objeto de la clase CDocument, (nuevamente por cuestiones de herencia, CLineas2Document que es derivada de CDocument, es la clase apuntada). Esa lnea existe por prudencia, para controlar cualquier error inesperado, pues ASSERT evala la expresin que tiene como parmetro y en caso de ser 0, o sea que m_pDocument no sea una instancia de CLineas2Doc, (eso es lo que hace la funcin IsKindOf()), enva un mensaje de error y salta este procedimiento. Bien, con esta funcin accederemos a los datos del documentos desde la vista, (un poco ms adelante ya haremos uso de la misma). Ahora agregaremos un variable miembro a la vista que nos permita guardar el punto anterior donde fue pulsado el mouse, (el ltimo punto), ya que necesitaremos 2 ptos. para poder trazar la lnea. Pulse con el botn derecho del mouse en ClassView sobre la clase CLineas2View y elija la opcin Add Member Variable del men contextual. Aparecer una ventana que nos pedir el tipo de variable, all escriba CPoint y el nombre de la variable, escriba: m_PtoAnterior. Tilde la opcin Protected, ya que esta variable ser una variable protegida de la clase CLineas2View. Slo tienen acceso a las variables protegidas de una clase las derivadas de esta, en nuestro caso solamente sirve para, por las dudas, "proteger" esta informacin, pero no derivaremos ninguna clase nueva. Agregar mensaje WM_LBUTTONDOWN a la vista: - Pulse Ctrl+W para abrir ClassWizard, vamos a agregar el mensaje WM_LBUTTONDOWN a nuestra vista. - En ClassWizard seleccione CLineas2View en la lista de clases Class Name. - En Object ID's verifique que se encuentre seleccionada la clase CLineas2View y en Messages seleccione WM_LBUTTONDOWN. - Pulse Add Function y luego Edit Code. All escriba: void CLineas2View::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CLineas2Doc *miDoc; (1) miDoc=GetDocument(); (2) //Tomo el documento desde la vista.

if (miDoc->m_Nuevo) //Si es la primera vez que se pulsa en la vista { m_PtoAnterior=point; (3) //Guardo el punto donde se puls. miDoc->m_ListaPuntos.AddTail(point); (4) //Lo agrego a la lista miDoc->SetModifiedFlag(TRUE); (5) //Se est modificando. miDoc->m_Nuevo =FALSE; }

else { CClientDC dlgDC(this); (6) CPen Lapiz; CPen *oldLapiz; Lapiz.CreatePen (PS_SOLID,2,RGB(0,0,0)); oldLapiz=dlgDC.SelectObject(&Lapiz); dlgDC.MoveTo (m_PtoAnterior); dlgDC.LineTo (point); dlgDC.SelectObject (oldLapiz); miDoc->m_ListaPuntos.AddTail(point); m_PtoAnterior=point; } CView::OnLButtonDown(nFlags, point); } Cuando se pulsa el botn izquierdo del mouse pueden ocurrir dos cosas: 1- se puls por primera vez. 2- ya se puls anteriormente. 1 - Si se puls el mouse por primera vez, nuestra variable m_Nuevo an tiene el valor TRUE, entonces simplemente debemos guardar el punto donde se puls el mouse como PtoAnterior y en la lista de puntos, y establecer adems el valor de m_Nuevo a FALSE. Eso es lo que se hace en el bloque verdadero de la sentencia if (m_Nuevo). Antes, en la lnea (1) se declara un puntero a nuestra clase documento y en (2) por medio de la funcin miembro GetDocument() de la vista, se obtiene el valor necesario que hace referencia a el documento. En (3) se guarda el punto como punto anterior, (note que point es el parmetro pasado a el mensaje OnLButtonDown() que precisamente trae las coordenadas donde se puls el mouse). En (4) se lo agrega a la lista con el mtodo de la lista AddTail(). Y como el hecho de pulsar el mouse en la vista supone posibles cambios en el documento, en (5) se establece con la funcin miembro del documento, SetModifiedFlag(), que se estn realizando modificaciones lo que luego dar a lugar, si intentamos salir de la aplicacin, que pregunte si deseamos realizar los cambios en el documento. Se establece m_Nuevo = FALSE, porque la siguiente pulsacin no ser la primera. 2 - Si m_Nuevo no es igual a TRUE es porque ya una vez se puls el mouse, entonces ahora hay que dibujar la lnea entre el punto anterior, m_PtoAnterior y el nuevo punto, o sea point. Adems guardar como punto anterior el nuevo punto, point y agregarlo a la lista. (7) (8) (9) (10) (11) (12) (13)

Para poder dibujar se crea en (6) un contexto de dispositivo para nuestro dilogo, (en realidad afecta slo a la vista), y adems se declaran objetos para el lpiz en uso y el nuevo. En (7) se crea un lpiz de trazo slido, ancho 2 y de color negro el cual se selecciona como activo en (8) guardando el lpiz anterior. Para poder trazar la lnea se mueve el cursor grfico al punto anterior, (9) y desde all se realiza el lnea hasta el punto actual (10). Se selecciona el lpiz anterior (11), se guarda el punto actual en la lista (12) y como punto anterior (13). Pulse CTRL + F5 para probar la aplicacin y ver que puede trazar lneas en la vista:

(las diferencias de las capturas de pantallas se deben a que cambi el Sistema Operativo a Windows XP). Si minimiza la aplicacin o superpone otra ventana ver que se borrarn las lneas, obviamente eso no debe ocurrir, por lo tanto deberemos escribir cdigo en el mensaje WM_PAINT el cual es gestionado por la funcin OnDraw() de la vista. All haremos uso de la lista de puntos pues como tenemos que reconstruir el dibujo, ya que se borraron las lneas, recorreremos toda la lista para ir trazando las lneas con los puntos de la misma. Entonces, pulse dos veces en ClassView sobre la funcin OnDraw() de la vista, y escriba: void CLineas2View::OnDraw(CDC* pDC) { CLineas2Doc* pDoc = GetDocument(); ASSERT_VALID(pDoc); POSITION pos; (1) CPen Lapiz; CPen *oldLapiz; // TODO: add draw code for native data here if (pDoc->m_ListaPuntos.IsEmpty ()) (2) //si la lista est vaca retorno, pues no hay nada que dibujar. return; Lapiz.CreatePen(PS_SOLID,2,RGB(0,0,0));

oldLapiz=pDC->SelectObject(&Lapiz); pos=pDoc->m_ListaPuntos.GetHeadPosition(); (3) //se obtiene el 1 elemento de la lista pDC->MoveTo(pDoc->m_ListaPuntos.GetNext(pos)); //muevo el cursor grfico a ese punto. while (pos!=NULL) //mientras no sea el fin de la lista pDC->LineTo(pDoc->m_ListaPuntos.GetNext(pos)); (4) pDC->SelectObject(oldLapiz); } Como el dibujo se borr parcialmente o en su totalidad porque se superpuso otra ventana o se minimiz la aplicacin, debemos reconstruir el dibujo haciendo uso de los puntos que fuimos guardando en la lista. Los valores almacenados en una lista se obtienen con funciones propias de clase CList junto con una variable de tipo POSITION, que es como una especie de ndice, (no es un ndice, es un puntero a los elementos de la lista, pero en realidad funciona de forma automtica en VC++ ). Por eso en (1) se declara una variable de tipo POSITION adems de los lpices que necesitaremos para dibujar, (el contexto de dispositivo no hace falta crearlo pues en esta funcin es un parmetro). Si la lista est vaca (2), es porque no hay nada que dibujar, por lo tanto retornamos. Creamos y seleccionamos el lpiz a usar y en (3) obtenemos la direccin del primer elemento, (es lo que se almacena en pos) por medio de la funcin GetHeadPosition() e inmediatamente movemos el cursor grfico a el valor almacenado all, GetNext(). A partir de aqu hacemos un bucle, mientras la variable pos no sea nula pues esto ocurre cuando ya no hay ms elementos en la lista. Dentro del bucle dibujamos la lnea desde donde se encuentra el cursor grfico hasta el siguiente punto almacenado(4). Cuando se dibuja la lnea el cursor grfico se mueve automticamente al otro extremo de la misma quedando as preparado para el siguiente trazo. Para terminar este primer ejemplo de aplicacin SDI, nos falta escribir el cdigo que al pulsar el botn derecho del mouse sobre la vista, permita borrar el dibujo. Agregue entonces con ClassWizard el mensaje WM_RBUTTONDOWN y all escriba: void CLineas2View::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CRect miRect; CString Mensaje; Mensaje="Desea borrar todo el contenido del documento?.\n"; Mensaje=Mensaje + "No podr recuperar la informacin.";

if (!GetDocument()->m_Nuevo) (1) if (MessageBox(Mensaje,"Borrar",MB_YESNO+MB_ICONQUESTION)==IDYES) (2) { GetClientRect(&miRect); (3) GetDocument()->m_ListaPuntos.RemoveAll(); (4) RedrawWindow(miRect,NULL,RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE); (5) GetDocument()->m_Nuevo =TRUE; (6) GetDocument()->SetModifiedFlag (FALSE); (7) } CView::OnRButtonDown(nFlags, point); } En (1) averiguamos si la variable m_Nuevo no tiene TRUE, lo que implica que ya hay algo dibujado, entonces por medio de la funcin MessageBox(), (2), preguntamos si realmente se desea borrar las lneas. En caso que conteste que SI, obtenemos las dimensiones de la vista y las almacenamos en el objeto miRect (3). Borramos todo el contenido de la lista (4) y llamamos a la funcin RedrawWindow() pasndole como parmetro las dimensiones de la regin a borrar, miRect, y adems las opciones tpicas RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE (5). En (6) le asignamos a la variable m_Nuevo del documento el valor TRUE, para as puede empezar todo de vuelta. Y finalmente, aunque esto es optativo, se le pasa el valor FALSE a la funcin SetModifiedFlag() para que no pregunte si se desea guardar la informacin en caso de pulsar el icono de guardar o de cerrar la aplicacin, porque se supone que un documento en blanco no se va a guardar. Note que en lugar de guardar el retorno de GetDocument() directamente se accede al documento escribiendo GetDocument()-> , cualquiera de las dos formas son vlidas, usando un puntero, (el retorno de GetDocument()) o la propia funcin. Por ahora esto es todo. Esta es una aplicacin SDI sencilla y lo que vi hasta ahora es como y cuando escribir cdigo en el documento y cuando en la vista. Resumiendo: Una aplicacin SDI bsica consta de cuatro clases: - Clase aplicacin. - Clase Marco. - Clase Vista. - Clase Documento. La clase documento es la encargada de portar los datos, (informacin) y la vista la representa de alguna forma.

Desde la vista se accede a los datos del documento por medio de la funcin GetDocument(), la cual retorna un puntero a la clase documento. Para almacenar informacin en memoria y la cantidad no es fija se puede usar la clase CList. Pasndole como parmetro TRUE a la funcin SetModifiedFlag() del documento se activa la posibilidad de grabar la informacin. TIPOS DE ARCHIVOS: En este captulo veremos los conceptos bsicos de la manipulacin de archivos en VC++. Existen 2 importantes grupos de archivos: - Archivos de texto. - Archivos binarios. Un archivo de texto es aquel cuyo contenido son caracteres, (de la tabla ASCII), que adems representan por si solos informacin. Por ejemplo, archivos de texto son los archivos .cpp que uno crea cuando programa en C++, tambin son de texto los archivos .txt y en general cualquier archivo, (no importa la extensin), cuyo contenido sean caracteres. Los archivos binarios son bien diferentes a los de texto y la diferencia radica en el formato. El contenido de un archivo binario es absurdo y carece de sentido si lo editamos con algn editor de texto, (ejemplo Notepad o el EDIT del DOS), y esto es as en principio por dos cosas: por un lado, el conjunto de caracteres que estn en el archivo no representan alguna informacin lgica al momento de visualizarlos como simples caracteres y fundamentalmente porque la creacin del archivo no sigue la pauta de la de un simple texto, esto es, se cre el archivo para que guarde informacin con un formato determinado. Por ejemplo guardar en un archivo el contenido de una estructura o de un arreglo de estructuras no sigue la regla simple de los archivos de texto de un caracter detrs del otro, saltos de lnea, etc; y si bien la estructura almacena caracteres estos ya estn subordinados a campos lo que cambia el formato del manejo de los datos. Ya no es simplemente texto, sino, informacin de bytes, (seguramente muchos de esos bytes son los caracteres almacenados en el campo APELLIDO de la estructura pero muchos otros no). La MFC ofrece una clase que sirve como "envoltorio" de las funciones para manipular archivos del SDK de Windows. Esta clase se llama CFile. Vamos a hacer una aplicacin que nos permitir crear y editar un archivo de texto; siempre leer y escribir el mismo archivo, "pp.txt", en el directorio actual. Cree un proyecto MFC llamado "Archivos1", basado en dilogos y acepte todas las opciones por defecto, (si quiere puede quitar la creacin del dilogo Acerca de). Disee el dilogo como muestra la figura:

Especifique las siguientes propiedades de los controles: Control de edicin: - ID: IDC_EDIT1

- Tildar los estilos: Multiline, Horizontal Scroll, Auto HScroll, Vertical Scroll, Want Return y Border. Botones: IDC_GUARDA, (Guardar "pp.txt") e IDC_ABRIR, (Cargar "pp.txt"). Con el Class Wizard cree una variable de tipo CString llamada m_strTexto para el control de edicin, (recuerde Class Wizard -> Member Variables -> Seleccione IDC_EDIT1 y pulse Add Variable). Ahora hay que escribir en el mensaje WM_LBUTTONDOWN del botn IDC_GUARDA el cdigo que crea el archivo "pp.txt" y guarda en l el contenido del cuadro de edicin. void CArchivos1Dlg::OnGuarda() { CFile Archi; (1) Archi.Open ("pp.txt", CFile::modeCreate | CFile::modeWrite); (2) archivo UpdateData(TRUE); (3) Archi.Write ( m_strTexto, m_strTexto.GetLength ()); (4) Archi.Close (); (5) m_strTexto=""; (6) UpdateData(FALSE); (7) AfxMessageBox("Archivo guardado con xito"); (8) } En la lnea (1) se crea un objeto CFile llamado Archi y con l en la lnea (2) abrimos el archivo por medio de la funcin miembro Open(). Esta funcin recibe 2 parmetros, el primero es una cadena que indica el archivo que se va abrir, (obviamente se puede incluir el path) y el segundo parmetro especifica el modo de apertura que se pueden combinar con las siguientes constantes: CFile::modeCreate Para crear un nuevo archivo. Si ya existe borra el contenido. CFile::modeNoTruncate Se combina con modeCreate. Si el archivo ya existe lo abre sin borrar el contenido sino lo crea. CFile::modeRead Abre como slo lectura. CFile::modeReadWrite Abre para lectura y escritura. CFile::modeWrite Abre para escribir. CFile::modeNoInherit No permite que el archivo sea heredado por un proceso descendiente. CFile::shareDenyNone Permite que otro proceso lea y/o escriba en el archivo. CFile::shareDenyRead No permite que otro proceso lea el archivo. CFile::shareDenyWrite No permite que otro proceso escriba en el archivo. CFile::shareExclusive No permite que otro procesa pueda escribir y leer en el archivo. CFile::typeText Especifica que el archivo se abre en modo texto con retorno de carro y salto de lnea, (slo se usa en clases derivadas). CFile::typeBinary Especifica que el archivo se abre en modo binario, (slo se usa en clases derivadas). //Abro el

En nuestro caso abrimos el archivo para crearlo y escribir. Open() retorna distinto de 0 en caso que la apertura tenga xito y 0 en caso que falle por algn motivo, (por ejemplo el disco esta protegido contra escritura). Deberamos colocar esta sentencia evaluada en un IF para que sea ms prolijo el cdigo, pero bueno, por ahora confiemos que se podr abrir sin problemas. Una vez abierto el archivo transfiero el contenido del cuadro de edicin a la variable miembro m_strTexto con la conocida funcin UpdateData(TRUE). Y en (4) escribimos en el archivo el contenido de m_strTexto con la funcin Write. Dos parmetros necesita: la variable de tipo CString a escribir y la longitud de la misma. Luego se cierra el archivo con Close(), se "limpia" la variable m_strTexto y se vuelve a llamar a UpdateData() esta vez con el parmetro FALSE para que pase el contenido de la variable al cuadro de edicin, lo que da a lugar que el cuadro de edicin quede vaco. Puede probar la aplicacin. Escriba algo en el cuadro de edicin y luego pulse el botn "Guardar pp.txt". Para verificar si realmente escribi en el archivo vaya a la carpeta donde se encuentre el proyecto y edite "pp.txt". Escribamos ahora el cdigo que me permite editar el archivo en el cuadro de edicin. Para esto agregue el mensaje OnAbrir() para el botn IDC_ABRIR. Para este caso hay que tener en cuenta que quizs el archivo a editar sea muy grande como para intentar leerlo de una vez, por eso vamos a leer, (mientras haya texto que leer), de a pedazos de 512 bytes, (o caracteres, es un estndar este nmero en lo que respecta a lectura en bloques). El procedimiento sera: - Abrir el archivo para slo lectura. - Si se pudo abrir, entonces, leer 512 bytes. - Mientras la cantidad de bytes ledos es mayor a 0, se "acumula" lo ledo en la variable miembro m_strTexto y se vuelve a leer 512 bytes. - Cuando no haya nada que leer se actualiza el cuadro de edicin con el contenido de m_strTexto y se cierra el archivo. Ms o menos debera se as, veamos entonces lo que hay que escribir en OnAbrir(). void CArchivos1Dlg::OnAbrir() { CFile Archi; char strLeo[512]; (1) UINT bLeidos; (2) Archi.Open ("pp.txt", CFile::modeRead); //Abro el archivo

if (Archi) (3) //si retorna distinto de NULL es porque se pudo abrir { m_strTexto=""; bLeidos=Archi.Read (strLeo,sizeof(strLeo)-1); (4) //leo 511 bytes while (bLeidos) (5) { strLeo[bLeidos]=NULL; (6) //Agrego terminacin nula al final m_strTexto += CString(strLeo); (7) //lo acumulo en la variable

bLeidos=Archi.Read (strLeo,sizeof(strLeo)-1); (8) //leo otra vez } Archi.Close (); //cierro el archivo } UpdateData(FALSE); (9) } En (1) se declara una cadena de 512 caracteres, en realidad 511 ya que el ltimo caracter de una cadena en C es '\0', el caracter nulo, (NULL), que indica donde termina la cadena, (en muy importante tener presente sto). En (2) se declara un entero largo, (UINT), donde se almacenar la cantidad de bytes ledo en cada intento. Se intenta abrir el archivo para slo lectura y en (3) se verifica que Archi no sea NULL porque sino implicara que no se pudo abrir el archivo. En caso de no haber problemas, en (4) se hace una lectura del archivo con la funcin Read(). Dos parmetros son necesarios para una simple lectura, primero la variable de tipo cadena, (puede ser CString o como en nuestro caso un arreglo de char) y luego el tamao de la misma que en caso de usar una variable char se obtiene con el operador sizeof(), si fuera CString se utilizara GetLength() como cuando escribimos en el archivo. Se preguntar por qu se hace sizeof(strLeo)-1 y no simplemente sizeof(strLeo); bien, el tema es que como usamos una cadena de tipo char tendremos que "poner a mano" nosotros el caracter nulo en la ltima posicin de la cadena, entonces en vez de leer 512 bytes leemos 511 y nos sobra una posicin para poder colocar el caracter de fin de cadena. Read() retorna la cantidad bytes leidos, entonces en (5) se verifica si algo se ley, o sea si bLeidos es distinto de 0 porque de mientras sea as se continuar leyendo, (es una forma de detectar el fin de archivo). Dentro del mientras, en (6) se le coloca a la cedena la terminacin nula, NULL, y se acumula su contenido en la variable miembro m_strTexto, lnea (7). En (8) se vuelve a leer. Cuando no haya ms bytes que leer el flujo sale del ciclo mientras, se cierra el archivo con Close() y se transfiere todo el contenido de m_strTexto, (que es justamente el contenido del archivo), a el cuadro de edicin por medio de UpdateData(FALSE). Resumiendo: Un archivo se abre usando la funcin Open() de la clase CFile. En los parmetros se especifica el archivo a abrir y el modo de apertura, ejemplo: CFile::modeCreate. Para escribir en un archivo hay que usar la funcin miembro de CFile, Write(), cuyos parmetros permiten especificar el buffer, (la variable que contiene lo que se quiere escribir) y el tamao de la misma. La lectura con Read() sigue lo mismo preceptos que Write(), salvo que el buffer ahora recibir los datos ledos desde el archivo. Es importante el retorno de Read() ya que el mismo devuelve la cantidad de bytes ledos que en caso de alcanzar el fin del archivo es menor que la cantidad leda. Archivos de estructuras:

Dentro del concepto de archivo binario, (archivos BMP, EXE, etc.), exiten los archivos de estructuras. Estos archivos contienen informacin en registros, donde cada registro obedece a una estructura determinada. Por ejemplo, tenemos la estructura Personas: struct Personas { char Apellido[20]; char Nombre[20]; int edad; }; Se puede crear un archivo que almacene registros de tipo Personas, o sea que se escribe y se lee informacin de tipo Personas. Vamos a escribir un ejemplo de aplicacin que manipule un archivo de estructuras, en particular de tipo Personas. Con el Application Wizard cree una aplicacin MFC basada en dilogos, (quite la opcin About Box), y disee el dilogo ms o menos como muestra la figura:

No borre el botn Cancelar, ya que de paso ahora, antes de empezar, veremos como hacer para que el dilogo no se cierre si se pulsa ESC. Cmo hacer para que al pulsar ESC no se cierre un dilogo: Cuando se genera una aplicacin Dialog Based, sta presenta ya un dilogo predefinido con los botones Aceptar y Cancelar, estos botones generan, al pulsarlos, los mensaje predefinidos OnOk() y OnCancel(). En los dos casos el resultado es el mismo, se cierra el dilogo. Para evitar que al pulsar la tecla ENTER se pulse Aceptar se puede optar por quitar el botn o mejor quitar el tilde a la propiedad Default Button en la solapa Style. Pero en el caso del botn Cancelar, aunque se lo borre del dilogo igual el efecto de pulsar la tecla ESC genera el mensaje OnCancel() y esto se puede evitar de la siguiente forma. Antes de disear lo que respecta al ejemplo y teniendo el dilogo en pantalla, pulse dos veces sobre el botn Cancelar para poder acceder al mensaje OnCancel() y alli comente con // la lnea CDialog::OnCancel();. Ahora puede usar el botn Cancelar para que cumpla la funcin de alguno necesario para la aplicacin que vamos a hacer, (por ejemplo puede ser el botn Ver datos), cambiandole el ID por defecto IDCANCEL por el que reemplace. Regresemos a nuestro ejemplo. Las propiedades de los controles y sus variables miembros asociadas, (crear con el Class Wizard), son:

Control Static Text

ID

Propiedades modificadas Caption: Apellido (20 caracteres). Caption: Nombre (20 caracteres). Caption: Edad.

Variable miembro asociada ninguna ninguna ninguna m_strApellido m_strNombre m_Edad ninguna ninguna ninguna ninguna

Tipo CString CString int -

El que trae por defecto El que trae por Static Text defecto El que trae por Static Text defecto Edit Box Edit Box Edit Box Button Button Button Button IDC_APELLIDO

Las propiedades por defecto. Las propiedades por IDC_NOMBRE defecto. Tildar: Number y Right IDC_EDAD aligned text Caption: Guardar datos y IDC_GUARDAR tildar la propiedad Default button IDC_VER Caption: Ver datos >> Caption: Aceptar y quitar IDOK el tilde a la propiedad Default button. IDC_LIMPIAR Caption: Limpiar

Recuerde que para poder crear las variables asociadas con el Class Wizard, debe especificar primero todos los ID de los controles. Lo que vamos a hacer es lo siguiente: En los cuadros de edicin el usuario va a escribir el apellido, el nombre y la edad, luego al pulsar el botn Guardar datos, (que como le asignamos la propiedad Default button esto ocurre con slo pulsar ENTER), se pasan las variables miembros asociadas a los controles a los campos de la estructura y se escribe en el archivo "datos.dat", (la extensin puede ser cualquiera). Pero atencin, si se ingresan nuevos datos el archivo no se vuelve a crear borrando el contenido existente sino que agregaremos los nuevos. Si se pulsa "Ver datos >>" podremos ir visualizando en los cuadros de edicin los campos de cada registro en cada pulsacin de este botn, hasta que se llegue al fin del archivo, cuando mostramos un mensaje aclaratorio y se visualizan nuevamente los datos del primer registro. El botn "limpiar" borra los contenidos de los cuadro de edicin en caso que se est visualizando un registro y se desee ingresar uno nuevo. Adems de estos controles vamos a necesitar definir en algn lugar la estructura Personas. Esto se podra hacer en el cdigo del botn Guardar datos y en el de Ver datos, pero no es el lugar ms prolijo, sino que se debe

definir la estructura en la declaracin de la clase del dilogo. A esta altura debe ya acostumbrarse a pensar qu cosas son tiles de definir en una clase y por lo general esto est asociado a la proteccin de la informacin y al mbito. Para definir la estructura en la clase del dilogo pulse dos veces en el Class View la entrada de la clase del dilogo: Se editar la declaracin de la clase del dilogo, (el archivo archivos2Dlg.h), all escriba lo que est marcado en amarillo: class CArchivos2Dlg : public CDialog { private: struct Persona { char Apellido[20]; char Nombre[20]; int edad; }; // Construction ..... } Otra cosa importante a tener en cuenta es que si vamos a recorrer el archivo para visualizar cada registro necesitaremos ir guardando la posicin del puntero del archivo en alguna variable. Para indicar que leer en un archivo ste utiliza un puntero que se debe desplazar tantos bytes como se desee, por eso en su momento necesitaremos una variable donde guardar esos desplazamientos. Tambin la declararemos en la clase del dilogo, pero seguiremos el mtodo tradicional, esto es, pulse con el derecho sobre la entrada CArchivos2Dlg en el Class View y seleccione Add Member Variable. Como Variable Type escriba unsigned long y como nombre de variable Pos. Pulse 2 veces sobre el constructor de la clase del dilogo, (CArchivos2Dlg::CArchivos2Dlg(CWnd* pParent =NULL), se editar el constructor, all le daremos un valor inicial a Pos. Como ltima lnea, antes de la llave que cierra, escriba Pos=0;. Con esto estamos listos para escribir el cdigo del botn "Guardar datos", pulse dos veces sobre el botn para generar OnGuardar() y all escriba: void CArchivos2Dlg::OnGuardar() { struct Persona p; (1) CFile f; int cr; CString nr;

if (MessageBox("Guarda estos datos?", "Guardar", MB_YESNO| MB_ICONQUESTION)==IDYES) { UpdateData(TRUE); strcpy(p.Apellido,m_strApellido); (2) strcpy(p.Nombre, m_strNombre); (3) p.edad =m_Edad; (4) f.Open ("datos.dat",CFile::modeCreate|CFile::modeNoTruncate| CFile::modeWrite); (5) f.SeekToEnd(); (6) f.Write (&p, sizeof(p)); (7) m_strApellido.Empty(); (8) m_strNombre.Empty(); (9) m_Edad=0; (10) UpdateData(FALSE); (11) AfxMessageBox("Datos guardados con xito"); CWnd* pApe=(CWnd*)GetDlgItem(IDC_APELLIDO) ; (12) pApe->SetFocus(); (13) cr=f.GetLength()/sizeof(p); (14) nr.Format ("Cantidad de registros: %i", cr); (15) SetWindowText(nr); (16) f.Close(); (17) } } Al principio, en (1), se declara una variable de tipo Personas, adems se contruye el objeto CFile, se declara una variable entera para almacenar la cantidad de registros, (algo extra que vamos a hacer) y una de tipo CString para poder mostrar en la barra de ttulo del dilogo la cantidad de registros del archivo. Se le pregunta al usuario si est seguro de querer guardar esa informacin y en caso afirmativo se pasan los contenidos de los cuadros de edicin a las variables asociadas con nuestra conocida UpdateData(TRUE). Una vez que las variables asociadas tienen los datos ingresados en (2), (3) y (4) se pasa esta informacin a los campos de la estructura p, (que es lo que en realidad vamos a grabar). En (5) se abre el archivo de manera tal que si no existe lo cree pero si ya existe que no lo trunque, o sea que no borre su contenido y que adems lo abrimos para escribir. Como vamos a agregar registros en caso que ya exista uno por lo menos, debemos mover el puntero al fin del archivo para que lo grabe a continuacin del ltimo, esto es lo que se hace con SeekToEnd() en (6). En (7) escribimos el registro en el archivo, los parmetros son exactamente los mismos que en el ejemplo anterior, primero la variable que contiene la informacin a escribir, ahora se trata de una estructura y luego el tamao de la

misma. Esto implica que cada registro del archivo que estamos creando es de sizeof(p) bytes de tamao. En (8), (9) y (10) vaciamos las variables asociadas, (las de tipo CString con su funcin Empty() y la entera igualando a 0) y en (11) llamamos a UpdateData(FALSE) para transferir estos nuevos contenidos de las variables a los controles, lo que hace que se limpien quedando listos para nuevos ingresos. Sera ideal que luego de guardar los datos el cursor quedara preparado de forma automtica en el cuadro de edicin del apellido para agilizar el ingreso, esto se logra "dandole el foco" a dicho control, es lo que se hace en las lneas (12) y (13). Como la funcin SetFocus() retorna un manejador de ventana, debera tratar al cuadro de edicin como una de ellas, por eso en (12) se obtiene un puntero a CWnd con GetDlgItem() para as en (13) hacer efectivo el traslado del foco. Y bueno, finalmente hay que obtener la cantidad de registros en el archivo para mostrar esa informacin en la barra de ttulo de la ventana. Si un registro es de sizeof(p) de tamao, haciendo tamao del archivo/sizeof(p) se obtiene cuantos registros hay. El tamao del archivo se obtiene con la funcin miembro de CFile, GetLenght(), en (14) se hace la cuenta almacenando el resultado en un entero, en (15) se formatea una cadena con ese valor y en (16) se establece el ttulo de la ventana. Cerramos el archivo en (17). Ahora escribiremos el cdigo del botn "Ver datos >>", pero pensemos antes como debera funcionar. Cuando se pulsa el botn "Ver..." podramos mostrar algo si existiera algn registro o lo que equivale a decir si no estamos en el fin de archivo. Como el botn se puede pulsar de forma contnua deberamos almacenar la posicin del puntero en el archivo e ir incrementandola para poder mostrar el siguiente en la prxima pulsacin. Por ejemplo si el puntero est en el primer registro su valor es 0, si quiero leer el segundo debera incrementarlo en sizeof(p) ya que el tamao de cada registro es ese y luego de leer el segundo si quiero leer el tercero otra vez hay que incrementar el puntero en sizeof(p) y as sucesivamente hasta llegar al fin del archivo que equivale a decir, cuando los sucesivos incrementos del puntero alcanzan el tamao del archivo. Por cada pulsacin se llama a alguna funcin que permita mover el puntero del archivo el desplazamiento que deseamos, se incrementa el puntero en sizeof(p) bytes para la siguiente pulsacin y se procede a la lectura del registro actual. Recuerde que declaramos una variable Pos para poder almacenar la posicin del puntero en el registro. Pulse dos veces en el botn "Ver..." para editar OnVer(), all escriba: void CArchivos2Dlg::OnVer() {

struct Persona p; CFile f; f.Open ("datos.dat", CFile::modeCreate|CFile::modeNoTruncate| CFile::modeRead); (1) if (pos<f.GetLength()) (2) { f.Seek(pos, CFile::begin); (3) //Ubico el puntero del archivo en el siguiente registro pos+=sizeof(p); (4) //incremento pos para el prximo registro f.Read (&p,sizeof(p)); //Leo un registro m_strApellido=p.Apellido; //paso los contenidos a las variables miembros m_strNombre=p.Nombre; m_Edad=p.edad; UpdateData(FALSE); //Actualizo los cuadros de edicin } else { //se lleg al fin del archivo AfxMessageBox("Fin de archivo.\nPulse nuevamente para ver el 1 registro"); pos=0; //ahora p es 0 para poder visulizar nuevamente el 1 registro } f.Close (); } Se abre el archivo para lectura (1) e inmediatamente se verifica si el contenido de Pos es menor que el tamao del archivo, (2), de ser as es porque an quedan registros que leer por eso en (3) ubico el puntero del archivo en el registro establecido por el contenido de Pos por medio de la funcin Seek(). Seek() recibe dos parmetros, el primero es la cantidad de bytes a desplazar el puntero y el segundo es desde dnde se realiza el desplazamiento, este segundo parmetro puede tomar los valores: CFile::begin : Desde el principio del archivo, CFile::current : desde la posicin actual y CFile::end : desde el fin del archivo. Si fuera la primera vez que se pulsa el botn, Pos tiene 0, entonces Seek(Pos, CFile::begin) desplaza el puntero 0 bytes desde el comienzo, lo que implica que no lo mueve. Luego de mover el puntero, en (4) se incrementa Pos en sizeof(p) bytes para poder en la siguiente pulsacin cuando ocurra la llamada a Seek() avanzar al siguiente registro. De aqui en ms se lee con Read() y se pasan los datos de la estructura a las variables asociadas a los controles. Si en la lnea (3) el contenido de Pos no era menor que el tamao del archivo es porque se alcanz el fin del mismo, entonces se muestra un mensaje aclaratorio y se pone Pos en 0 para poder mostrar, en caso que pulse //si no se lleg al fin del archivo

nuevamente el botn "Ver..", el primer registro, (como que peg toda una vuelta). Para terminar nos queda el cdigo del botn Limpiar, es muy fcil, slo hay que limpiar las variables asociadas y luego llamar a UpdateData(FALSE). Resumiendo: Para poder ir agregando registros a un archivo de estruturas es importante llamar a Open() con los modos CFile::modeCreate y CFile::modeNoTruncate, adems de que antes de escribir con Write() hay que mover el puntero del archivo al final del mismo.. La funcin Seek() permite mover el puntero en desplazamientos de acuerdo al tamao del registro. Si un registro es de 50 bytes y quiero ubicar el puntero en el tercer registro tendr que especificar Seek(100, CFile::begin) ya que el primer registro se encuentra en el desplazamiento 0, el segundo a los 50 bytes y el tercero a los 100 bytes de "distancia" del principio. Bases de Datos: Muchas de las aplicaciones comerciales que uno puede hacer involucran irremediablemente el uso de bases de datos. Lenguajes de programacin como Visual C++, Visual Basic, Delphi, Borland Build C++ 5.0, Visual Fox, etc. permiten crear atractivas aplicaciones de bases de datos. Tal vez Visual Fox y Visual Basic planteen el desarrollo ms sencillo y prctico que con cualquiera de los restantes lenguajes nombrados. Pero ms all de esto, las aplicaciones ms verstiles y de mejor rendimiento se escriben con Delphi y los C++ de Microsoft y Borland, por eso es inevitable no tocar el tema "bases de datos". Para abrir una base de datos existen diferentes formas de acuerdo al motor de bases de datos que se elija e incluso podra adems plantear una conexin ODBC a travs del Panel de Control. Motores de bases de datos: Los motores de bases de datos no son ni ms ni menos que libreras que ofrecen una serie de objetos con sus respectivos mtodos y propiedades que sirven como interface entre la aplicacin y la fuente de datos, (ejemplo una base de Access). Los ms comunes son: DAO: Data Access Object - Es el tradicional motor JET de microsoft. ADO: ActiveX Data Object - Es el nuevo modelo que utiliza tecnologa COM. RDO: Remote Data Object - Motor para el acceso a bases de datos remotas. A esto se le puede sumar la posibilidad de crear una conexin ODBC para una base de datos y abrirla de forma indirecta a travs de dicha conexin. Aplicacin Dialog Based con bases de datos: El Application Wizard permite crear un armazn de aplicacin con base de datos, siempre y cuando seleccionemos la opcin de aplicacin SDI. Considero que para comprender el uso de bases de datos este armazn no es el ms ptimo ya que adems hay que agregar explicacines sobre las diferentes vistas y las relaciones con el documento de la aplicacin. Por eso en este ejemplo vamos a crear una simple aplicacin basada en dilogo, (tan slo tendr uno), y escribiremos todo el cdigo necesario para poder abrir una base de datos y manipular los registros de diferentes tablas.

La aplicacin constar de un dilogo con diversos controles, un par de listas y algunos cuadros de edicin. Y usaremos estos controles para almacenar los contenidos de algunos campos. Cree una aplicacin Dialog Based de nombre Basedao y disee el dilogo como muestra la figura: Son dos controles listas y dos cuadros de edicin, (adems de los Statics para identificar cada control). A continuacin se muestra una tabla con las propiedades especificadas de cada control y adems las variables asociadas a crear:

Variable asociada CListBox CListBox IDC_LSTAUTORES por defecto m_lstAut CListBox CListBox IDC_LSTLIB_AUT por defecto m_lstISBN Multiline, Vertical Scroll, Border, CString CEdit IDC_TITULO Read Only m_strTitulo AutoHScroll, Border, Read CEdit IDC_FECHA int m_nFecha Only, Right Aligned Text. Control ID Propiedades Veamos ahora qu debe hacer la aplicacin. La base de datos que usaremos es Biblio.mdb que viene con el Visual Studio, (en particular se instala con Visual Basic). En primer lugar debemos abrir la base de datos de manera tal que se pueda acceder desde cualquier funcin del dilogo. Luego al cargar el dilogo, (OnInitDialog()), tomamos de una tabla, (creando un Recordset), todos los autores y cargamos la lista Autores. La base estar siempre abierta, por lo que, al seleccionar un autor de la lista Autores, podremos crear un nuevo Recordset con todos los registros que contengan un ISBN que pertezca al autor seleccionado y cargarlos en la lista ISBN. Y finalmente si se selecciona un ISBN se muestra la fecha de publicacin y el ttulo de la publicacin en sus respectivos controles de edicin. Es una aplicacin sencilla pero que muestra interesantes operaciones con los registros de diferentes tablas utilizando sencillas consultas SQL. Tal vz muchos se pregunten qu es un Recordset?, bueno simplemente se podra decir que un Recordset es un conjunto de registros de una o varias tablas, creado a apartir de una consulta SQL. Cmo abriremos una Base de datos?, cmo crearemos un Recordset?. Existen clases MFC que encapsulan mtodos para tratar bases de datos y recordsets. Como usaremos el motor DAO, utilizaremos las clases CDaoDatabase y CDaoRecordset, para lo cual en el archivo de implementacin

del dilogo agregaremos el archivo de cabecera correspondiente: #include "afxdao.h". Para que la base de datos sea "visible" en todo el dilogo definiremos una variable global de tipo CDaoDatabase; ser un puntero a la clase CDaoDatabase. Escriba en el archivo de implementacin del dilogo, antes de la definicin del constructor la lnea siguiente marcada en amarillo: // basedaoDlg.cpp : implementation file #include "stdafx.h" #include "basedao.h" #include "basedaoDlg.h" #include "afxdao.h" //Archivo de cabecera para usar DAO #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CDaoDatabase *pBD = new CDaoDatabase; //Puntero global para ver la base en todos los procedimientos ///////////////////////////////////////////////////////////////////////////// // CBasedaoDlg dialog CBasedaoDlg::CBasedaoDlg(CWnd* pParent /*=NULL*/) : CDialog(CBasedaoDlg::IDD, pParent) { .................... Para abrir la base de datos crearemos una funcin miembro de CBasedaoDlg. En Class View pulse con el botn derecho sobre la clase CBasedaoDlg y seleccione del men contextual la opcin Add Member Function. El nombre de la funcin ser AbrirBase y como tipo escriba void, pulse Aceptar y se editar la funcin. Alli escriba: void CBasedaoDlg::AbrirBase() { pBD->Open ("biblio.mdb"); } Se abre la base de datos con el mtodo Open(). Tiene varios parmetros optativos: virtual void Open( LPCTSTR lpszName, BOOL bExclusive = FALSE, BOOL bReadOnly = FALSE, LPCTSTR lpszConnect = _T("") ); El primer parmetro, (el nico que usamos en nuestra aplicacin), es el nombre de la base de datos includo el path. En nuestro caso como la base Biblio.mdb la colocamos en el Workspace del proyecto no hace falta especificarle el path. El siguiente parmetro indica el modo de apertura y ser TRUE en caso que se abra de modo exclusivo y FALSE para compartido, (omitir este parmetro implica modo compartido, o sea FALSE). El tercer parmetro es para especificar si se abrir la base de slo lectura, (TRUE) o lectura/escritura,

(FALSE), (tambin si se omite este parmetro se supone FALSE). Y el ltimo parmetro, si el primero es una cadena vaca "" entonces este ltimo ser el nombre de la conexin ODBC. _T() es una macro que permite portabilidad para UNICODE y ANSIS, (podra haber omitido sto y simplemente escribir la cadena). En lugar de tener CDaoDatabase pBD = new CDaoDatabase, se podra haber declarado una variable CDaoDatabase y un puntero a CDaoDatabase. Luego en AbrirBase() abriramos la base con la variable y luego le asignamos la direccin de memoria al puntero. Pero usando new es ms prolijo. Ahora hay que escribir la llamada a AbrirBase() en OnInitDialog(), as al cargar la aplicacin se abre la base. Simplemente escriba lo que est marcado en amarillo: BOOL CBasedaoDlg::OnInitDialog() { CDialog::OnInitDialog(); // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here AbrirBase(); //Llamo al procedimiento que abre la base de datos. return TRUE; } Una vez que se abre la base, inmediatamente se debera cargar la lista Autores, por eso como hizo con AbrirBase(), agregue una funcin miembro al dilogo de tipo void y de nombre CargaAutores() y escriba: //Procedimiento que carga la lista de autores void CBasedaoDlg::CargaAutores() { COleVariant v; (1) CString dato; (2) CDaoRecordset recAut(pBD); (3) try (4) { //Creo el recordset recAut.Open (dbOpenDynaset,"SELECT author FROM authors ORDER BY author", dbReadOnly); (5) recAut.MoveFirst (); (6) //Voy al primer registro while (!recAut.IsEOF ()) (7) //Mientras no sea fin de archivo { recAut.GetFieldValue ("author", v); (8) dato.Format ("%s%s", dato,v.bstrVal); (9) m_lstAut.AddString (dato); (10) dato.Empty (); (11)

recAut.MoveNext(); (12) } recAut.Close(); } catch( CDaoException* e ) (13) base el flujo de error {

//De existir un error al abrir o leer en la //pasa por ac y muestro mi mensaje

AfxMessageBox("Error de base de datos", MB_OK); e->Delete( ); (14) } } En (1) y en (2) se declaran variables, una COleVariant y otra CString. Los mtodos de la clase CDaoRecordset, (que usaremos en esta funcin), devuelven los contenidos de los campos de una tabla en variables de tipo Variant, encapsulado en la clase COleVariant. As que tendremos una variable de tipo COleVariant en la funcin para poder obtener el contenido del campo necesario, pero luego lo formatearemos a CString. Se pueden representar los tipos reales de los contenidos de los campos, una vez que ya se tiene la variable COleVariant con el valor de algn campo, por medio de algnos de los siguientes datos miembros de COleVariant: bstrVal = BSTR (una cadena). bVal = unsigned char iVal = short lVal = long fltVal = float dblVal = double Estos son slo algunos de los valores, mire la ayuda con referencia a COleVariant para ms informacin. En (3) se crea una instancia de la clase CDaoRacordset con la variable recAut, pasandole al constructor el puntero a la base de datos. Manejar recordsets puede implicar la posibilidad de algn error al por ejemplo querer cargarlo, as que usaremos una estructura try - catch para prevenirnos de que puede ocurrir algn error, (excepcin). Dentro del bloque try intentaremos cargar el recordset con los registros que querramos y llenaremos la lista, de ocurrir una excepcin el flujo saltar al bloque catch() donde podremos mostrar un mensaje aclaratorio sin que se cuelgue la aplicacin. Este manejo de los errores equivale en Visual Basic a la sentencia On Error Goto nombre-etiqueta. Por eso en (4) se plantea la estructura try. Bien, necesitamos un recordset con todos los nombres de los autores, los cuales se encuentran almacenados en el campo "author" de la tabla "authors", as que en (5) con el mtodo Open() en el segundo parmetro, a travs de una

cadna SQL le indicamos precisamente eso, "todos los autores de la tabla autores ordenado por autor". En el primer parmetro del mtodo Open() del objeto Recordset se indica qu tipo de recordset se abrir: Dynaset, Table o Snapshot, por medio de una constante. dbOpenDynaset: Permite abrir un recordset de tipo Dynaset, el cual consta de un conjunto de registros y es de escritura/lectura. dbOpenTable: Permite abrir un recordset de tipo Table, el cual alberga todos los registros de una tabla tradicional y es de lectura/escritura. dbOpenSnapshot: Permite abrir un recordset de tipo Snapshot, el cual consta de un conjunto de registros y es de slo lectura. Y el tercer parmetro es el modo de apertura, algunos de los posibles constantes son: dbAppendOnly Slo se pueden agregar registros. dbDenyWrite Otros usuarios no pueden modificar ni agregar registros. dbDenyRead Otros usuarios no pueden ver los registros, (slo tipo Table). dbReadOnly Slo lectura. Si mira la ayuda con respecto a Open() de la clase CDaoRecordset, ver que es una funcin sobrecargada as que se plantean muchas otras posibilidades de apertura. En este ejemplo slo se destaca un caso en particular. Luego que se carga el recordset, por las dudas muevo el puntero de la tabla al primer registro por medio de la funcin MoveFirst() en (6) y en (7) se verifica que no sea fin de archivo con CDaoRecordset::IsEof(). En caso que no sea fin de archivo obtengo el contenido del campo "author" del registro actual por medio de la funcin CDaoRecordset::GetFieldValue() (8), cuyos parmetros son, el nombre del campo entre comillas y la variable de tipo COleVariant donde se almacenar el contenido. Este valor almacenado en la variable v es de tipo Variant por lo tanto no nos sirve, pero si v.bstrVal que nos retorna lo mismo pero de tipo cadena, el cual en (9) formateamos a CString para almacenarlo en la variable dato. En (10) agregamos el contenido de la variable dato, (el autor que acaba de ser ledo con GetFieldValue()), a la lista, en (11) limpiamos esta variable y en (12) nos movemos al siguiente registro con CDaoRecordset::MoveNext(), para as verificar nuevamente el fin de archivo. Si en algn momento ocurre una excepcin el flujo del programa "salta" a la esctructura catch(), en (13) dnde se especifica qu tipo de excepcin se va a manejar, en este caso ser CDaoException el puntero que se declara. Se muestra un mensaje, (que en realidad debera ir de acuerdo al error puntual de tipo CDaoException que ocurri) y se elimina el objeto. Antes de probar esto que escribimos, tenemos que llamar a esta funcin desde algn lugar, OnInitDialog() es el sitio indicado, as que luego de la llamada a AbrirBase() escriba: CargarAutores(); Pruebe la aplicacin y ver que al presentar el dilogo ya tiene cargada la lista de autores. Tenemos que escribir cdigo para que al seleccionar un autor de la lista se carguen todos los ISBN del mismo en la otra lista. Cuando se selecciona un elemento de una lista se "dispara" el mensaje OnSelChange(), as que vaya a

Class Wizard y seleccionada la lista de Autores, agregue el mensaje LBN_SELCHANGE y pulse Edit Code, escriba: void CBasedaoDlg::OnSelchangeLstautores() { CString autor; COleVariant v,v2; (1) CString dato, strISBN, strCR; char Consulta[100]; (2) CDaoRecordset recBook(pBD); (3) CDaoRecordset recAutor(pBD); (4) m_lstAut.GetText (m_lstAut.GetCurSel (),autor); (5) try { recAutor.Open (dbOpenDynaset, "SELECT au_id FROM WHERE author = '" + autor +"'", dbReadOnly); recAutor.GetFieldValue ("au_id", v); (7) dato.Format ("%s%ld",dato,v.lVal); (8) authors (6)

//Formateo una cadena para la consulta SQL sprintf(Consulta,"SELECT isbn FROM title_author WHERE au_id = %ld", v.lVal ); (9) recBook.Open (dbOpenDynaset,Consulta, dbReadOnly); (10) m_lstISBN.ResetContent (); (11) recBook.MoveFirst (); while(!recBook.IsEOF ()) (12) { recBook.GetFieldValue ("isbn", v2); (13) strISBN.Format ("%s%s",strISBN,v2.bstrVal ); m_lstISBN.AddString (strISBN); strISBN.Empty (); recBook.MoveNext (); } recBook.Close(); recAutor.Close(); } catch( CDaoException* e ) { e->Delete( ); } } Qu debe ocurrir al seleccionar un autor?. Bueno, los ISBN de ese autor se encuentran en la tabla "title_author" y el campo que nos permite relacionar esta tabla con la tabla de autores es "au_id", un nmero que identifica al autor. Al seleccionar un autor de la lista no tenemos el id del autor, (o sea el campo "au_id"), sino que tenemos el nombre del mismo, entonces tendrmos primero

que ubicar el autor en la tabla "author", tomar el contenido de "au_id" y luego con este campo tomar todos los ISBN de la tabla "title_author" entonces s volcarlos a la lista. Nos hubieramos ahorrado el paso de ubicar el autor en la tabla "author" para obtener su id si al momento de cargar la lista de autores tambin hubieramos tomado los id's y los hubieramos guardado en una lista dinmica global. Pero bueno, la idea de este ejemplo es manipular tablas y este caso nos viene muy bien para afianzar el uso de Recordset. Como por un lado voy a obtener el valor de "au_id" en la tabla "author" y el valor del campo "isbn" de la tabla "title_author" declaro en (1) dos variables objeto COleVariant y dos objetos CDaoRecordset en (3) y (4). En (2) declaro una cadena de 100 caracteres para almacenar la consulta SQL que formulo. Guardo, en (5), en la variable autor, el autor seleccionado de la lista y en (6) cargo el recordset recAutor solamente con el campo "aut_id" del autor seleccionado, (eso es lo que devuelve la consulta SQL especificada). Obtengo en (7) el contenido con CDaoRecordset::GetFieldValue() y le doy en (8) formato de string guardndolo en Dato. En (9) preparo la variable char Consulta con la cadena SQL necesaria para que me devuelva todos los isbn cuyo campo "au_id" se igual al contenido de Dato. Y en (10) cargo el recordset recBook con el resultado de esa consulta, (que puede ser ningn registro). Limpio la lista en (11) y evalo en (12) que mientras no sea fin de archivo voy obteniendo los contenidos del campo "isbn" de cada registro, (13), y lo voy agregando a la lista. Listo, si prueba la aplicacin podr apreciar que ahora al seleccionar un autor de la lista se cargan en la otra todos los isbn de sus libros publicados. Slo nos que al seleccionar un isbn se muestre en los cuadros de edicin, la fecha de publicacin y el ttulo del libro. Vaya a Class Wizard y agregue el mensaje LBN_SELCHANGE para la lista de ISBN, (IDC_LSTLIB_AUT). Edite la funcin y escriba: void CBasedaoDlg::OnSelchangeLstlibAut() { CString strISBN, strTitu, strFecha; COleVariant v; CString Consulta; CDaoRecordset recTitu(pBD); m_lstISBN.GetText (m_lstISBN.GetCurSel (),strISBN); try { Consulta="SELECT * FROM titles WHERE isbn = '" +strISBN +"'"; recTitu.Open (dbOpenDynaset, Consulta, dbReadOnly); (1) recTitu.GetFieldValue ("title",v); (2) strTitu.Format ("%s%s", strTitu,v.bstrVal); m_txtTitulo=strTitu; recTitu.GetFieldValue("yearpublished", v); (3) m_nFecha=v.iVal; (4) UpdateData(FALSE); (5) m_txtTitulo.Empty ();

recTitu.Close(); } catch( CDaoException* e ) { e->Delete( ); } } Esta funcin es mucho ms sencilla que las anteriores, simplemente formulo una consulta SQL para obtener todos los campos del registro de la tabla "titles", (dnde se encuentran el ttulo de la publicacin y la fecha), cuyo campo "isbn" coincida con el seleccionado en la lista y cargo un recordset con esta informacin, (1). Obtengo primero el contenido del campo "title", (2), lo formateo y se lo asigno a la variable asociada al cuadro de edicin. Luego en (3) obtengo el contenido del campo "yearpublished", (verifique en la tabla "titles" que el campo se llame as, sino modifique el nombre del campo directamente en la tabla) y directamente se lo asigno a la variable asociada m_nFecha, (note que no se formatea el valor Variant sino que directamente utilizo la propiedad iVal) y actualizo los cuadros de edicin con UpdateData(FALSE). Nos queda slo una cosa ms. Debemos cerrar la base y eliminar la memoria reservada para pBD, (el puntero a la clase CDaoDatabase), al momento de "destruirse" la aplicacin. Para esto agregue con el Class Wizard el mensaje WM_DESTROY a la clase del dilogo y alli escriba: void CBasedaoDlg::OnDestroy() { CDialog::OnDestroy(); pBD->Close(); delete pBD; } Toda referencia creada con new debe ser eliminada con delete. La aplicacin est terminada, pruebela y debera obtener una salida similar a la siguiente:

Para que el aspecto del dilogo coincida con el de la imagen deber "retocar" algunas propiedades del mismo. Investigue por su cuenta. Este ha sido nuestro primer ejemplo de aplicacin con base de datos. Me parece que usar una aplicacin Dialog Based es muy til ya que toda informacin sobre cmo utilizar bases de datos en una aplicacin Dialog

Based, sea en la ayuda de MSDN, en Internet y/o en libros especializado es nula. Toda la informacin que he encontrado estaba orientada a aplicaciones SDI basadas en el armazn que App. Wizard genera y para comprender los rudimentos bsicos del trabajo con bases me parece que no es prctico. No obstante en prximos captulos, (no necesariamente el siguiente), crearemos aplicaciones con bases de datos utilizando aplicaciones SDI pero no sin antes afianzar ms ciertos conceptos de la arquitectura DOCUMENTOVISTA. Resumiendo: Desde un dilogo, si se desea utilizar bases de datos con el motor DAO es imprecindible agregar el archivo de cabecera afxdao.h en el archivo de implementacin. En una aplicacin Dialog Based, se crea una objeto CDaoDatabase en el montn, utilizando el operador new. Depender de dnde se lo cree si la base ser vista por todos los mensaje del dilogo y sus controles o no. Se abre una base de datos DAO con el mtodo Open() de la clase CDaoDatabase pasandole principalmente el path al archivo .mdb y luego otros parmetros que indican el modo de apertura entre otras cosas. Teniendo abierta una base, se puede primero crear una objeto recordset pasandole como parmetro al constructor el puntero a la base y luego cargarlo con un conjunto de registros de una o ms tablas usando la funcin Open() de la clase CDaoRecordset. Cuando se utiliza la funcin CDaoRecordset::GetFieldValue() para obtener el contenido de un campo de un registro, este es de tipo Variant; entonces para poder utilizarlo con el tipo equivalente de la definicin de la tabla se utiliza la clase COleVariant para almacenar el valor y luego se accede a la propiedad de tipo de acuerdo al formato original del contenido. Esto es, por ejemplo, si se lee un campo originalmente ENTERO, en principio se almacena en un objeto de tipo COleVariant y luego se obtiene el valor tal como est en el campo del registro con la propiedad iVal. ACTIVEX DATA OBJECT (ADO): En principio se podra decir que ADO es el sucesor de DAO y RDO. Es la nueva interfaz orientada a objetos para el acceso a bases de datos. Como ADO encapsula la funcionalidad, (aunque no toda, por cierto), de DAO y RDO, se est convirtiendo en un estndar en la programacin de bases de datos. No obstante el mismo Microsoft aconseja el uso de DAO para aplicaciones con bases de datos locales y recomienda a aquellos que usan RDO para conexiones con bases de datos de SQL Server y de Oracle, que utilicen ADO. ADO funciona a travs de un servidor OLE DB, o sea, es una interfaz entre el servidor OLE DB de los datos, (ejemplo Access, SQL Server, Oracle, etc.), y la aplicacin. ADO es un componente COM, (Component Object Model), que encapsula diversos objetos tiles al momento de manipular grandes bases de datos. (Se recomienda la lectura de artculos de MSDN sobre la arquitectura COM.).

Dificultades al usar ADO con C++: En Visual Basic o en Java es muy transparente el uso de ADO y hasta dira simple. En cambio en Visual C++ no existe una clase que encapsule la funcionalidad de la librera de ADO, (por lo menos hasta donde yo s, an no he visto el Visual C++ .NET), lo que implica que la aplicacin se debe "comunicar" directamente con el componente COM, (aunque puede usar la clase que escribi Carlos Antollini cuyos fuentes y documentacin puede encontrar en la seccin de artculos). Pero salvado ese obstculo, an se encuentran contratiempos al momento de manipular los datos que al igual que DAO son devueltos por la funciones como valores de tipo VARIANT, (COleVariant o _variant_t). Adems las cadenas que esperan ciertas funciones deben de tipo _bstr_t. ADO en una aplicacin Dialog Based: Bien, vamos a hacer una aplicacin Dialog Based sencilla que nos permita ver como se usa ADO en Visual C++, (lo ms importante, la conexin a la base y creacin de un recordset). Para esto vamos a usar una base de datos de Access(*) llamada "base_empleados.mdb" que consta de una tabla "Empleados" con los campos "Apellido", "Nombre", "Salario" e "ID".. Cree una aplicacin Dialog Based de nombre "ado_emp", (en realidad puede poner el nombre que quiera) y disee el dilogo ms o menos como se ve en la siguiente imagen:

(*)la base puede ser creada con cualquier versin de Access ya que todas soportan ADO, no pasa lo mismo con DAO, que a apartir de Access 2000 no es reconocido, (salvo que se guarde la base de datos como una versin anterior). Especifique las siguientes propiedades y cree variables asociadas a los controles de acuerdo a lo indicado en la siguiente tabla:

Control CListBox CEdit CEdit CEdit CButton CButton

ID IDC_LISTAPE IDC_NOMBRE IDC_SALARIO IDC_ID IDC_INFO IDC_SALIR

Propiedades por defecto Read Only Read Only Read Only por defecto por defecto

Variable asociada CListBox m_lstApe CString m_Nombre CString m_Salario CString m_ID ninguna. ninguna.

La idea es que cuando se cargue el dilogo, (OnInitDialog()), deberemos llenar la lista con los apellidos de la tabla, luego al seleccionar un apellido de la lista mostrar los restantes campos en los cuadros de edicin, (muy parecido a lo hecho en el captulo anterior con DAO). Importando la librera de ADO: Para trabajar con ADO hay que importar el archivo DLL correspondiente: msado15.dll con la directiva #import, (en realidad existen tres formas de acceder a ADO, ver Knowledge Base de MSDN Q174565). Un buen lugar para ubicar esta sentencia es en el archivo de cabecera del dilogo,ado_empDlg.h, all luego de la lnea #endif escriba: #import "c:\Archivos de programa\Archivos comunes\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF") El path puede variar si su versin de Windows est en ingls, (ni que decir si esta en islands o mandarn... en fin : ). En esta sentencia, adems de indicar la importacin de funciones de esa librera, estamos renombrando la propiedad EOF por adoEOF ya que de lo contrario el compilador nos arrojara errores. Inicializacin de OLE: Es necesario inicializar OLE antes de establecer la conexin, (por que se trata de objetos COM), para eso usaremos la funcin CoInitialise() pasndole como parmetro NULL. Escriba en el constructor del dilogo: CoInitialize(NULL); Conectando la base de datos: Con ADO en lugar de abrir directamente la base de datos, se realiza primero una conexin, para eso agregue una variable al dilogo, (en la clase del dilogo), de tipo _ConnectionPtr. _ConnectionPtr pAdoCone; Este es un objeto de tipo Connection y nos permitir conectar nuestra base de datos con la aplicacin.

Agregue al dilogo una funcin que retorne int de nombre Conectar y escriba all: int CAdo_empDlg::Conectar() { //Objeto _bstr_t con la cadena para la conexin. _bstr_t bstrCone(L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=base_empleados.mdb;"); (1) try { //Se crea una instancia para la conexin pAdoCone.CreateInstance (__uuidof(Connection)); (2) //se establece la conexin pAdoCone->Open (bstrCone ,_bstr_t ( "" ), _bstr_t ( "" ), adModeUnknown ); (3) return 1; } catch (_com_error& ce) (4) { //si ocurri una excepcin, se llama a la funcin que mostrar el mensaje de error ver_com_error(ce); (5) return 0; } } En (1) se declarar una cadena de tipo _bstr_t, la esperada por las funciones de ADO, cuyo fin es especificar informacin como Provider que en nuestro ejemplo es Microsoft.Jet.OLEDB.4.0 ya que pretendemos usar una base de Access, (si fuera una base de datos de SQL Server deberamos escribir Provider=SQLOLEDB), y Data Source que hace mencin a la fuente de datos, nuestro archivo. Luego, dentro de una estructura try-catch, (altamente recomendada), en (2) creamos una instancia de la conexin, (quienes ya han usado ADO en Visual Basic o ASP esta lnea equivale a Set pAdoCone = New Connection) y luego abrimos la conexin en (3) con la funcin Open() cuyo formato es: Open(BSTR ConnectionString, BSTR UserID, BSTR Password, long Options) ConnectioString: La cadena para la conexin que armamos en (1). UserID y Password: El ID del usuario y el password. En nuestro ejemplo ejemplo pasamos cadenas nulas siempre convirtiendola al tipo esperado, _bstr_t. Options: Una constante que especifica un modo de conexin: Constante Descripcin

adModeUnknown

adModeRead adModeWrite adModeReadWrite adModeShareDenyRead adModeShareDenyWrite adModeShareExclusive adModeShareDenyNone

Predeterminada. Indica que los permisos no se han establecido an o que no se pueden determinar. Indica que son permisos de slo lectura. Indica que son permisos de slo escritura. Indica que son permisos de lectura/escritura. Impide que otros abran una conexin con permisos de lectura. Impide que otros abran una conexin con permisos de escritura. Impide que otros abran una conexin. Impide que otros abran una conexin con cualquier tipo de permiso.

Ahora, bien, de no existir ningn error, (excepcin), la funcin, (nuestra funcin Conectar), retorna 1. Si ocurre una excepcin el flujo del programa salta a catch (4) donde lo que se verifica es un error de COM, _com_error. Los datos de la excepcin se almacenarn en la variable ce de tipo _com_error, la cual manipulamos en una funcin que deberemos crear llamada ver_com_error. (5) Entonces agregue al dilogo la funcin ver_com_error(_com_error &e) con retorno void y escriba en ella: void CAdo_empDlg::ver_com_error(_com_error &e) { CString msgErr; //aqu se almacenar el mensaje de error _bstr_t bstrOrigen= e.Source(); //origen del error _bstr_t bstrDescri=e.Description(); //texto del error msgErr.Format ("ADO - COM Error\n\tCdigo = %08lx\n\tOrigen = %s\n\tDescripcin = %s\n", e.Error(),(LPCSTR)bstrOrigen, (LPCSTR)bstrDescri); AfxMessageBox( msgErr, MB_OK | MB_ICONERROR ); } El objeto _com_error e trae informacin sobre la excepcin ocurrida. La funcin Source() devuelve una cadena que indica el origen del error y Description() una breve descripcin del mismo. Se formatea un objeto CString con estos datos ms el cdigo de error, funcin Error(), que luego se muestra con AfxMessageBox(). Ahora, coloque en OnInitDialog() la llamada a Conectar() de la siguiente forma: //Llamo a la funcin conectar if (!Conectar()) { AfxMessageBox("No se pudo realizar la conexin."); EndDialog(0); }

El siguiente paso es escribir una funcin que cargue la lista de apellidos con todos los apellidos de la tabla, (en este ejemplo se supone que no se repite ninguno). Entonces agregue otra funcin al dilogo, que retorne int, llamada CargaLista() y escriba: int CAdo_empDlg::CargaLista() { CString msgErr; try{ //Se crea una instancia del recordset _RecordsetPtr Rs(__uuidof(Recordset)); (1) (ver nota) //se le asocia la conexin Rs->PutRefActiveConnection(pAdoCone); (2) //armo una cadena de tipo _bstr_t con la sentencia SQL necesaria _bstr_t bstrSql(L"Select apellido from Empleados Order By apellido"); (3) //abro el recordset Rs>Open(bstrSql,vtMissing,adOpenForwardOnly,adLockReadOnly,adCmdText); (4) CString strApe; while(!Rs->adoEOF) //mientras no sea fin de archivo... { //obtengo el valor del campo apellido strApe = (char*) (_bstr_t) Rs->Fields->GetItem("Apellido")->Value; (5) //lo agrego a la lista m_lstApe.AddString (strApe); //paso al siguiente registro Rs->MoveNext(); } //cierro y libero el recordset Rs->Close(); Rs.Release(); return 1; } catch (_com_error& ce) { ver_com_error(ce); return 0; } } Como debe ser, las sentencias de ADO deben estar controladas en una estructura try-catch(_com_error), y entonces podemos en (1) crear una instancia del objetos recordset, _RecordsetPtr, (en VB equivale a Set Rs = new Recordset) y en (2) le indicamos que pertenece a nuestra conexin pAdoCone. En (3) armamos la cadena SQL que tomar los datos requeridos, o sea todos los apellidos ordenados alfabticamente y en (4) abrimos el recordset.

Luego, mientras no sea fin de archivo, tomamos en (5) el contenido del campo Apellido convirtindolo, primero en _bstr_t y despus en char*. Siempre que un campo sea de tipo cadena debe aplicar estas dos conversiones. En caso de encontrar un error en ejecucin, el flujo "salta" a la seccin catch, la cual llama a la funcin ver_com_error() que se encarga de mostrar un mensaje aclaratorio. Nota: En (1) puede ver la palabra clave _uuidof() que se encarga de devolver el GUID, (Globally Unique IDentifier), de una expresin, en este caso el objeto Recordset. En pocas palabras, el compilador le asocia un GUID a una clase declarada o definida con el atributo uuid. __declspec( uuid(ComObjectGUID) ) declarator El atributo uuid recibe una cadena como parmetro, la cual se trata de un GUID. Por ejemplo: struct __declspec(uuid("00000000-0000-0000-c000-000000000046")) IUnknown; struct __declspec(uuid("{00020400-0000-0000-c000-000000000046}")) IDispatch; Esto se aplica en particular a los objetos COM para que el sistema pueda suministrar las definiciones de interfaces como IUnknown. (ver tema sobre objetos COM en MSDN). Si prueba el programa, ver como se carga la lista con los apellidos. Nos falta escribir cdigo para que cuando se seleccione un apellido de la lista, se muestren los restantes campos en los cuadros de edicin. Para sto agregue el mensaje OnSelChangeListApe(), (recuerde que sto se hace por medio del Class Wizard). En este mensaje escriba: void CAdo_empDlg::OnSelchangeListape() { CString apeSel; ' char strSql[255] = "Select * from Empleados where apellido= ' "; //tomo el apellido seleccionado m_lstApe.GetText(m_lstApe.GetCurSel(),apeSel); //le coloco una comilla al final apeSel.Insert (apeSel.GetLength (), "'"); //entre comillas dobles hay una ' //termino de armar la cadena SQL strcat(strSql, apeSel); try{ //declaro el objeto recordset _RecordsetPtr Rs(__uuidof(Recordset)); //lo asocio a la conexin activa //Ac almaceno el nombre seleccionado

//cadena sql sin completar. Falta concatenarle el apellido seleccionado entre '

Rs->PutRefActiveConnection(pAdoCone); _bstr_t bstrSql(strSql); //ahora la cadena SQL es de tipo _bstr_t //abro el recordset Rs>Open(bstrSql,vtMissing,adOpenForwardOnly,adLockReadOnly,adCmdText); //tomo el nombre m_Nombre = (char*) (_bstr_t) Rs->Fields->GetItem("Nombre")->Value; //v es una variable variant, (podra haber sido un objeto COleVariant), //para el contenido de los campos. _variant_t v; v=Rs->Fields->GetItem("Salario")->Value; //obtengo el salario m_Salario.Format ("%.2f",v.fltVal); //lo coloco en el edit v=Rs->Fields->GetItem("ID")->Value; //obtengo el ID m_ID.Format("%i", v.intVal); //lo coloco en el edit //actualizo los controles UpdateData(FALSE); Rs->Close(); Rs.Release(); } catch (_com_error& ce) { ver_com_error(ce); //llamo a la funcin que se encarga de mostrar el error } } Bueno, este cdigo est exhaustivamente comentado, lnea X lnea, as que sera redundante explicar todo otra vez, simplemente es importante remarcar la diferencia que hay al obtener el contenido de un campo de tipo cadena y uno numrico. Para los numricos se usa una variable de tipo _variant_t, o un objeto COleVariant como en el captulo XIII, (tambin lo puede hacer para las cadenas), y luego se obtiene el valor por medio de la propiedad que identifica el tipo en concreto, por ejemplo fltVal si es float o intVal si es entero, etc. Por ltimo escribiremos el cdigo que permita, al pulsar el botn que dice "Ver connection string", mostrar un mensaje con el contenido por defecto de la cadena de conexin usada para conectar la base. Hay ms parmetros que los que especificamos al momento de conectar la base, con este botn podremos ver el contenido por defecto de los restantes. Escriba entonces: void CAdo_empDlg::OnInfo() { //Muestro un mensaje con la cadena completa de la conexin MessageBox(pAdoCone->GetConnectionString() ,"Informacin de la conexin", MB_ICONINFORMATION); }

Simplemente se hace uso de la funcin GetConnectionString() del objeto Connection.

Resumiendo: ADO es un conjunto de objetos escritos segn la arquitectura COM almacenados en una librera. Para poder usar ADO hay que importar dicha librera: #import "c:\Archivos de programa\Archivos comunes\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF") En ADO se crean conexiones a bases de datos o fuentes de datos ODBC. Por esto hay que crear un objeto de tipo _ConnectionPtr que luego es utilizado para abrir la conexin con la funcin Open() del mismo objeto, que recibe 3 parmetros: La cadena de conexin, el ID del usuario y opciones de conexin. Una vez creada la conexin, se puede en cualquier momento obtener informacin de las tablas almacenadas en la base, por medio de un objeto Recordset o Command, (este ltimo no ha sido visto en este captulo). Es necesario crear un objeto de tipo _RecordsetPtr y luego usar la funcin Open() del mismo para obtener los datos. Todos los campos son devueltos por funciones del objeto RecordsetPtr como tipo _variant_t. Siempre hay que escribir las sentencias de ADO entre estructuras TRY-CATCH que controlen la excepcin _com_error.

Você também pode gostar