Serie ficheros virtuales 

 

 

C Ficheros Virtuales




A.II.3  Multiagenda telefónica

 
A.II.3.1 Presentación
 
A.II.3.2 Interfaces
 
A.II.3.3 Construcción detallada de un interfaz “dialog-based” simple
 
A.II.3.4 Código asociado

 
A.II.3.5 La implementación de la lista de agendas con vFile

   
A.II.3.5.1 La estructura de soporte
   
A.II.3.5.2 La carga desde el fichero de respaldo
   
A.II.3.5.3 La edición de la lista de agendas
   
A.II.3.5.4 La descarga al fichero de respaldo


 
A.II.3.6 La implementación de una agenda individual con vFile

   
A.II.3.6.1 La estructura de soporte
   
A.II.3.6.2 La carga desde la vista principal y desde el lógico


 

                                                       _______

 
A.II.3.1 Presentación

 

Las agendas son un buen ejemplo para utilizar una programación orientada a objetos puesto que es muy fácil asociar objetos físicos y lógicos.

 

Así, partiremos de una multiagenda consistente en una lista de agendas, cada una de las cuales puede tener tamaños diferentes para poder dedicarse por ejemplo a listas de teléfonos completos o a listas reducidas relativas a extensiones de teléfonos.

 

Se trata de la implementación del ejemplo más sencillo de la serie que se presenta en esta parte del blog.



                                                       _______

 

A.II.3.2 Interfaces

 

La implementación consta de un interfaz de entrada que consiste en un simple botón de arranque del mantenimiento/selección de la lista de agendas, que a su vez invoca a un segundo interfaz de mantenimiento de detalle de la agenda seleccionada.

 

Gráficamente, se presenta entonces inicialmente el botón de arranque del mantenimiento/selección de la lista de agendas:


 

 

(La construcción de este sencillo interfaz, exhaustivamente detallada paso a paso más adelante, es la que se anticipaba en la introducción de la zona dedicada a ejemplos)

 

Al pulsar el botón de seleccionar agenda se invoca la pantalla de selección de agenda individual:

 

 


Al seleccionar la edición de la agenda EXTENPRUEBA se presenta la pantalla de mantenimiento de los registros de detalle, que puede pedirse reordenada:

 

 

 El sencillo ejemplo que se presenta podría extenderse hasta llegar a desarrollar una aplicación al uso de mantenimiento de bases de datos físicas respaldadas por homologas virtuales.

 

 Sin embargo no es una vía en la que el producto profundice, pues es un campo en que existen desarrollos muy poderosos.

 

 Otra cosa es la problemática virtual a la que está dedicada el libro, precisamente la falta de oferta frente a un problema que surge en el desarrollo práctico es lo que ha llevado a implementar esta herramienta, ciertamente auxiliar, pero que deviene imprescindible al resultar práctica, cómoda y fiable.



                                                       _______



 

En el blog hermano VBA Ficheros virtuales se hace un desarrollo de los ficheros virtuales en VBA como complemento excell y se dedica también una entrada a una multiagenda sencilla en el capítulo dedicado a lógicos. En esta visión alternativa de un libro excell soporte de la multiagenda, se navega simplemente seleccionando cada hoja del mismo que corresponde a un listín concreto con el siguiente aspecto

 



y en donde las sentencias núcleo vienen dadas por instrucciones similares a las que se utilizan en la solución actual. Así por ejemplo, cada fichero virtual asociado a una hoja y su vista lógica se crean con las sentencias

. . .

' Crea los ficheros virtuales de soporte si procede

lResul = W_NID(sHoja)
If lResul = 0 Then
                                                 
                                                  '      30/10    | 30       10
    lResul = W_NEW(sHoja, 30, 40)                 ' Base: Nombre  | Nombre + Nº
    lResul = W_CRTLF(sHoja & "01", sHoja, 31, 10) ' Lf01: Nº      | Nombre + Nº


    ' Restaura ficheros desde el contenido actual de la hoja

    RSTF

