Inicio > Delphi, Ejemplos, Programación, Trucos > Crear accesos directos a un menú

Crear accesos directos a un menú

Share Button

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).

0
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
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:

0
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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.

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

Share Button
  1. Juan F.
    miércoles, 26 de enero de 2011 a las 11:57 | #1

    Hola Neftalí,
    Entro todas las semanas en este blog y la idea de utilizar ADO con Threads fue muy interesante.
    Pero esta vez no entiendo (o no encuentro uso) para que serviría lo de los accesos directos. ¿No sería suficiente con una TToolbar?

    La intención con la que escribo es para saber que utilidad tendría esto que publicas, espero no molestar.

    Saludos y a seguir con el blog.

  2. Neftalí
    miércoles, 26 de enero de 2011 a las 12:25 | #2

    Hola Juan.
    No molesta, en absoluto. La idea original es la de que un usuario pueda crear sus accesos directos sobre el “fondo” de la aplicación. Algo así como si trabajaras sobre el escritorio, pero dentro de tu aplicación.

    De todas formas, no deja de ser un ejemplo; Creo que lo más importante en el ejemplo es la interacción con los elementos del menu, que puede servir para lo que he comentado o para otras cosas.

    Un saludo.

  3. Juan F.
    jueves, 27 de enero de 2011 a las 12:56 | #3

    Hola Neftalí!

    Ahora entiendo mejor el ejemplo. Quizás me confundí al estar pensando en las TToolBar (y TActionToolBar) y la capacidad de configuración sin apenas código.

    Me quedo con la idea de escritorio propio de la aplicación.
    Además me he decidido a probar con un MDI. Ha sido muy sencillo reutilizar tu ejemplo para convertirlo a una aplicación MDI y gestionar los accesos directos desde otro form.
    A partir de aquí se puede crear una interfaz de programa muy personalizada.

    Gracias por el ejemplo y hacerme ver su utilidad.

    Saludos!

  4. alexzm1
    lunes, 28 de marzo de 2011 a las 17:55 | #4

    Muchas gracias, funciona a la perfección.

  1. Sin trackbacks aún.
What is 11 + 7 ?
Please leave these two fields as-is:
IMPORTANTE! Para continuar, debes contestar la pregunta anterior (para evitar SPAM) :-)