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

Создание динамического меню с помощью компонента Ribbon в Delphi 12: решение проблемы с отображением элементов

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

 

В современных приложениях часто требуется создавать динамические интерфейсы, элементы которых загружаются из базы данных. В этой статье мы рассмотрим, как правильно реализовать динамическое меню с использованием компонента TRibbon в Delphi 12, а также разберем решение распространенной проблемы, когда в группах отображается только один элемент.

Проблема динамического создания меню Ribbon

Как видно из исходного сообщения пользователя steelha, основная сложность заключалась в том, что при попытке динамического создания элементов меню в группах Ribbon отображался только один элемент, несмотря на то, что в базе данных их было несколько.

Анализ исходного кода

В первоначальной версии кода использовался следующий подход:
1. Очистка существующих вкладок Ribbon
2. Загрузка данных о главных вкладках меню из БД
3. Для каждой вкладки загрузка подгрупп
4. Для каждой подгруппы загрузка элементов

Основная ошибка заключалась в неправильной работе с ActionManager и ActionBarItem, что приводило к перезаписи элементов вместо их добавления.

Решение проблемы

Рассмотрим исправленную версию кода, которая решает проблему с отображением всех элементов меню.

Ключевые изменения в исправленном коде

  1. Очистка ActionBars перед началом процесса:
amMenu.ActionBars.Clear;

Это важно для предотвращения конфликтов с существующими элементами.

  1. Правильное создание и связывание ActionBarItem с TRibbonGroup:
CurrentActionBarItem := amMenu.ActionBars.Add;
CurrentActionBarItem.ActionBar := Group;
CurrentActionBarItem.Tag := PadreIDGrupo;
  1. Создание отдельных действий для каждого элемента меню:
Action := TAction.Create(Self);
Action.Caption := Trim(QryItems.FieldByName('titulo').Value);
Action.OnExecute := ItemClick;
Action.Tag := QryItems.FieldByName('menu_item_id').AsInteger;
  1. Создание различных типов кнопок в зависимости от настроек:
if QryItems.FieldByName('tipo_control').AsString = 'LARGEBUTTON' then
begin
  Bg := TRibbonLargeButtonControl.Create(Group);
  Bg.ActionClient := BarAction;
end
else if QryItems.FieldByName('tipo_control').AsString = 'SMALLBUTTON' then
begin
  Bs := TRibbonSmallButtonControl.Create(Group);
  Bs.ActionClient := BarAction;
end;

Полный рабочий пример

Вот полный код процедуры для динамического создания меню Ribbon:

procedure TfrmPrincipal.Button2Click(Sender: TObject);
var
  TabItem: TRibbonTabItem;
  Group: TRibbonGroup;
  BarAction: TActionClientItem;
  PadreIDTab, PadreIDGrupo: Integer;
  Action: TAction;
  i: Integer;
  GroupCaption: string;
  CurrentActionBarItem: TActionBarItem;
  Bg: TRibbonLargeButtonControl;
  Bs: TRibbonSmallButtonControl;
