The My Visual Database development environment holds a lot of secrets and mysteries. This is partly due to the lack of detailed documentation that would allow us to solve many problems, in particular – customization of system forms (“About the program”, “Authorization” and others) or their localization. In the article “Hello, world!” I posted as a bonus a couple of lines that change the appearance standard login form. It’s time to take a closer look at what MVDB has under the hood.
In my book Visual Programming in the Anatomy Lesson chapter, I gave an example programmatic construction of a tree into which all forms and components of the project were loaded.
The main feature of the Component Explorer is a fully programmatic implementation of its interface. This means that for use in your project, it is enough to add a source code file and write it in the uses section. After that, call the procedure for displaying the component browser. For example, you can add a browser call to the main menu of the program:
uses 'ce.pas';
procedure Label_LinkClick(Sender: TObject);
// click on the label - open the link in the browser
var
tmpLabel:TLabel;
begin
tmpLabel := TLabel(Sender);
OpenURL(tmpLabel.Caption);
end;
procedure Init;
var
tmpItem: TMenuItem;
tmpItem2: TMenuItem;
tmpForm: TAForm;
begin
tmpForm := frmMain; // the main form of the application on which the menu is located
// create a menu item to call the browser
tmpItem := TMenuItem.Create( tmpForm );
tmpItem.Name := 'mniCE';
tmpItem.Caption := 'Component Explorer';
tmpItem.OnClick := @MenuItem_OnClick;
// paste it in the "File" section
TMenuItem( tmpForm.FindComponent('mniFile') ).Insert(0,tmpItem);
// for beauty, separate with a separator
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; // open component browser
end;
begin
Init;
end.
Code language: Delphi (delphi)
To store a reference to the component browser form, we need a global variable that we will initialize when calling the ShowCE procedure. Since the browser displays the state of forms and components in run-time mode, and their composition and properties can change all the time, the list of forms is filled every time the ShowCE procedure is launched using the Combo_GetFormList() procedure.
procedure ShowCE;
// display form Component Explorer
var
tmpLabel: TLabel;
tmpCombo: TdbComboBox;
tmpGrid: TdbStringGridEx;
begin
// if the form doesn't exist yet, create it
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 := 'App Forms';
// form combo box
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 := 'Components';
//
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 := 'Properties';
//
tmpLabel := TLabel.Create(CEForm);
tmpLabel.Name := 'labInfoLink';
tmpLabel.Parent := CEForm;
tmpLabel.Caption := chr( $A9 ); // copyright icon
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;
// fill combo box
Combo_GetFormList( tmpCombo );
CEForm.Show;
end;
Code language: Delphi (delphi)
To create a table view, the CreateGrid() helper procedure was used. This would also be an interesting example of creating a table view programmatically:
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;
Code language: Delphi (delphi)
The “Component Properties” table is updated with each click on the “Form Components” table. In the first version of the browser, only a couple of properties are displayed, and even then not for all components. This is due to the fact that the Grid_OnCellClick() procedure, which is responsible for filling in the properties table, for its implementation requires casting the component to the expected class, and then extracting the properties through the script:
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;
Code language: Delphi (delphi)
Therefore, for a start, I decided to limit myself to the Caption properties and coordinates for the image. Here’s what the browser form’s self-portrait looks like:
The source code of the project, as well as the book “Visual Programming”, are available in subscription access to the “Visual Programming” library.