Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Оптимизация проверки TMenuItem в Delphi: определение элементов панели меню без перебора всех пунктов

Delphi , Компоненты и Классы , Меню

Определение элементов главного меню в 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. Этот подход:

  1. Не требует перебора всех элементов меню
  2. Работает быстро и эффективно
  3. Подходит для большинства стандартных сценариев

Для более сложных случаев, таких как 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




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Меню ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-06-27 01:47:28/0.0034728050231934/0