begin
  ribMenu.Tabs.Clear;
  amMenu.ActionBars.Clear;

  // Загрузка главных вкладок меню
  QryMenu.Close;
  QryMenu.SQL.Text := 'SELECT * FROM Menu WHERE (padre_id IS NULL OR padre_id = 0) AND activo = 1 ORDER BY orden ASC';
  QryMenu.Open;

  while not QryMenu.Eof do
  begin
    // Создание вкладки
    TabItem := ribMenu.Tabs.Add;
    TabItem.Caption := Trim(QryMenu.FieldByName('titulo').Value);
    PadreIDTab := QryMenu.FieldByName('menu_item_id').Value;

    // Загрузка групп для текущей вкладки
    QrySubMenu.Close;
    QrySubMenu.SQL.Text := 'SELECT * FROM Menu WHERE padre_id = :PadreIDTab AND activo = 1 ORDER BY orden ASC';
    QrySubMenu.ParamByName('PadreIDTab').Value := PadreIDTab;
    QrySubMenu.Open;

    while not QrySubMenu.Eof do
    begin
      if UpperCase(QrySubMenu.FieldByName('tipo').Value) = 'GRUPO' then
      begin
        GroupCaption := Trim(QrySubMenu.FieldByName('titulo').AsString);

        // Поиск существующей группы
        Group := nil;
        for i := 0 to TabItem.Page.ControlCount - 1 do
          if (TabItem.Page.Controls[i] is TRibbonGroup) and 
             (TRibbonGroup(TabItem.Page.Controls[i]).Caption = GroupCaption) then
          begin
            Group := TRibbonGroup(TabItem.Page.Controls[i]);
            Break;
          end;

        // Создание новой группы при необходимости
        if not Assigned(Group) then
        begin
          Group := TRibbonGroup.Create(ribMenu);
          Group.Parent := TabItem.Page;
          Group.Caption := GroupCaption;
        end;

        PadreIDGrupo := QrySubMenu.FieldByName('menu_item_id').Value;

        // Поиск или создание ActionBarItem для группы
        CurrentActionBarItem := nil;
        for i := 0 to amMenu.ActionBars.Count - 1 do
          if amMenu.ActionBars[i].ActionBar = Group then
          begin
            CurrentActionBarItem := amMenu.ActionBars[i];
            Break;
          end;

        if not Assigned(CurrentActionBarItem) then
        begin
          CurrentActionBarItem := amMenu.ActionBars.Add;
          CurrentActionBarItem.ActionBar := Group;
          CurrentActionBarItem.Tag := PadreIDGrupo;
        end;

        // Загрузка элементов для текущей группы
        QryItems.Close;
        QryItems.SQL.Text := 'SELECT * FROM Menu WHERE padre_id = :PadreIDGrupo AND activo = 1 ORDER BY orden';
        QryItems.ParamByName('PadreIDGrupo').Value := PadreIDGrupo;
        QryItems.Open;

        while not QryItems.Eof do
        begin
          if UpperCase(QryItems.FieldByName('tipo').Value) = 'ITEM' then
          begin
            // Создание действия
            Action := TAction.Create(Self);
            Action.Caption := Trim(QryItems.FieldByName('titulo').Value);
            Action.OnExecute := ItemClick;
            Action.Tag := QryItems.FieldByName('menu_item_id').AsInteger;

            // Добавление элемента в группу
            BarAction := CurrentActionBarItem.Items.Add as TActionClientItem;
            BarAction.Action := Action;
            BarAction.Caption := Action.Caption;

            // Создание соответствующего типа кнопки
            if QryItems.FieldByName('tipo_control').AsString = 'LARGEBUTTON' then
            begin
              Bg := TRibbonLargeButtonControl.Create(Group);
              Bg.ActionClient := BarAction;
            end
            else if QryItems.FieldByName('tipo_control').AsString = 'SMALLBUTTON' then
            begin
              Bs := TRibbonSmallButtonControl.Create(Group);
              Bs.ActionClient := BarAction;
            end;
          end;
          QryItems.Next;
        end;
        QryItems.Close;
      end;
      QrySubMenu.Next;
    end;
    QrySubMenu.Close;
    QryMenu.Next;
  end;

  // Обновление отображения Ribbon
  if ribMenu.Tabs.Count >= 2 then
  begin
    ribMenu.TabIndex := 1;
    ribMenu.TabIndex := 0;
  end;
  ribMenu.Refresh;
  QryMenu.Close;
end;

Обработка кликов по элементам меню

Для обработки действий при нажатии на элементы меню используется общий обработчик:

procedure TfrmPrincipal.ItemClick(Sender: TObject);
var
  Action: TAction;
  MenuItemID: Integer;
begin
  if Sender is TAction then
  begin
    Action := TAction(Sender);
    MenuItemID := Action.Tag;

    // Здесь можно реализовать логику обработки нажатия
    ShowMessage('Выбрано меню с ID: ' + IntToStr(MenuItemID) + 
                ', заголовок: ' + Action.Caption);
  end;
end;

Альтернативное решение

Вместо использования ActionManager можно реализовать более легковесное решение, создавая кнопки напрямую:

procedure TfrmPrincipal.CreateRibbonMenuDirectly;
var
  TabItem: TRibbonTabItem;
  Group: TRibbonGroup;
  Button: TRibbonLargeButton;
  PadreIDTab, PadreIDGrupo: Integer;