End If

. . .
                                                       _______

 

 

A.II.3.3 Construcción detallada de un interfaz “dialog-based” simple

 

Centrémonos ahora la construcción detallada del sencillo interfaz de entrada.

 

 

Comenzamos seleccionando en el menú de archivo “New

 

 


A continuación seleccionamos la ficha Workspace del menú emergente “NEW”

 



 


En donde especificaríamos el nombre del nuevo espacio de trabajo, Vagen, y su localización, por ejemplo F:\Vagen

 

 

Una vez creado el workspace, se añade el proyecto base a desarrollar.

 

Para ello se acede al menú de archivo y se selecciona “NEW” eligiendo la ficha correspondiente a MFC AppWizard (Exe)

 



indicando que se desea agregar al workspace en curso.

 

Así, estaríamos indicando que se desea crear un nuevo proyecto Vagen en la localización F:\Vagen\Vagen

 

 

En la siguiente ficha se elige el tipo de aplicación a crear. En nuestro caso sería “Dialog Based

 


Confirmamos con next y finish en las siguientes pantallas



 

 

 

 

 

Y obtenemos como resultado la siguiente proforma

 



Que ajustamos hasta dejar como se muestra:

 

 

En donde desactivamos el botón de aceptar original pues no nos interesa aceptar y terminar, sino aceptar, procesar y esperar una nueva petición.

Para ello hay que introducir un botón nuevo “Seleccionar Agenda” que controle el proceso.



                                                       _______

 

A.II.3.4 Código asociado

 

La codificación específica resultante no es muy larga, ocupa bastante más la incorporada automáticamente por MsC++.

 

Se incluye a continuación expurgada precisamente de los añadidos adicionales:

 

 


   // VagenDlg.cpp : implementation file
   //

   #include "stdafx.h"   // Este es el include común de MsC++
   #include "Vagen.h"
   #include "VagenDlg.h"


   #ifdef _DEBUG
   #define new DEBUG_NEW
   #undef THIS_FILE
   static char THIS_FILE[] = __FILE__;
   #endif


   /////////////////////////////////////////////////////////////////////////////

   // Se expurga la codificación añadida por MsC++


   /////////////////////////////////////////////////////////////////////////////
   // CVagenDlg message handlers

   // La siguiente rutina es importante, en ella se incluirían las tareas iniciales
   // a incluir en el módulo.

   // Consta de una 1ª parte suministrada por MsC++ y luego vendría la zona a
   // utilizar por el desarrollador (Que aquí no se utiliza

   BOOL CVagenDlg::OnInitDialog()
   {
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
     CString strAboutMenu;
     strAboutMenu.LoadString(IDS_ABOUTBOX);
     if (!strAboutMenu.IsEmpty())
     {
      pSysMenu->AppendMenu(MF_SEPARATOR);
      pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
     }
    }

    // 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



    // ...


    // (Zona de codificación para el desarrollador [Aquí no utilizada])


 
    return TRUE;  // return TRUE  unless you set the focus to a control
   }



   // Se eliminan aquí otras rutinas agregadas por MsC++



   // Y ahora comienzan realmente las rutinas específicas del módulo,
   // teniendo en cuenta que MsC++ proporciona codificación para un botón
   // OK que en el ejemplo se deshabilita para poder ejecutar un bucle de proceso
   // que terminaría pulsando el botón cancelar

   void CVagenDlg::OnOK()
   {
    int iresul = 0; // Control de invocaciones


    // TODO: Add extra validation here


       // Control inhibido. Sustituido por Button1 para evitar fin prematuro.

 
    CDialog::OnOK();
   }


   // Botón OK alternativo

   void CVagenDlg::OnButton1()
   {
    int iResul = 0; // Control de invocaciones


    // Invoca al seleccionador de agendas utilizando ejecución de aplicación   
    // windows (Versión explotación)

    iResul = WinExec("vagendas", SW_SHOWNORMAL);

    if (iResul > 31) return;


    // Invoca al seleccionador de agendas utilizando ejecución directa d.o.s.
    // (Versión desarrollo)

    iResul = system("vagendas");

    return;
   }


 

