Среда разработки My Visual Database хранит в себе множество тайн и загадок. Отчасти это связано с отсутствием детальной документации, которая бы позволила решить множество задач, в частности – кастомизацию системных форм ( “О программе”, “Авторизация” и другие) или их локализацию. В статье “Hello, world!” я опубликовал в качестве бонуса пару строчек, меняющих внешний вид стандартной формы логина. Настало время внимательно рассмотреть, что находится у MVDB под капотом.
В своей книге “Визуальное программирование” в главе “Урок анатомии” я приводил пример программного построения дерева, в которое загружались все формы и компоненты проекта.

На этот раз мы создадим инструмент, который можно использовать для исследования форм, компонентов и некоторых их свойств. И назовем его Component Explorer – Обозреватель компонентов.

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

Для этого понадобится небольшой скрипт:
uses 'ce.pas'; procedure Label_LinkClick(Sender: TObject); // клик по метке - открытие в браузере ссылки var tmpLabel:TLabel; begin tmpLabel := TLabel(Sender); OpenURL(tmpLabel.Caption); end; procedure Init; var tmpItem: TMenuItem; tmpItem2: TMenuItem; tmpForm: TAForm; begin tmpForm := frmMain; // главная форма приложения, на котором находится меню // создаём пункт меню для вызова обозревателя tmpItem := TMenuItem.Create( tmpForm ); tmpItem.Name := 'mniCE'; tmpItem.Caption := 'Component Explorer'; tmpItem.OnClick := @MenuItem_OnClick; // вставляем его в раздел "Файл" TMenuItem( tmpForm.FindComponent('mniFile') ).Insert(0,tmpItem); // для красоты отделяем разделителем tmpItem := TMenuItem.Create( tmpForm ); tmpItem.Name := 'mniBreak_1'; tmpItem.Caption := '-'; TMenuItem( tmpForm.FindComponent('mniFile') ).Insert(1,tmpItem); end; procedure MenuItem_OnClick (Sender: TObject; ); begin ShowCE; // открыть обозреватель компонентов end; begin Init; end.
Для хранения ссылки на форму обозревателя компонентов нам понадобится глобальная переменна, которую мы будем инициализировать при вызове процедуры ShowCE. Так как обозреватель отображает состояние форм и компонентов в режиме выполнения (run-time), а их состав и свойства могут всё время меняться, то заполнение списка форм производится при каждом запуске процедуры ShowCE с помощью процедуры Combo_GetFormList().
procedure ShowCE; // отобразить форму Component Explorer var tmpLabel: TLabel; tmpCombo: TdbComboBox; tmpGrid: TdbStringGridEx; begin // если формы ещё нет, то создать её if CEForm = nil then begin CEForm := TForm.Create(Application); CEForm.Name := 'frmComponentExplorer'; CEForm.Caption := ' Component Explorer'; CEForm.Width := 900; CEForm.Height := 465; CEForm.Position := poScreenCenter; CEForm.BorderStyle := bsDialog; CEForm.Font.Size := 12; // метка tmpLabel := TLabel.Create(CEForm); tmpLabel.Name := 'labForm'; tmpLabel.Parent := CEForm; tmpLabel.Top := 8; tmpLabel.Left := 8; tmpLabel.Caption := 'Формы приложения '; // комбик со списком форм tmpCombo := TdbComboBox.Create(CEForm); tmpCombo.Name := 'cmbFormList'; tmpCombo.Parent := CEForm; tmpCombo.Top := tmpLabel.Top + tmpLabel.Height + tmpLabel.Left; tmpCombo.Left := tmpLabel.Left; tmpCombo.Width := CEForm.ClientWidth - tmpCombo.Left*2; AssignEvents(tmpCombo); tmpCombo.dbOnChange := @FormList_OnChange; TCombobox(tmpCombo).Sorted := True; // tmpLabel := TLabel.Create(CEForm); tmpLabel.Name := 'labComponent'; tmpLabel.Parent := CEForm; tmpLabel.Left := tmpCombo.Left; tmpLabel.Top := tmpCombo.Top + tmpCombo.Height + tmpCombo.Left;; tmpLabel.Caption := 'Компоненты формы'; // tmpGrid := CreateGrid ('tgrComponent',CEForm ,tmpLabel.Left,tmpLabel.Top + tmpLabel.Height + tmpLabel.Left, (CEForm.ClientWidth - tmpLabel.Left*3) div 2 , CEForm.ClientHeight - (tmpLabel.Top + tmpLabel.Height + tmpLabel.Left) - tmpLabel.Left ,'Компонент','Класс'); AssignEvents(tmpGrid); tmpGrid.dbOnCellClick := @Grid_OnCellClick; // tmpGrid := CreateGrid ('tgrProperty',CEForm , tmpGrid.Left + tmpGrid.Width + tmpGrid.Left, tmpGrid.Top, tmpGrid.Width, tmpGrid.Height,'Свойство','Значение'); // tmpLabel := TLabel.Create(CEForm); tmpLabel.Name := 'labProperty'; tmpLabel.Parent := CEForm; tmpLabel.Left := tmpGrid.Left; tmpLabel.Top := tmpCombo.Top + tmpCombo.Height + tmpCombo.Left;; tmpLabel.Caption := 'Свойства компонента'; // tmpLabel := TLabel.Create(CEForm); tmpLabel.Name := 'labInfoLink'; tmpLabel.Parent := CEForm; tmpLabel.Caption := chr( $A9 ); // значек копирайта tmpLabel.Top := 8; tmpLabel.Left := CEForm.ClientWidth - tmpLabel.Width - tmpLabel.Top; tmpLabel.Font.Color := clGray; tmpLabel.Cursor := crHandPoint; tmpLabel.Hint := 'https://createmyvisualdatabaseapp.blogspot.com'; tmpLabel.OnClick := @Label_HintLinkClick; end; // заполнить комбик списком Combo_GetFormList( tmpCombo ); CEForm.Show; end;
Для создания табличного представления использована вспомогательная процедура CreateGrid(). Это также будет интересным примером программного создания табличного представления:
function CreateGrid(AName:string; AForm:TForm; ALeft, ATop, AWidth, AHeight: integer; ACaption1, ACaption2: string;):TdbStringGridEx; begin Result := TdbStringGridEx.Create(AForm); Result.Name := AName; Result.Parent := AForm; Result.Top := ATop; Result.Left := ALeft; Result.Width := AWidth; Result.Height := AHeight; Result.HeaderSize := 22; Result.RowSize := 22; Result.HeaderStyle := hsFlatBorders; Result.Options := goSelectFullRow + goGrid + GoHeader; Result.Columns.Clear; try Result.Columns.Add(TNxTextColumn); except end; Result.Columns[0].color := clWhite; Result.Columns[0].Width := (Result.Width - 20) div 2; Result.Columns[0].Header.Caption := ACaption1; Result.Columns[0].Header.Color := AForm.Color; try Result.Columns.Add(TNxTextColumn); except end; Result.Columns[1].color := clWhite; Result.Columns[1].Width := Result.Columns[0].Width; Result.Columns[1].Header.Caption := ACaption2; Result.Columns[1].Header.Color := AForm.Color; end;
Таблица “Свойства компонента” обновляется при каждом клике по таблице “Компоненты форм”. В первой версии обозревателя отображаются лишь только пара свойств, да и то не у всех компонентов. Это связано с тем, что процедура Grid_OnCellClick(), которая отвечает за заполнение таблицы свойств, для своей реализации требует приведение компонента к ожидаемому классу, а затем извлечение свойств через скрипт:
procedure Grid_OnCellClick (Sender: TObject; ACol, ARow: Integer); var tmpClassName: string; tmpComponent: TComponent; tmpForm: TForm; tmpComponentGrid: TdbStringGridEx; tmpPropertyGrid: TdbStringGridEx; begin tmpComponentGrid := TdbStringGridEx(Sender); tmpForm := TForm(TComponent(Sender).Owner); tmpPropertyGrid := TdbStringGridEx( tmpForm.FindComponent('tgrProperty') ); tmpComponent := TComponent( tmpComponentGrid.Cell[0, ARow ].Tag ); tmpPropertyGrid.ClearRows; tmpClassName := tmpComponent.ClassName; case tmpClassName of 'TLabel' : begin Grid_AddData( tmpPropertyGrid, 'Caption', TLabel(tmpComponent).Caption ); end; 'TdbLabel' : begin Grid_AddData( tmpPropertyGrid, 'Caption', TdbLabel(tmpComponent).Caption ); end; 'TLinkLabel' : begin Grid_AddData( tmpPropertyGrid, 'Caption', TLabel(tmpComponent).Caption ); end; 'TMenuItem' : begin Grid_AddData( tmpPropertyGrid, 'Caption', TMenuItem(tmpComponent).Caption ); end; 'TImage' : begin Grid_AddData( tmpPropertyGrid, 'Top', IntToStr(TImage(tmpComponent).Top) ); Grid_AddData( tmpPropertyGrid, 'Left', IntToStr(TImage(tmpComponent).Left) ); Grid_AddData( tmpPropertyGrid, 'Width', IntToStr(TImage(tmpComponent).Width) ); Grid_AddData( tmpPropertyGrid, 'Height', IntToStr(TImage(tmpComponent).Height) ); end; end; {case} end;
Поэтому для начала я решил ограничиться только свойствами Caption и координатами для картинки.
Вот как выглядит автопортрет формы обозревателя:

Исходный код проекта, а также книга “Визуальное программирование”, доступны в абонементном доступе к библиотеке “Визуальное программирование”.