Es Abhilfe ist, aber es ist sehr, sehr schmutzig: eine Cracker-Klasse verwenden, Zugang zum privaten Mitglied fhandle der zu erhalten TPopupMenu.Items Menüpunkt Eigenschaft.
Eine Cracker-Klasse umfasst das Reproduzieren des privaten Speicherlayouts der Zielklasse bis einschließlich des interessierenden privaten Members und das Verwenden einer Typumwandlung zum "Überlagern" dieses Typs auf eine Instanz des Zieltyps in einem Kontext Dann können Sie auf den internen Speicher des Ziels zugreifen.
In diesem Fall wird das Zielobjekt ist die Artikel Eigenschaft TPopupMenu, die eine Instanz von TMenuItem. TMenuItem leitet sich von TComponent so die Cracker-Klasse Zugriff auf fhandle für eine TMenuItem zur Verfügung zu stellen:
type
// Here be dragons...
TMenuItemCracker = class(TComponent)
private
FCaption: string;
FChecked: Boolean;
FEnabled: Boolean;
FDefault: Boolean;
FAutoHotkeys: TMenuItemAutoFlag;
FAutoLineReduction: TMenuItemAutoFlag;
FRadioItem: Boolean;
FVisible: Boolean;
FGroupIndex: Byte;
FImageIndex: TImageIndex;
FActionLink: TMenuActionLink;
FBreak: TMenuBreak;
FBitmap: TBitmap;
FCommand: Word;
FHelpContext: THelpContext;
FHint: string;
FItems: TList;
FShortCut: TShortCut;
FParent: TMenuItem;
FMerged: TMenuItem;
FMergedWith: TMenuItem;
FMenu: TMenu;
FStreamedRebuild: Boolean;
FImageChangeLink: TChangeLink;
FSubMenuImages: TCustomImageList;
FOnChange: TMenuChangeEvent;
FOnClick: TNotifyEvent;
FOnDrawItem: TMenuDrawItemEvent;
FOnAdvancedDrawItem: TAdvancedMenuDrawItemEvent;
FOnMeasureItem: TMenuMeasureItemEvent;
FAutoCheck: Boolean;
FHandle: TMenuHandle;
end;
HINWEIS: Da diese Technik beruht auf einer exakte Wiedergabe des internen Speicher Layout der Zielklasse, die Cracker-Erklärung muss möglicherweise enthalten $ IFDEF Variationen für Änderungen in diesem in ternales Layout zwischen verschiedenen Delphi-Versionen. Die obige Deklaration ist korrekt für Delphi XE4 und sollte mit der TMenuItem Quelle auf Korrektheit w.r.t anderen Delphi-Versionen überprüft werden.
Mit dieser Cracker-Klasse können wir dann ein Utility-Proc zur Verfügung stellen, um die bösen Tricks, die wir dann mit dem Zugriff, den dies bietet, auszuführen.In diesem Fall können wir die Menüeinträge wie gewohnt löschen, aber auch DestroyMenu() selbst aufrufen und dabei die FHandle Membervariable mit 0 überschreiben, da sie nun ungültig ist und 0 sein muss, um die zu erzwingen TPopupMenu das Menü neu erstellen, wenn neben benötigt:
procedure ResetPopupMenu(const aMenu: TPopupMenu);
begin
aMenu.Items.Clear;
// Here be dragons...
DestroyMenu(aMenu.Items.Handle);
TMenuItemCracker(aMenu.Items).FHandle := 0;
end;
in Ihrem Beispielcode einfach auf Ihren Anruf PopupMenu1.Items.Clear in Ihrem Button2Click Handler mit einem Aufruf an ResetPopupMenu (PopupMenu1) ersetzen.
Es ist selbstverständlich, dass dies im Extremfall gefährlich ist. Abgesehen von dem bloßen Wahnsinn des Hackens im privaten Speicher einer Klasse wird in diesem speziellen Fall zum Beispiel nicht darauf geachtet, zusammengeführte Menüs zu trennen.
Aber Sie haben gefragt, ob es einen Workaround gab, und hier ist mindestens einer. :)
Ob Sie dies mehr oder weniger praktisch oder wünschenswert halten, als einfach die TPopupMenu zu zerstören und neu zu erstellen, liegt bei Ihnen. Class Cracking ist eine Technik, die nützlich sein kann, um aus einem Stau herauszukommen, der sonst unmöglich zu lösen wäre, aber definitiv als "letzter Ausweg" betrachtet werden sollte!
Ich kann das mit nur api Aufrufen, CreatePopupMenu, InsertMenu, TrackPopupMenu, DeleteMenu usw. duplizieren. Es gibt keine "Kontraktion", solange das Handle gültig ist. Daher ist meine Meinung, dass die einzige Lösung darin besteht, das Popup-Menü freizugeben und es zur Laufzeit neu zu erstellen, das ist die einzige Möglichkeit, 'DestroyMenu' aufzurufen. –
@hikari: Danke für die Bearbeitung.Die Frage ist viel nützlicher mit dem verfügbaren Code, insbesondere für zukünftige Leser, die sie in einer Suche finden können. –