// No hay codificación adicional para el botón de cancelar, se acepta el comportamiento por defecto de la clase, por // ello no aparece una rutina asociada al mismo. En otros ejemplos sí se implementará.

 

                                                       _______

 

 

A.II.3.5 La implementación de la lista de agendas con vFile

 

Una vez pulsado el botón de selección se presenta la lista de agendas para su edición. Veamos su codificación extractada

 

                                                       _______

 

A.II.3.5.1 La estructura de soporte

 

La lista de agendas se apoya en el siguiente diseño que se manejará como fichero virtual en el programa y que se lee y graba en respaldo físico al comenzar y finalizar el mismo

 


  // Estructura de claves del fichero agenda.dat

  struct sDsK {char cNombreAgenda[12];}; // Nombre de la agenda


  // Estructura de datos del fichero agenda.dat

  struct sDsD
  {
   short int iLongClav; // Longitud de la clave de la agenda
   int iLongDat;        // Longitud de los datos de la agenda
  };  


  // Estructura cabecera definición del formato de registro resultante (Clave+datos),
  // que se explota para generar el fichero virtual “AGENDAS”, su respaldo físico  
  // “Agendas.dat” y el registro de características de la agenda seleccionada actual,
  // que se archiva a su vez en “Agenda.dat

  static struct DsReg
  {
   struct sDsK sDk;
    struct sDsD sDd;
   }
    Agenda_reg,  // DS Soporte características de la agenda en curso [Agenda.dat]
    Agendas_reg, // DS Soporte lista de agendas [Agendas.dat]
    sDsR;        // DS Soporte del fichero virtual copia del físico "Agendas.dat"



                                                       _______

 

A.II.3.5.2 La carga desde el fichero de respaldo

 

Al comenzar el programa el fichero virtual de soporte se carga desde su respaldo físico como se indica en el código resumido a continuación que consta básicamente del método RSTF para restaurar la lista y de un bucle de paso a pantalla:

 

  BOOL CVagendasDlg::OnInitDialog()
   {
    CDialog::OnInitDialog();

     . . .

     // TODO: Add extra initialization here

     . . .


     // Recupera valores anteriores

     strcpy(cAGENDAS, SRRCW_NAME("AGENDAS"));
     lresul = vagendas_dat.RSTF(cAGENDAS);



     // Vuelca lista leida a pantalla

     for(l=1;;++l)
     {
      lresul = vagendas_dat.READ(l, &Agendas_reg);
      if (lresul) break;

 
      // Pasa el registro leido como nuevo ítem del Cuadro de Dialogo Principal

      strcpy(cInString, Agendas_reg.sDk.cNombreAgenda);
      m_list.AddString(cInString);

      _itoa(Agendas_reg.sDd.iLongClav, cInString, 10);
      m_list_iDimC.AddString(cInString);

      _itoa(Agendas_reg.sDd.iLongDat, cInString, 10);
      m_list_lDimD.AddString(cInString);
     }
 

    // NºItems

    er = vagendas_dat.INF(&iDimC, &lDimD, &lNITEM, &lBAJAS, &lNIDD);

    m_lNitem = lNITEM - lBAJAS;


    . . .


    return TRUE;  // return TRUE  unless you set the focus to a control
   }

                                                       _______

 

A.II.3.5.3 La edición de la lista de agendas

 

Una vez presentada la lista, cualquier ítem de la misma se puede marcar con doble-click para pasarlo a los cuadros de entrada y a continuación actualizarlo o escogerlo como agenda concreta de proceso en el botón principal

 

