Archivo

Entradas Etiquetadas ‘Menu’

Crear accesos directos a un menú

martes, 25 de enero de 2011 4 comentarios

Hace tiempo me topé con un tema similar a este, aunque con un enfoque diferente; En aquel caso se trataba de que el usuario pudiera crear su propio menú personalizado dentro de una aplicación. Es decir, que además de las opciones propias de la aplicación, el usuario pudiera configurarse un menú con las opciones que más deseara utilizar o tener más «a mano». En este caso, y a partir de este hilo en los foros del Clubdelphi, se ha planteado la posibilidad de que un usuario pueda crearse sus propios «accesos directos» a opciones del menú.

La solución en aquel momento pasó por «volcar» el contenido del menú a otro componente (en ese caso un TreeView, de forma similar a cómo se ve en este ejemplo) y desde ese, generar la estructura del nuevo punto de menú arrastrando elementos.

Para el problema de generar accesos directos, se me antoja que se pueda usar un sistema similar.

(1) «Volcar» el contenido del menú hasta otro componente que nos permita trabajar con los elementos del menú (ya que ni el menú ni los ítems poseen opciones para arrastrar -Drag & Drop-). Este esta caso vamos a utilizar un componente (TListBox) donde almacenaremos los elementos y los apuntadores a los ítems del menú (propiedad Objects).

procedure TFormMain.Button1Click(Sender: TObject);
var
  i:integer;
  str:string;
 
  // Recursiva para obtener los subItems
  procedure GetItems(mi:TMenuItem);
  var
    i:Integer;
  begin
    for i := 0 to (mi.Count - 1) do begin
      Str := mi.Items[i].Caption;
      ListBox1.Items.AddObject(Str, mi.Items[i]);
      // SubItems de este
      GetItems(mi.Items[i]);
    end;
  end;
begin
 
  // Recorerr menu principal
  for i := 0 to (MainMenu1.Items.Count - 1) do begin
    Str := MainMenu1.Items[i].Caption;
    ListBox1.Items.AddObject(Str, MainMenu1.Items[i]);
    // SubItems de este
    GetItems(MainMenu1.Items[i]);
  end;
end;

Con este código poblamos el ListBox con los Caption(Text) de los elementos del menú, y lo que es más importante, los apuntadores a cada elementos que se guardar al utilizar AddObject.

(2) ¿Cómo crear un acceso directo que permita ejecutar una opción de menú? Para ello podemos utilizar un TImage que sobre el cual programaremos el evento OnDblClick/OnClick.

Crear el componente es sencillo, y se puede ver código de ejemplo de cómo hacerlo en estas entradas:

La idea es que cada «acceso directo» posea un apuntador al elemento de menú correspondiente para poder ejecutar el código programado en el OnClick o en la TAction asociada a ese elemento del menú. Lo lógico sería utilizar una propiedad del propio componente (Data, Object,….) que nos permitiera enlazar directamente. No es el caso del TImage, así que en el ejemplo utilizaremos el propio ListBox como el «contenedor» de los apuntadores (como una lista intermedia), aunque como he dicho, la solución ideal, sería que cada «acceso directo»  tuviera un puntero «directo» al TMenItem asociado.

El código paras crear el componente y gestionar esa asociación podría ser similar a este:

procedure TFormMain.Button2Click(Sender: TObject);
var
  img:TImage;
  mi:TMenuItem;
begin
  If  ListBox1.ItemIndex = -1 then begin
    MessageDlg('Selecciona un elemento de la llista', mtWarning, [mbOK], 0);
    Exit;
  end;
 
  // Item del menu
  mi := TMenuItem(ListBox1.Items.Objects[ListBox1.ItemIndex]);
 
  // Tiene asignado el OnClick?
  if Assigned(mi.OnClick) then begin
    // Nada
  end
  else begin
    // Tiene asignada la action?
    if Assigned(mi.Action) then begin
      //signado OnExecute
      if Assigned(mi.Action.OnExecute) then begin
        // Nada
      end
      else begin
        MessageDlg('Ese elemento no tiene nada que hacer asignado',
             mtWarning, [mbOK], 0);
        Exit;
      end;
    end
    else begin
      MessageDlg('Ese elemento no tiene nada que hacer asignado',
             mtWarning, [mbOK], 0);
      Exit;
    end;
  end;
 
  Randomize;
 
  // Elemento seleccionado
  img := TImage.Create(nil);
  img.Parent := Panel1;
  img.Height := 32;
  img.Width := 32;
  img.Left := Random(panel1.Width - img.Width);
  img.Top := Random(panel1.Height - img.Height);
  img.Stretch := True;
  img.Transparent := True;
  // El TAG es la posicion en la lista
  img.Tag := ListBox1.ItemIndex;
  // Item del menu
  mi := TMenuItem(ListBox1.Items.Objects[ListBox1.ItemIndex]);
  // Asignar la imagen
  ImageList1.GetBitmap(mi.ImageIndex, img.Picture.Bitmap);
  // Asignar el evento
  img.OnClick := MyImgClick;
end;

Primero se realizan unas comprobaciones para detectar si posee alguna acción asignada (sea OnClick o TAction) y posteriormente se crea el TImage, se configura y se asigna como TAG el ItemIndex del ListBox (que es este caso estamos utilizando como estructura intermedia para guardar el apuntador al TMenuItem).

