este espacio está libre ¿deseas utilizarlo? haz clic aquí.

   Secciones

   Inicio
   Documentos
   Programas
   Links
   Proyectos
   Libros

   Actualizaciones & Foro

   Avisar al actualizar
   Foro Delphiladero

   Otras

   Contacto
   Agregar a favoritos
   Vota por nosotros

 

 

 

inicio » documentos » intro a las mdi - parte 2


indice

Aplicaciones MDI

      El toque final, los MRU
Ultimos comentarios


« aplicaciones mdi »


el toque final, los mru

Bien, todo cocinado, ¿es acaso este el fin del sufrimiento?. Pues, no. Como verás tengo muchas más cartas debajo de la manga, entre ellas hacer un menú MRU. ¿Qué significa eso? Bueno, simplemente es la abreviatura de Most Recently Used o Los Usados Más Recientemente. Por supuesto, esta frase está haciendo referencia a los documentos abiertos/guardados más recientemente. Esto puede resultar un agregado bastante útil para un editor de textos, sobre todo si es MDI porque eso quiere decir que el usuario está permanentemente abriendo y cerrando documentos, o más aún, modificando varios a la vez.

EL MRU, entonces, es una característica muy común de la programación en Windows - una sección del menú archivo que lista los archivos abiertos/guardados recientemente. Al hacer clic en cualquiera de estos se abre el archivo correspondiente permitiéndole al usuario seguir con el trabajo que había dejado.

Como era de esperar esta es la parte más complicada de todo el artículo, por eso va al final. Ahora bien, ¿por qué complicada?. Bueno, porque debemos utilizar un TStringList y un archivo INI.

Aparentemente, sin ser ningún experto en el tema, podemos decir que vamos a necesitar hacer 3 cosas:

1) mostrar la lista de los MRU en el menú al iniciarse la aplicación.
2) ir actualizando la lista a medida que se abren/guardan documentos.
3) al hacer clic en alguno de los MRU listados abrirlo.

Uno imagina que para mostrar los MRU todo ocurre en tiempo de ejecución. De hecho eso es casi cierto. Digo casi, porque los captions de los items sí los determinamos en tiempo de ejecución, pero antes en tiempo de diseño debemos agregar items en blanco en la posición donde irán los MRU. En cierta forma estamos "reservando el espacio" y en tiempo de ejecución determinamos los caption. Pero, ¿no me van a quedar los espacios en blanco en caso de que no haya MRU o haya 2 en vez de 4?. No, Delphi se encarga automaticamente de que los items con caption vacio no se muestren, lo cuál es lógico, ya que no hay nada que mostrar.

Antes de empezar determinemos cuantos MRU vamos a mostrar. Una cantidad aceptable sería 4, pero pueden ser más. Sin embargo, no es recomendable sobrepasar los 9 por 2 razones: los menús se alargan mucho y no hay forma de crear accesos directos con & (por ejemplo, &1, &2 ... &8, &9, &10 se repite el &1).

En cuanto a la posición de los MRU dentro del menú, esta puede variar según los gustos, pero tampoco puede hacerlo demasiado. Básicamente hay 2 maneras de mostrar los MRU en una aplicación. Colocarlos, limitados por 2 separadores uno al comienzo y otro al final, justo antes del último item del menú archivo, generalmente Salir. La segunda opción es crear un sub-menú llamado Archivos Recientes en el menú Archivo y colocar los MRU allí.

Entonces, crearemos 4 items en blanco en el menú Archivo que estarán limitados por 2 separadores. A cada uno de ellos los nombramos mniMRU1, mniMRU2, mniMRU3, mniMRU4 y modificamos su propiedad Tag a 0, 1, 2, 3 respectivamente. Una vez que hayas terminado, haz lo mismo en el menú de la ventana hija utilizando los mismos nombres y propiedades.

El objeto TStringList almacenará la lista de MRUs durante la ejecución del programa, mientras que entre sesión y sesión esa información se guardará en un archivo INI.

El mejor momento para cargar en el objeto TStringList (MRUList) todos los MRUs almacenados en el archivo INI será en el evento OnCreate de la ventana madre y el mejor momento para guardar en el archivo INI todos los MRUs almacenados en el objeto TStringList  será en el evento OnDestroy de la ventana madre. Veamos, entonces cómo debemos implementar estos eventos.

procedure TfrmMain.FormCreate(Sender: TObject);
var IniFile: TIniFile;
    Index: integer;
begin
  MRUList := TStringList.Create;
  IniFile := TIniFile.Create('anotador.ini');

  try {escribimos el contenido del INI en el stringlist}
    for Index := 0 to 3 do
      MRUList.Add(IniFile.ReadString('MRU',IntToStr(Index), ''));
  finally
    IniFile.Free;
  end;
