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.

This time we will create a tool that can be used to explore shapes, components and some of their properties. And let’s call it Component Explorer.

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:

To do this, you need a small script:
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.

Leave a Reply

Your email address will not be published. Required fields are marked *