Определение элементов главного меню в Delphi без перебора всех пунктов
При работе с меню в Delphi (VCL) часто возникает необходимость определить, является ли конкретный TMenuItem элементом главного меню (например, "Файл", "Правка") или подменю. В этой статье мы рассмотрим несколько подходов к решению этой задачи и оптимизации проверки.
Проблема стандартного подхода
Изначально предлагается использовать проверку на количество подпунктов:
if aItem.Count > 0 then
Однако этот метод не различает элементы главного меню и подменю, имеющие свои собственные подпункты.
Решение с использованием Windows API
Один из предложенных вариантов использует API функцию GetMenuItemInfo:
function IsMenuBarItem(aMenu: TMainMenu; aItem: TMenuItem): Boolean;
var
LInfo: MENUITEMINFOW;
LHMenu: HMenu;
I: Integer;
begin
Result := False;
LHMenu := aMenu.Handle;
ZeroMemory(@LInfo, SizeOf(LInfo));
LInfo.cbSize := SizeOf(LInfo);
LInfo.fMask := MIIM_SUBMENU;
for I := 0 to GetMenuItemCount(LHMenu) - 1 do
begin
if GetMenuItemInfoW(LHMenu, I, True, LInfo) then
begin
if LInfo.hSubMenu = aItem.Handle then
begin
Result := True;
Exit;
end;
end;
end;
end;
Недостаток этого подхода - необходимость перебора всех элементов меню, что может быть неэффективно.
Альтернативные решения
1. Использование свойства Parent
Как отметил Remy Lebeau, свойство Parent для элементов главного меню указывает на свойство Items главного меню:
function IsMenuBarItem(aItem: TMenuItem; aMenu: TMainMenu): Boolean;
begin
Result := (aItem.Parent = aMenu.Items);
end;
Этот метод более эффективен, так как не требует перебора элементов.
2. Использование метода Find
Brian Evans предложил использовать метод Find:
function IsMenuBarItem(MainMenu: TMainMenu; MenuItem: TMenuItem): Boolean;
begin
Result := (MainMenu.Items.Find(MenuItem.Caption) <> nil);
end;
Однако этот метод может давать ложные срабатывания, если в меню есть элементы с одинаковыми заголовками.
3. Двойной цикл обхода
Для решения конкретной проблемы с добавлением символа & можно использовать двойной цикл:
procedure FixMenuCaptions(aMenu: TMainMenu);
var
I, K: Integer;
procedure FixItem(aItem: TMenuItem);
var
J: Integer;
begin
if (Pos('&', aItem.Caption) = 0) and (not aItem.IsLine) then
aItem.Caption := '&' + aItem.Caption;
for J := 0 to aItem.Count - 1 do
FixItem(aItem.Items[J]);
end;
begin
for I := 0 to aMenu.Items.Count - 1 do
for K := 0 to aMenu.Items[I].Count - 1 do
FixItem(aMenu.Items[I].Items[K]);
end;
Этот подход гарантирует, что обработка не коснется элементов главного меню.
Проблема с OwnerDraw и TToolBar
В случае, когда TMainMenu размещается в TToolBar с OwnerDraw := True, стандартные методы могут работать некорректно. В этом случае можно использовать комбинированный подход:
function IsMenuBarItem(aItem: TMenuItem; aMenu: TMainMenu): Boolean;
begin
// Проверяем, что Parent указывает на Items главного меню
// И элемент не является разделителем
Result := (aItem.Parent = aMenu.Items) and (not aItem.IsLine);
end;
Заключение
Для большинства случаев самым эффективным и надежным методом определения элементов главного меню является проверка свойства Parent. Этот подход:
Не требует перебора всех элементов меню
Работает быстро и эффективно
Подходит для большинства стандартных сценариев
Для более сложных случаев, таких как OwnerDraw меню в TToolBar, может потребоваться дополнительная проверка или использование API функций.
Пример итогового решения:
procedure FixMenuCaptions(aMenu: TMainMenu);
var
I: Integer;
procedure FixItem(aItem: TMenuItem);
var
J: Integer;
begin
if (Pos('&', aItem.Caption) = 0) and
(not aItem.IsLine) and
(not IsMenuBarItem(aItem, aMenu)) then
aItem.Caption := '&' + aItem.Caption;
for J := 0 to aItem.Count - 1 do
FixItem(aItem.Items[J]);
end;
begin
for I := 0 to aMenu.Items.Count - 1 do
FixItem(aMenu.Items[I]);
end;
function IsMenuBarItem(aItem: TMenuItem; aMenu: TMainMenu): Boolean;
begin
Result := (aItem.Parent = aMenu.Items);
end;
Это решение обеспечивает правильную обработку меню без необходимости ручного тегирования элементов и работает в большинстве сценариев.
Определение элементов главного меню в Delphi без перебора всех пунктов.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.