begin
  ribMenu.Tabs.Clear;

  QryMenu.Close;
  QryMenu.SQL.Text := 'SELECT * FROM Menu WHERE (padre_id IS NULL OR padre_id = 0) AND activo = 1 ORDER BY orden ASC';
  QryMenu.Open;

  while not QryMenu.Eof do
  begin
    TabItem := ribMenu.Tabs.Add;
    TabItem.Caption := Trim(QryMenu.FieldByName('titulo').Value);
    PadreIDTab := QryMenu.FieldByName('menu_item_id').Value;

    QrySubMenu.Close;
    QrySubMenu.SQL.Text := 'SELECT * FROM Menu WHERE padre_id = :PadreIDTab AND activo = 1 ORDER BY orden ASC';
    QrySubMenu.ParamByName('PadreIDTab').Value := PadreIDTab;
    QrySubMenu.Open;

    while not QrySubMenu.Eof do
    begin
      if UpperCase(QrySubMenu.FieldByName('tipo').Value) = 'GRUPO' then
      begin
        Group := TRibbonGroup.Create(ribMenu);
        Group.Parent := TabItem.Page;
        Group.Caption := Trim(QrySubMenu.FieldByName('titulo').AsString);
        PadreIDGrupo := QrySubMenu.FieldByName('menu_item_id').Value;

        QryItems.Close;
        QryItems.SQL.Text := 'SELECT * FROM Menu WHERE padre_id = :PadreIDGrupo AND activo = 1 ORDER BY orden';
        QryItems.ParamByName('PadreIDGrupo').Value := PadreIDGrupo;
        QryItems.Open;

        while not QryItems.Eof do
        begin
          if UpperCase(QryItems.FieldByName('tipo').Value) = 'ITEM' then
          begin
            Button := TRibbonLargeButton.Create(Group);
            Button.Parent := Group;
            Button.Caption := Trim(QryItems.FieldByName('titulo').Value);
            Button.Tag := QryItems.FieldByName('menu_item_id').AsInteger;
            Button.OnClick := DirectButtonClick;
          end;
          QryItems.Next;
        end;
        QryItems.Close;
      end;
      QrySubMenu.Next;
    end;
    QrySubMenu.Close;
    QryMenu.Next;
  end;
  QryMenu.Close;
end;

procedure TfrmPrincipal.DirectButtonClick(Sender: TObject);
begin
  if Sender is TRibbonLargeButton then
    ShowMessage('Нажата кнопка: ' + TRibbonLargeButton(Sender).Caption + 
                ', ID: ' + IntToStr(TRibbonLargeButton(Sender).Tag));
end;

Советы по улучшению кода

  1. Вынесение SQL-запросов в отдельный модуль: Для больших проектов лучше вынести все запросы к базе данных в отдельный модуль или класс.

  2. Кэширование данных меню: Если меню редко меняется, можно загружать его один раз при старте приложения и кэшировать.

  3. Использование стилей: Для единообразия интерфейса применяйте стили к элементам Ribbon.

  4. Ленивая загрузка: Для больших меню можно реализовать ленивую загрузку элементов при активации вкладки.

  5. Логирование ошибок: Добавьте обработку ошибок при загрузке меню из базы данных.

Заключение

В этой статье мы рассмотрели решение проблемы динамического создания меню с использованием компонента TRibbon в Delphi 12. Основные ключевые моменты:
- Правильная очистка существующих элементов перед созданием новых
- Корректная работа с ActionManager и ActionBarItem
- Создание отдельных действий для каждого элемента меню
- Альтернативный подход без использования ActionManager

Представленные решения помогут вам создавать гибкие и динамические интерфейсы, элементы которых загружаются из базы данных, что особенно важно для корпоративных приложений с возможностью настройки меню под конкретные нужды пользователей.

Создано по материалам из источника по ссылке.

Описание процесса создания динамического меню с использованием компонента Ribbon в Delphi 12, включая решение проблемы отображения всех элементов в группах.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




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


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


реклама


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

Время компиляции файла: 2024-12-22 20:14:06
2025-07-15 14:45:41/0.0064339637756348/0