void CVagendasDlg::OnOkMain()
   {
    // TODO: Add your control notification handler code here


    // Define fichero de salida

    FILE * pAgenda_dat;

    . . .


    // Ejecuta tratamiento pendiente según la selección establecida por radio button

    switch(cRadio)
    {
     
     case 'A': // Añadir


        // Añade registro al fichero virtual que posteriormente se descargará al físico
    
        lresul = vagendas_dat.WRITE(&Agenda_reg);

      
        break;



     case 'E': // Editar agenda individual


        // Abre fichero de salida receptor de la agenda seleccionada
      
        if (!(pAgenda_dat = fopen("Agenda.dat", "wb"))) return;


        // Graba el registro seleccionado

        fwrite (&Agenda_reg, sizeof(Agenda_reg), 1, pAgenda_dat);


        // Cierra fichero de salida receptor de la agenda seleccionada

        fclose(pAgenda_dat);


 
        // Invoca al gestor de agenda individual utilizando invocación de aplicación
        // windows (Versión explotación)
 
        lresul = WinExec("vagenda", SW_SHOWNORMAL);


        // Invoca al gestor de agenda individual utilizando invocación directa d.o.s.
        // (Versión desarrollo)
   
        if (lresul <= 31) lresul = system("Vagenda");


        // Fin de proceso

        CVagendasDlg::OnOK();
   
        return;
 


     case 'S': // Suprimir ítem


        // suprime el registro del fichero virtual que posteriormente se descargará
       
        lresul = vagendas_dat.DELET(&Agenda_reg);

        break;



     default: return;
    }



   // Común. Recarga de la lista



   // Limpia el cuadro de lista


   m_list.ResetContent();
   m_list_iDimC.ResetContent();
   m_list_lDimD.ResetContent();



   // Vuelca lista leida a pantalla (De forma similar a como se hizo antes)

   for(l=1;;++l)
   {
    lresul = vagendas_dat.READ(l, &Agendas_reg);
    if (lresul) break;

    . . .
   }
 
 

   // NºItems

   . . .  


   (Son las mismas rutinas que al cargar el programa)



   // Actualiza pantalla

   UpdateData(false);
 
   return;
  }


                                                       _______

 

 

A.II.3.5.4 La descarga al fichero de respaldo

 

Al finalizar el programa el fichero virtual de soporte se descarga a su respaldo físico como se indica en el código presentado a continuación que consta básicamente del método SAVF:

 

   void CVagendasDlg::OnOK()
   {
    long lresul = 0;  // Control invocaciones

 
    // Descarga el fichero virtual al físico

    strcpy(cAGENDAS, SRRCW_NAME("AGENDAS"));
    lresul = vagendas_dat.SAVF(cAGENDAS);


    // Invoca al destructor
 
    vagendas_dat.~vFile();


    // Cierra soporte de ficheros virtuales
 
    SRRCW_CLOSE();

   
    // Fin de proceso

    CDialog::OnOK();
   }


 

A continuación se muestra el código para la cancelación en donde se pide confirmación para descartar cambios:

 

void CVagendasDlg::OnCancelar()
   {
    int iResul=0; // Control de resultados

    // TODO: Add your control notification handler code here

    . . .

 
    // Cancelación con cambios exige confirmación
 
    iResul = AfxMessageBox("¿Desea descartar los cambios y salir?",
                           MB_APPLMODAL|MB_YESNO|MB_ICONQUESTION);

    if (iResul == IDYES)
    {
     vagendas_dat.~vFile();
     SRRCW_CLOSE();
     exit(0);
    }

    return;
   }


                                                       _______


 

A.II.3.6 La implementación de una agenda individual con vFile

 

El tratamiento de las agendas individuales es similar al de la lista de agendas, salvo que se permite reordenar por una vía de acceso alternativa. Veamos como

                                                       _______

 

 

A.II.3.6.1 La estructura de soporte

 

Cada agenda individual se apoya en un diseño muy simple, simplemente reservar 124 posiciones para clave y 1024 para datos, que se manejará como fichero virtual en el programa y que se lee y graba en respaldo físico al comenzar y finalizar el mismo.

 

