Visualizza un suggerimento suggerimento su una voce di menu disabilitata di un menu popup
-
19-08-2019 - |
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.
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.