Có workaround, nhưng nó là rất, rất bẩn: Sử dụng một lớp bánh để có được quyền truy cập vào các thành viên tin FHandle của TPopupMenu.Items thuộc tính mục menu.
Lớp cracker liên quan đến việc tái tạo bố cục lưu trữ riêng tư của lớp đích đến và bao gồm thành viên riêng tư quan tâm và sử dụng loại truyền cho "lớp phủ" nhập vào một thể hiện của loại mục tiêu trong ngữ cảnh sau đó cho phép bạn truy cập bộ nhớ trong của mục tiêu.
Trong trường hợp này, đối tượng mục tiêu là Items tài sản của TPopupMenu mà là một thể hiện của TMenuItem. TMenuItem xuất phát từ TComponent vì vậy lớp cracker để cung cấp quyền truy cập vào FHandle cho một TMenuItem là:
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;
LƯU Ý: Kể từ khi kỹ thuật này dựa trên một tái tạo chính xác của bộ nhớ trong bố cục của lớp mục tiêu, tuyên bố trình bẻ khóa có thể cần phải bao gồm $ IFDEF các biến thể để phục vụ cho thay đổi trong đó bố trí ternal giữa các phiên bản Delphi khác nhau. Tuyên bố ở trên là chính xác cho Delphi XE4 và phải được kiểm tra đối với các nguồn TMenuItem cho sự chính xác với các phiên bản Delphi khác.
Với lớp cracker đó, chúng tôi có thể cung cấp một tiện ích proc để bọc các thủ thuật khó chịu mà chúng tôi sẽ thực hiện bằng cách sử dụng quyền truy cập này.Trong trường hợp này, chúng tôi có thể xóa các mục menu như bình thường, nhưng cũng có thể tự xóa các mục menu như là bình thường, nhưng cũng có thể tự gọi số DestroyMenu() bằng cách sử dụng công cụ cracker cast để ghi đè biến thành viên FHandle với 0 vì nó hiện không hợp lệ và cần phải là 0 để buộc TPopupMenu để tái tạo menu khi tiếp theo cần thiết:
procedure ResetPopupMenu(const aMenu: TPopupMenu);
begin
aMenu.Items.Clear;
// Here be dragons...
DestroyMenu(aMenu.Items.Handle);
TMenuItemCracker(aMenu.Items).FHandle := 0;
end;
trong mã mẫu của bạn chỉ đơn giản là thay thế cuộc gọi của bạn để PopupMenu1.Items.Clear trong handler Button2Click của bạn với một cuộc gọi đến ResetPopupMenu (PopupMenu1).
Không cần phải nói rằng điều này là nguy hiểm trong cùng cực. Khá khác biệt với sự điên rồ tuyệt đối của việc xâm nhập vào kho lưu trữ riêng của một lớp, không có tài khoản nào được thực hiện trong trường hợp cụ thể này để hủy các menu đã hợp nhất, ví dụ.
Nhưng bạn đã hỏi liệu có cách giải quyết hay không và đây là ít nhất một. :)
Cho dù bạn xem xét điều này nhiều hơn hoặc ít thực tế hoặc mong muốn hơn là việc hủy và tái tạo TPopupMenu tùy thuộc vào bạn. Lớp nứt là một kỹ thuật có thể hữu ích cho bạn ra khỏi mứt mà nếu không có thể không thể giải quyết nhưng chắc chắn nên được coi là một "phương sách cuối cùng"!
Tôi có thể sao chép điều này chỉ với các cuộc gọi api, CreatePopupMenu, InsertMenu, TrackPopupMenu, DeleteMenu vv .. Không có 'co' miễn là xử lý hợp lệ. Như vậy, ý kiến của tôi là, giải pháp duy nhất là giải phóng menu popup và tạo lại nó trong thời gian chạy, đó là cách duy nhất để gọi 'DestroyMenu'. –
@hikari: Cảm ơn bạn đã chỉnh sửa.Câu hỏi này hữu ích hơn nhiều với mã sẵn có, đặc biệt cho những người đọc trong tương lai có thể tìm thấy nó trong một tìm kiếm. –