Así, en la zona de definiciones se crea el soporte base

 

  static vFile vagenda_dat(128, 1024); // Soporte de agenda virtual en curso con vFile

 

 

Y en la zona de carga, restaurá los datos y creará la vista lógica

 

   BOOL CVagendaDlg::OnInitDialog()
   {
    CDialog::OnInitDialog();

    . . .

    // Recupera características de la agenda seleccionada

    if (pAgenda_dat = fopen("Agenda.dat", "rb"))
    {
     fread (&Agenda_reg, sizeof(Agenda_reg), 1, pAgenda_dat);
     if (!feof(pAgenda_dat))
     {
 
      // Pasa a campos de pantalla

      strcpy(cInString, Agenda_reg.sDk.cNombreAgenda);
      m_text1 = cInString;

      m_iDimC = Agenda_reg.sDd.iLongClav;
      m_lDimD = Agenda_reg.sDd.iLongDat;
     }


     // Cierra archivo de características leído
   
     fclose(pAgenda_dat);
    }

 
    // Dimensión Total

    lDimT  = m_iDimC + m_lDimD;



    // Recupera ítems de la agenda seleccionada

    strcpy(cAGENDA, SRRCW_NAME(cagenda_en_curso));
 
    m_lNitem = vagenda_dat.RSTF(cAGENDA);



    // Crea lógico de reordenación, tomando como clave la zona de datos de la
    // perspectiva principal


    lresul = vagenda_dat.CRTLF("vagenda_dat01", 129, m_lDimD);


 

 

Ahora el programa puede utilizar una u otra perspectiva según convenga

 

                                                       _______


 

A.II.3.6.2 La carga desde la vista principal y desde el lógico

 

Como se indicó en el epígrafe dedicado a interfaces, A.II.3.2, ambas vistas son intercambiables y la carga alternativa de una u otra se controla con la marca bReorden, que se activa/desactiva en el botón de reordenar.

 

Veámos el detalle de su implementación en la rutina de recarga postedición:

 

 

   void CVagendaDlg::OnButton1()
   {
     . . .

    
    (La edición es similar a la de la lista de agendas. Saltamos hasta la zona de
     recarga común posterior al tratamiento)

     . . .


    // Limpia el cuadro de lista

    m_list.ResetContent();
    m_list2.ResetContent();



    // Lee fichero virtual y lo descarga al cuadro de lista

    for(long l=1;;++l)
    {
     if (bReorden)
     {

      // Lee en el orden alternativo

      er = vagenda_dat.READ("vagenda_dat01", l, &Vagenda_reg);
      if (er) break;
   
 
      // Graba el registro virtual leido en el cuadro de lista, formato reorden

      m_list2.AddString(Vagenda_reg.sdk.cClave);
      m_list.AddString(Vagenda_reg.sdd.cDatos);
     }

     else

    {
     // Lee en el orden base

     er = vagenda_dat.READ(l, &Vagenda_reg);
     if (er) break;


     // Graba el registro virtual leido en el cuadro de lista
   
     m_list2.AddString(Vagenda_reg.sdd.cDatos);
     m_list.AddString(Vagenda_reg.sdk.cClave);
    }
 
   }


   // NºItems

   er = vagenda_dat.INF(&iDimC, &lDimD, &lNITEM, &lBAJAS, &lNIDD);

   m_lNitem = lNITEM - lBAJAS;


  . . .


 

 

Con ello termina la presentación del ejemplo. Recordemos que ha sido un extracto, se invita a consultar la codificación completa en el apéndice

"C5 Relación de programas de muestra desarrollados en el libro"



                                                       _______

 

 

Como nota final, simplemente remarcar que la clase vFile es una implementación orientada para el uso de una única base de datos (“Físico+Lógicos”) por programa, lo que es válido en la concepción de “Dialog-based” que se ha utilizado en la presentación.

 

Para usar varias bases de datos en el mismo programa hay que utilizar directamente SRRCW ó la clase wFile introducida en la segunda zona del libro, para la plataforma NET.

                                                       _______