end;

procedure TfrmMain.FormDestroy(Sender: TObject);
var IniFile: TIniFile;
    Index: integer;
begin
  IniFile := TIniFile.Create('anotador.ini');

  try {escribimos el contenido del stringlist en el INI}
    for Index := 0 to 3 do
      IniFile.WriteString('MRU', IntToStr(Index), MRUList[Index]);
  finally
    IniFile.Free;
  end;
end;

Nota: para que Delphi pueda hacer uso de los archivos INI debes incluir el unit IniFiles en tu lista uses.

Bien, ya hemos creado nuestra lista y hemos logrado que se cargue/guarde al abrir/cerrar la aplicación. Pero también debe ser actualizada periodicamente en la medida que se abran/guarden nuevos documentos. Para mantener la lista al día debemos crear un procedure que se encargue del trabajo sucio y luego referenciarlo cada vez que se abran/guarden nuevos docs. Veamos, veamos...

procedure TfrmMain.ActualizarMRU(const Nombre: string);
var i: integer;
begin
  {revisar que no este repetido en la lista}
  for i := MRUList.Count - 1 downto 0 do
    if Nombre = MRUList[i] then
      MRUList.Delete(i);

  {revisar que no este repetido en la lista}
  for i := 0 to (MRUList.Count - 1) do
    if Nombre = MRUList[i] then
      MRUList.Delete(i);

  {nos aseguramos de que la lista tenga 3 elementos}
  while MRUList.Count > 3 do
    MRUList.Delete(MRUList.Count - 1);

  while MRUList.Count < 3 do
    MRUList.Add('');

  {insertar el nuevo doc en primer lugar}
  MRUList.Insert(0, Nombre);
end;

Nota: los documentos más recientes van al comienzo de la lista.

Como ves, este procedimiento primero verifica que el documento a agregar no se encuentre ya listado, si lo está lo borra, luego nos aseguramos de que la lista tenga 3 elementos agregando en caso de que falten y quitando en caso de que sobren; por último inserta el nuevo documento en la primera posición de la lista.

Ok, pero ActualizarMRU sólo actualiza el objeto MRUList pero no la lista que se visualiza en el menú. Para ello debemos crear otro procedimiento llamado VisualizarMRU que ponga el menú al día con respecto a las modificaciones que sufre el objeto MRUList. Veamos su implementación.

procedure TfrmMain.VisualizarMRU;
begin
  mniMRU1.Caption := '&1 ' + MRUList[0];
  mniMRU1.Visible := (MRUList[0] <> '');

  mniMRU2.Caption := '&2 ' + MRUList[1];
  mniMRU2.Visible := (MRUList[1] <> '');

  mniMRU3.Caption := '&3 ' + MRUList[2];
  mniMRU3.Visible := (MRUList[2] <> '');

  mniMRU4.Caption := '&4 ' + MRUList[3];
  mniMRU4.Visible := (MRUList[3] <> '');
end;

Nota: (MRUList[x] <> '') es una expresión booleana que devuelve True en caso de ser cierta o False en caso de no serlo.

Ahora bien, todo esto que estuvimos haciendo lo hicimos en la ventana madre, pero ¿es que la ventana hija no hay que actualizarla? Por supuesto que sí, asi que debes agregar este mismo procedimiento (VisualizarMRU) en las sección public de la ventana hija. No es necesario cambiarle los nombres ya que son los mismos para ambos menús, pero sí es necesario cambiar a MRUList[x] por frmMain.MRUList[x]. Esta es la razón por la que MRUList fue declarado public en la ventana madre.

Como ya explicamos ActualizarMRU y VisualizarMRU deben ser llamados al crear/abrir documentos, ya que de acuerdo a los estándares, los nuevos documentos no pueden ser agregados hasta que no se les asigne un nombre y sean guardados. De la misma forma, los documentos que se cierran no pueden ser agregados si no fueron guardados anteriormente. Bien, veamos cómo debemos llamar a ActualizarMRU y VisualizarMRU.

procedure TfrmMain.mniArAbrirClick(Sender: TObject);
begin
  if OpenDialog.Execute then
    CrearVentanaMDIHija(OpenDialog.FileName);

  TfrmChild(ActiveMDIChild).DocNuevo := False;
  ActualizarMRU(OpenDialog.FileName);
  TfrmChild(ActiveMDIChild).VisualizarMRU;
end;

y el procedimiento para guardar declarado en frmChild queda así:
 

procedure TfrmChild.Guardar(Nombre: string);
begin
  reMain.Lines.SaveToFile(Nombre);
  reMain.Modified := False;
  DocNuevo := False;
  frmMain.ActualizarMRU(Nombre);
  VisualizarMRU;