Finalmente sólo quedar crear el procedimiento MyImgClick, que ejecutará el código asignado al elemento del menú cuando se presione sobre la imagen asociada. Se incluyen comprobaciones similares a las anteriores, por si el elemento no tiene nada asignado y se tiene en cuenta también que haya código en el OnClick del TMenuItem o exista una TAction asociada.

var
  i:integer;
  str:string;
  mi:TMenuItem;
begin
  // Test del sender
  if not (sender is TImage) then begin
    Exit;
  end
  else begin
    i := TImage(Sender).Tag;
    Str := ListBox1.Items[i];
  end;
 
  // Acceder a la opción de menú
  mi := TMenuItem(ListBox1.Items.Objects[i]);
  // Asignado código?
  if Assigned(mi.OnClick) then begin
    mi.OnClick(nil);
    Exit;
  end
  else begin
    // Tiene asignada la action?
    if Assigned(mi.Action) then begin
      // Asignado OnExecute
      if Assigned(mi.Action.OnExecute) then begin
        mi.Action.OnExecute(nil);
        Exit;
      end;
    end
  end;
 
  MessageDlg('No hay nada asignado a esa opción...', mtInformation, [mbOK], 0);
end;

Se puede mejorar y «refinar» bastante más, pero creo que la idea queda clara. A partir de aquí cada uno que «añada» lo que quiera. Cualquier sugerencia será bien recibida.

El código del ejemplo se puede descargar desde aquí.

<DESCARGAR SOURCES>

Crear un menu de aplicación a partir de texto

lunes, 24 de marzo de 2008 1 comentario

Éste ejemplo muestra cómo crear en una aplicación, un menú dinámicamente a partir de texto; Normalmente la opción más común es que éste texto se almacene en un fichero o incluso en Base de Datos. Para el ejemplo, el texto ya está almacenado en un memo; Las formas de almacenar ese texto correspondiente al menú son muy variadas.

Imagen del ejemplo

En el ejemplo podemos ver cómo crear menús en tiempo de ejecución y además cómo asignarles los eventos y acciones necesarias.
Además está implementada la creación del menú a partir de un formato de Texto. En este caso almacenado en un TMeno, pero fácilmente exportable a otros formatos.
Es ideal para aplicaciones que trabajen con menús dinámicos, con algun sistema de plugins (y el menú no sea fijo) o para aquellas que por temas de seguridad deban fucncionar con diferentes menús (segun los diferentes permisos de los usuarios, por ejemplo).

Download Descargar ejemplo

Categories: Delphi, Ejemplos Tags:

Convertir un Menu en un TreeView

domingo, 24 de febrero de 2008 3 comentarios

En este ejemplo se muestra de forma sencilla cómo «pasar» o convertir un Menu existente (componente TMainMenu) en un TTreeView; Conservando la jerarquía de los elementos y asignando las imágenes que ya existan en el ejemplo.

Imagen del ejemplo

Download Descargar ejemplo

Categories: Delphi, Ejemplos Tags: , ,

«Pasar» uno o varios ítems de un menú a la parte derecha

sábado, 1 de diciembre de 2007 Sin comentarios

A veces es interesante poder situar una o varias opciones de un menú a la parte derecha, como una forma de diferenciarlas de las demás o por una característica púramente estética.
Utilizando el siguiente procedimiento se pueden mover uno o varios ítems.

Definimos un menú y sus ítems de la forma estandard en Delphi. Llamamos a éste procedimiento (en el OnShow del formulario por ejemplo) con el menú como parámetro y en índice del elemento que queremos «mover» a la parte derecha; ese y los siguiente serán desplazados a la parte derecha de la zona del título.

Ej: Total Commander, por ejempo, utiliza ésta característica con el menú ade ayuda:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{:Pasa un item de menú a la derecha de la pantalla.
Pasa un item de menú y todos los que hay a su derecha a la parte "derecha" de
la barra de menús del formulario.}
procedure MenuItemAtRight (MainMenu:TMainMenu; Position:Integer);
var
   Handle:HMENU;
   MenuItemInfo:TMenuItemInfo;
   Buffer: array[0..79] of char;
begin
   // Coger el handle del menu principal
   Handle := MainMenu.Handle;
 
   // Rellenar estructura
   FillChar (MenuItemInfo, SizeOf(TMenuItemInfo), 0);
   MenuItemInfo.cbSize := SizeOf(TMenuItemInfo);
   MenuItemInfo.fMask := MIIM_TYPE;
   MenuItemInfo.dwTypeData := Buffer;
   MenuItemInfo.cch := SizeOf(Buffer);
 
   // No se puede obtener la informacion del item del menú ?
   if not GetMenuItemInfo(Handle, Position, True, MenuItemInfo) then begin
    // salir
    Exit;
   end;
 
   // Modificar el tipo de menú para ponerlo a la derecha
   MenuItemInfo.fType := MenuItemInfo.fType or MF_RIGHTJUSTIFY;
 
   // No se puede establecer informacion del item de menú
   if not SetMenuItemInfo (Handle, Position, True, MenuItemInfo) then begin
     Exit;
   end;
end;

Puedes llamar a ésta funcioón de la siguiente forma:

1
2
3
4
5
// Para poner el tercer eleento y los siguientes:
MenuItemAtRight(Self.MainMenu1, 3);
// Para poner el último elemento:
MenuItemAtRight(Self.MainMenu1,
Windows.GetMenuItemCount(Self.MainMenu1.Handle)-1);
Categories: Delphi, Trucos Tags: , , ,