Среда разработки 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 и координатами для картинки.

Вот как выглядит автопортрет формы обозревателя:

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *