问题
I have a menu structure like this:
1. Option A
1.1 Option B
1.1.1 Option C
1.1.2 Option D
1.2 Option C
1.2.1 Option B
1.2.2 Option D
1.3 Option D
1.3.1 Option B
1.3.2 Option C
2. Option B
2.1 Option A
2.1.1 Option C
2.1.2 Option D
2.2 Option C
2.2.1 Option A
2.2.2 Option D
2.3 Option D
2.3.1 Option A
2.3.2 Option C
3. Option C
3.1 Option A
3.1.1 Option B
3.1.2 Option D
3.2 Option B
3.2.1 Option A
3.2.2 Option D
1.3 Option D
3.3.1 Option A
3.3.2 Option B
4. Option D
4.1 Option A
4.1.1 Option B
4.1.2 Option C
4.2 Option B
4.2.1 Option A
4.2.2 Option C
4.3 Option C
4.3.1 Option A
4.3.2 Option B
Why do I do such thing? - This menu is used to select a combination of options A,B,C,D
where sequence of selected options matters.
For Example: The user clicks on menu-item 2.3.1. That results in combination B-D-A
.
Now, you know how I currently do it theoretically. Actually, there are much more options to combine. But only three are to be combined at the same time.
The problem is that I have to create all menu-items (three levels deep) before the menu is shown.
Is there a way to add submenu-items just when they are needed (that is when they should be shown)?
回答1:
You can add a dummy item to act as a placeholder for sub menus and then use OnClick
event handler of items that have the dummy items to replace them with real items.
Below is to demonstrate only, not meant to be used in production code. It duplicates the example in the question.
procedure TForm1.PopupMenu1Popup(Sender: TObject);
var
NewItem: TMenuItem;
i: Integer;
begin
PopupMenu1.Items.Clear;
for i := 0 to 3 do begin
NewItem := TMenuItem.Create(PopupMenu1);
NewItem.Caption := Format('%d. Option %s', [i + 1, Chr(i + 65)]);
NewItem.OnClick := ItemClick;
NewItem.Tag := i;
NewItem.Add(TMenuItem.Create(NewItem));
PopupMenu1.Items.Add(NewItem);
end;
end;
procedure TForm1.ItemClick(Sender: TObject);
var
Root: TMenuItem;
function ItemLevel(Item: TMenuItem): Integer;
begin
Result := 0;
while Item.Parent <> Root do begin
Item := Item.Parent;
Inc(Result);
end;
end;
function ExistsInTree(Item: TMenuItem; Option: Integer): Boolean;
begin
Result := Option = Item.Tag;
if not Result then
while Item.Parent <> Root do begin
Item := Item.Parent;
Result := Option = Item.Tag;
if Result then
Break;
end;
end;
function LevelString(Item: TMenuItem): string;
begin
Result := '';
while Item.Parent <> Root do begin
Item := Item.Parent;
Result := IntToStr(Item.MenuIndex + 1) + '.' + Result;
end;
end;
var
Item, NewItem: TMenuItem;
i: Integer;
path: string;
begin
Item := Sender as TMenuItem;
Root := PopupMenu1.Items;
if ItemLevel(Item) < 2 then begin
if Item.Count = 1 then begin
for i := 0 to 3 do begin
if ExistsInTree(Item, i) then
Continue;
NewItem := TMenuItem.Create(Item);
NewItem.OnClick := ItemClick;
NewItem.Tag := i;
Item.Add(NewItem);
NewItem.Caption := Format('%s%d. Option %s',
[LevelString(NewItem), Item.Count - 1, Chr(i + 65)]);
if ItemLevel(NewItem) < 2 then
NewItem.Add(TMenuItem.Create(NewItem));
end;
Item.Delete(0);
end;
end else begin
path := Chr(Item.Tag + 65);
while Item.Parent <> Root do begin
Item := Item.Parent;
path := Chr(Item.Tag + 65) + '-' + path;
end;
ShowMessage(path);
end;
end;
回答2:
It's a bit complicated, since Delphi doesn't have events for this in PopupMenu. What I think is that maybe it could be done the following way:
First of all, check for custom popup menus, for example TMS' one. With this you can prevent the automatic close of the popup when the user clicks on it. After that you can catch that event and dinamically add the submenus you want. But I don't know it will display it immediately, or the Popup should closed and reopened.
You can try to achieve the same with the standard popup, you can find how to prevent the closing here.
来源:https://stackoverflow.com/questions/28048599/how-to-add-subitems-dynamically-to-a-tmenuitem-the-moment-its-subitems-are-shown