Delphi и OpenToolsAPI. Работаем с главным меню.
Мы уже рассмотрели простейшие операции с Open Tools API. Настало время для более серьёзных вещей. Сегодня мы получим полный доступ к главному меню IDE, научимся добавлять и удалять пункты в любые его разделы и создадим небольшой прикол 
Но сначала поговорим вот о чём. При неумелом использовании Open Tools API можно повредить среду разработки, получить кучу Acces Violation'ов и довести IDE до такого состояния, что придётся её переустанавливать. Поэтому необходимо знать, как отлаживать Open Tools API. И Delphi это позволяет. После того, как мы создали проект, нужно проделать следующие действия. В менеджере проектов нажимаем правой кнопкой на пакет, в меню нажимаем Build (не Install!!!).
После этого нажимаем в главном меню Run - Parametres, и в поле Host application ставим Delphi, то есть bds.exe. После этого можно будет нажать клавишу F9 и отлаживать пакет как обычную программу.
Приступим к кодингу. Создадим новый пакет, в него добавим юнит. Теперь в раздел uses нужно дополнительно добавить модуль Menus. Создадим заготовку такую же, как в первой части, но с небольшими изменениями:
unit Wizard1; interface uses ToolsAPI, dialogs, menus; type TThWizard1 = class(TNotifierObject, IOTAWizard, IOTANotifier) function GetName: string; function GetAuthor: string; function GetIDString: string; function GetState: TWizardState; procedure Execute; //Теперь нам не нужен метод GetMenuText end; procedure register; implementation procedure register; begin RegisterPackageWizard(TThWizard1.Create); end; { TThWizard1 } procedure TThWizard1.Execute; begin //Оставляем пустым end; function TThWizard1.GetAuthor: string; begin result := 'Darth_Vaider';//здесь всё, как в первой части end; function TThWizard1.GetIDString: string; begin result := 'Darth_Vaider.ThWizard1';//как в первой части end; function TThWizard1.GetName: string; begin result := 'ThWizard1';//Как в первой части end; function TThWizard1.GetState: TWizardState; begin result := [];//Возвращаем пустое множество. end; end.
Далее нам понадобятся конструктор и деструктор. Конструктор выполняется при первой установке пакета и впоследствии при каждом запуске среды. Деструктор выполняется при закрытии среды. К сожалению, мне не удалось выяснить, что выполняется при деинсталляции пакета, но это определённо не деструктор. Поэтому я не знаю, как потом за собой прибираться
Абсолютно все пункты меню в делфи реализованы с помощью таких пакетов. Это видно при загрузке IDE: внизу есть строка, гласящая, что "Loading package: *.bpl". Это мы наблюдаем загрузку как главного меню, так и всех остальных компонентов среды. Поэтому в деструкторе можно удалить себя из меню, но это не является обязательным.
Итак, создаём конструктор.
constructor TThWizard1.Create; begin end;
Здесь нам понадобятся две переменные, одна будет непосредственно нашим пунктом меню, а вторая - родительским пунктом.
var Menu, Item: TMenuItem;
Наш собственный пункт нужно создать, дать ему имя и caption и обработчик события OnClick. Родительский пункт создавать не нужно, он уже существует, нужно лишь получить его расположение. Это делается так:
constructor TThWizard1.Create; var Menu, Item: TMenuItem; begin Item := TMenuItem.Create(nil); //Создаём наш пункт меню Item.Name := 'test1'; //даём ему имя Item.Caption := 'TestItem1'; //Устанавливаем текст Item.OnClick := Click; //устанавливаем обработчик события Menu := (BorlandIDEServices as INTAServices).MainMenu.Items[1]; { Я затрудняюсь сказать, что такое BorlandIDEServices. Могу утверждать только то, что через эту переменную мы получаем доступ ко всем компонентам среды. Например, методом MainMenu мы получаем главное меню среды. Выбрав в нём первый пункт, мы получаем меню Edit. } Menu.Insert(0, Item); //Вставляем наш пункт в начало меню Edit. end;
Теперь займёмся деструктором. В нём немного приберёмся за собой, то есть удалим наш пункт.
destructor TThWizard1.Destroy; var Menu: TMenuItem; begin Menu := (BorlandIDEServices as INTAServices).MainMenu.Items[1];//точно так же получаем меню Edit Menu.Delete(0); //и удаляем из него наш пункт end;
Хотелось бы заметить, что не следует вслепую удалять пункты, как показано в этом примере. Мы не знаем точно, удалось ли нам ранее добавить наш пункт, поэтому можем случайно удалить и другие пункты. Чтобы избежать этой ситуации, следует просмотреть в цикле всё меню и каждый пункт сравнивать по имени или по caption с нашим пунктом. И, если он был найден, то только в этом случае можно его удалять.
Теперь нам осталось только сделать обработчик события для нашего пункта.
procedure TThWizard1.Click(Sender: TObject); begin //Здесь пишем всё, что нам заблагорассудится end;
Но я предлагаю в этом месте сделать небольшой прикол. Добавим форму в наш пакет (в менеджере проектов правой кнопкой нажимаем Contains - Add new - Form). Теперь добавим нашу форму в раздел uses, и в обработчике события пишем код:
procedure TThWizard1.Click(Sender: TObject); begin if not assigned (Form1) then Form1 := TForm1.Create(nil); Form1.Show; end;
Попробуем запустить наш проект. Мы увидим, как в меню Edit добавился новый пункт, по нажатию на который открывается наша форма. А в нашу форму мы поместим клон главного меню. Добавим к форме TMainMenu (я это делал вручную, поэтому и создавать меню буду сам).
Теперь в обработчике события OnCreate формы пишем код:
procedure TForm1.FormCreate(Sender: TObject); var MenuItem: TMenuItem; MenuIDE: TMainMenu; Items: array of TMenuItem; ItemsA: array of TMenuItem; i, j: integer; begin MainMenu := TMainMenu.Create(self); //Создаём меню MenuIDE := (BorlandIDEServices as INTAServices).MainMenu; //Получаем меню среды MainMenu.Images := MenuIDE.Images; //Получаем ImageList главного меню for i := 0 to MenuIDE.Items.Count - 1 do //проходим по всем пунктам begin MenuItem := MenuIDE.Items[i]; SetLength(Items, Length(Items)+1); //добавляем текущий пункт в массив Items[i] := TMenuItem.Create(nil); Items[i].Name := MenuItem.Name; //и копируем все его свойства Items[i].Caption := MenuItem.Caption; Items[i].ImageIndex := MenuItem.ImageIndex; MainMenu.Items.Add(Items[i]); for j := 0 to MenuItem.Count - 1 do //Для всех пунктов смотрим вложения begin SetLength(ItemsA, Length(ItemsA)+1); ItemsA[j] := TMenuItem.Create(nil); ItemsA[j].Name := MenuItem[j].Name; ItemsA[j].Caption := MenuItem[j].Caption; //и точно также копируем все их свойства ItemsA[j].ImageIndex := MenuItem[j].ImageIndex; ItemsA[j].OnClick := MenuItem[j].OnClick; ItemsA[j].ShortCut := MenuItem[j].ShortCut; ItemsA[j].Enabled := MenuItem[j].Enabled; Items[i].Insert(j, ItemsA[j]); end; end; end;
Теперь при запуске вы увидите форму с клоном главного меню Делфи. Недостатком этого кода является то, что я не смотрел третий уровень вложенности. Поэтому меню File - New останется без потомков.
Можно спросить: почему я копировал все свойства, когда можно было использовать метод Assign? Это - баг Open Tools API. Когда я пытался его использовать, я получал странную ошибку "Cannot assign TMenuItem to TMenuItem". Поэтому я решил поочерёдно копировать все свойства.
Всё на сегодня! Удачи!
| Вложение | Размер |
|---|---|
| example2.zip | 2.09 КБ |
- Darth_Vaider's блог
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
- 1177 просмотров



Комментарии
8 комментария(ев)Дата: Втр, 25/10/2011 - 20:42
ни одного коммента.. все в шоке )))
Дата: Втр, 25/10/2011 - 21:29
Ну почему? Комменты были. Только ту тему удалили почему-то. И опубликовали заново. Там и комменты все пропали. Их было не мало.
Дата: СР, 26/10/2011 - 01:49
В смысле? Какие комменты?
Дата: СР, 26/10/2011 - 02:17
Блин. Ну ты выкладывал статью. Я там писал еще, что ты неверно посчитал количество методов. Еще про visualstudio тебе ссылки кидали. Потом вместо того, чтобы обновить статью, она очевидно была сделана через удаление предыдущей. Естественно все комментарии к прошлой статье были тоже потеряны.
Дата: СР, 26/10/2011 - 15:50
Там отдельная тема ) она тоже видна )
Дата: СР, 26/10/2011 - 16:14
Кстати, как думаете, что будет, если написать
?
Дата: СР, 26/10/2011 - 17:18
Va-Bank, это продолжение
Дата: СР, 26/10/2011 - 21:27
Надо же. Я даже не заметил. Думал что прошлую удалили