end;

Como vemos, aunque los procedimientos son bastante directos y fáciles de entender, deben ser llamados en el momento indicado para evitar cualquier tipo de errores. Hasta el momento venimos llamando a frmChild.VisualizarMRU, es decir que venimos actualizando al menú que se muestra siempre que hay documentos abiertos, pero como todos sabemos no siempre los hay. También es necesario, entonces, llamar a frmMain.VisualizarMRU cada vez que se inicia la aplicación y cuando se cierra la última ventana MDI hija, es decir, en los casos en los que no hay documentos abiertos y por lo tanto se va a mostrar el menú de frmMain.

procedure TfrmMain.FormCreate(Sender: TObject);
var IniFile: TIniFile;
    Index: integer;
begin
  MRUList := TStringList.Create;
  IniFile := TIniFile.Create('anotador.ini');

  try {escribimos el contenido del INI en el stringlist}
    for Index := 0 to 3 do
      MRUList.Add(IniFile.ReadString('MRU',IntToStr(Index), ''));
  finally
    IniFile.Free;
  end;

  VisualizarMRU;
end;

procedure TfrmChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;

  {si este es el último MDI child abierto ...}
  if frmMain.MDIChildCount = 1 then
    frmMain.VisualizarMRU;
end;

Finalmente, debemos llamar a VisualizarMRU siempre que el foco pase de una ventana hija a otra para actualizar el listado de MRUs en el menú de las ventanas MDI hijas. De lo contrario, cada ventana hija visualizará los MRUs de acuerdo a su última actualización, y por lo tanto, no necesariamente el listado actual.

procedure TfrmChild.FormActivate(Sender: TObject);
begin
  VisualizarMRU;
  frmMain.DeterminarToolbar(tlbMain);
end;

Para concluir con esta parte, entonces, queda que ActualizarMRU, el procedimiento utilizado para actualizar el objeto MRUList, se define sólo en frmMain, mientras que VisualizarMRU debemos definirlo en frmMain y también en frmChild. Los llamados a ActualizarMRU sólo se dan en frmMain.Abrir y frmChild.Guardar; mientras que los llamados a frmMain.VisualizarMRU

Bien, por fin llegamos al fin. Jej!, después de tanto escribir como que cansa, pero seguiremos hasta el final. Nos falta sólo permitir que cuando el usuario haga clic sobre el archivo listado en el menú éste se abra. Para ello creamos un nuevo procedimiento public llamado AbrirMRU. Veamos cómo hacerlo.

procedure TfrmMain.AbrirMRU(Sender: TObject);
var Index: integer;
begin
  Index := TMenuItem(Sender).Tag;

  if MRUList[Index] <> '' then
  begin
    CrearVentanaMDIHija(MRUList[Index]);
    TfrmChild(ActiveMDIChild).DocNuevo := False;
    TfrmChild(ActiveMDIChild).VisualizarMRU;
  end;
end;

Ahora, todo lo que nos queda por hacer es llamar a este procedimiento desde el evento OnClick de los items del menú de frmMain y frmChild.

procedure TfrmMain.MRUClick(Sender: TObject);
begin
  AbrirMRU(Sender);
end;

procedure TfrmChild.MRUClick(Sender: TObject);
begin
  frmMain.AbrirMRU(Sender);
end;

Le asignamos el procedimiento MRUClick al evento OnClick de todos los items de los menús y listo. Salió con fritas. Terminamos. Una pavada esto de los MDI, ¿eh?

Nota: Para que sea posible asignar MRUClick al evento OnClick de los items de los menús en tiempo de diseño es necesario declarar MRUClick en la sección donde se dclaran los demás eventos (no en private ni en public) del form correspondiente.


últimos comentarios

He intentado resumir en este artículo todo lo que he logrado aprender en estos últimos tiempos relacionado con las aplicaciones MDI. Como veras este no es otro artículo más sobre MDI, explica los problemas con los que seguramente te tropezarás a la hora de crear este tipo de aplicaciones. Espero hayas disfrutado esta lectura y lo que es más importante espero haber motivado tu espíritu de aprendizaje y borrado la creencia popular de que programar es MUY complicado. Ahora todo depende de ti.

Cualquier duda, crítica o sugerencia que tengas por supuesto puedes escribirme y lo más pronto que pueda trataré de responderte.


Recomienda este documento a un amigo.
Recuerda enviarme tus comentarios sobre el artículo.


«anterior - Indice - siguiente»


 Copyright © Pablo Castagnino 2000-2002. Todos los derechos reservados.


Puedes ayudarnos

¿Conoces algún documento enteramente en español que pueda resultar interesante para nuestra comunidad delphiadicta?

Coméntanos de él aquí.


Vota por nosotros