Domanda

Quindi ho un TMenuItem collegato a una TAction su un TPopupMenu per un TDBGrid (in realtà di terze parti, ma hai l'idea). In base alla riga selezionata nella griglia, TAction è abilitato o disabilitato. Quello che voglio è poter mostrare un suggerimento all'utente che spiega perché l'elemento è disabilitato.

Per quanto riguarda il motivo per cui voglio un suggerimento su una voce di menu disabilitata, diciamo solo che sono in accordo con Joel .

Tutti i TMenuItem hanno una proprietà hint, ma come meglio posso dire che vengono usati solo il gestore eventi TApplicationEvent.OnHint per inserire il suggerimento in un TStatusBar o in qualche altra elaborazione speciale. Ho trovato un articolo su come creare la tua finestra uniforme per un TMenuItems di TMainMenu, ma non funziona su TMenuItem di TPopupMenu. Funziona gestendo il messaggio WM_MENUSELECT, che per quanto ne so non viene inviato su un TPopupMenu.

È stato utile?

Soluzione

WM_MENUSELECT è effettivamente gestito per le voci di menu anche nei menu popup, ma non dal proc di Windows del modulo contenente il menu (popup), ma da una finestra di aiuto invisibile creata da Menus.PopupList. Fortunatamente puoi (almeno sotto Delphi 5) arrivare a questo HWND tramite Menus.PopupList.Window.

Ora puoi usare il vecchio stile per sottoclassare una finestra, come descritto ad esempio in questo CodeGear articolo , per gestire WM_MENUSELECT anche per i menu popup. L'HWND sarà valido dopo la creazione del primo TPopupMenu prima della distruzione dell'ultimo oggetto TPopupMenu.

Un rapido test con l'app demo nell'articolo collegato nella domanda dovrebbe rivelare se funzionerà.

Modifica: funziona davvero. Ho modificato l'esempio collegato per mostrare suggerimenti anche per il menu popup. Ecco i passaggi:

Aggiungi un gestore per OnDestroy, una variabile membro per il vecchio proc della finestra e un metodo per il nuovo proc della finestra al modulo:

TForm1 = class(TForm)
  ...
  procedure FormCreate(Sender: TObject);
  procedure FormDestroy(Sender: TObject);
  procedure ApplicationEvents1Hint(Sender: TObject);
private
  miHint : TMenuItemHint;
  fOldWndProc: TFarProc;
  procedure WMMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;
  procedure PopupListWndProc(var AMsg: TMessage);
end;

Modifica il gestore OnCreate del modulo per sottoclassare la finestra nascosta PopupList e implementare il corretto ripristino del proc della finestra nel gestore OnDestroy:

procedure TForm1.FormCreate(Sender: TObject);
var
  NewWndProc: TFarProc;
begin
  miHint := TMenuItemHint.Create(self);

  NewWndProc := MakeObjectInstance(PopupListWndProc);
  fOldWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
    integer(NewWndProc)));
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  NewWndProc: TFarProc;
begin
  NewWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
    integer(fOldWndProc)));
  FreeObjectInstance(NewWndProc);
end;

Implementa il proc della sottoclasse:

procedure TForm1.PopupListWndProc(var AMsg: TMessage);

  function FindItemForCommand(APopupMenu: TPopupMenu;
    const AMenuMsg: TWMMenuSelect): TMenuItem;
  var
    SubMenu: HMENU;
  begin
    Assert(APopupMenu <> nil);
    // menuitem
    Result := APopupMenu.FindItem(AMenuMsg.IDItem, fkCommand);
    if Result = nil then begin
      // submenu
      SubMenu := GetSubMenu(AMenuMsg.Menu, AMenuMsg.IDItem);
      if SubMenu <> 0 then
        Result := APopupMenu.FindItem(SubMenu, fkHandle);
    end;
  end;

var
  Msg: TWMMenuSelect;
  menuItem: TMenuItem;
  MenuIndex: integer;
begin
  AMsg.Result := CallWindowProc(fOldWndProc, Menus.PopupList.Window,
    AMsg.Msg, AMsg.WParam, AMsg.LParam);
  if AMsg.Msg = WM_MENUSELECT then begin
    menuItem := nil;
    Msg := TWMMenuSelect(AMsg);
    if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then begin
      for MenuIndex := 0 to PopupList.Count - 1 do begin
        menuItem := FindItemForCommand(PopupList.Items[MenuIndex], Msg);
        if menuItem <> nil then
          break;
      end;
    end;
    miHint.DoActivateHint(menuItem);
  end;
end;

Questo viene fatto per tutti i menu popup in un ciclo, fino a quando non viene trovato il primo elemento o sottomenu corrispondente.

Altri suggerimenti

Non sono sicuro che sia d'aiuto, ma ho creato la mia finestra di suggerimento multilinea (per Delphi7) per poter mostrare più di una sola riga di testo. È open source e puoi trovarlo qui .

È necessario del lavoro per mostrarlo nella giusta posizione sullo schermo, ma hai il pieno controllo su di esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top