Continuation of the article “Three main buttons”

In addition to the main mechanisms described earlier, the program has a number of small procedures and functions that carry out very important tasks.

Edit parentID

My Visual Database does not have a standard component for editing a field that refers to a parent element, so for this function a bunch of two visual components are used: a drop-down list for rendering the selected value and editing, and a text input field for writing / reading the value by means of MVDB automation .

Let’s consider the implementation in more detail on the example of the efmTask task editing form (forms for editing efmFuncProc functions and efmClassType classes work similarly).

Since the drop-down list should display data from the same table that we are editing (there is no reference to an external table in the data structure), when setting the ForeingKey (1) field, you must manually enter the task value, and then select the name of the displayed field name. In the case of tasks, only a group can be a parent element, so the expression isGroup = 1 must be added to the Filter (2) field.

For the edit field, set the properties of binding to database fields (1) to the appropriate values, and make the element itself invisible (2).

When setting the save button, the combobox remains disabled (1), and the input field is placed in the list of components involved in saving data (2).

It remains to synchronize the work of these two interface elements. When opening the form, we will adjust the value in the drop-down list in accordance with the value in the edit field, and vice versa when saving. To do this, we need three procedures – event handlers: for displaying the edit form (efmTask.onShow), for changing the value in the drop-down list (efmTask.cmbParent.OnChange) and for clicking save data buttons (efmTask.btnSave.OnClick).

procedure efmTask_OnShow (Sender: TObject; Action: string);
begin
  if Action = 'NewRecord' then
  begin
    // if a tree is open, then add a parent
    if frmMain.pgcTaskView.ActivePage = frmMain.tshTaskTree then
      efmTask.edtParentID.Value := frmTask_Tree.trvMain.dbItemID;
  end;
  // update value visualization
  efmTask.cmbParent.dbItemID := trunc( efmTask.edtParentID.Value );
  efmTask.edtName.SetFocus;
end;
 
procedure efmTask_cmbParent_OnChange (Sender: TObject);
begin
  // move the value to the saved field
  efmTask.edtParentID.Value := efmTask.cmbParent.dbItemID;
end;
 
procedure efmTask_btnSave_OnClick (Sender: TObject; var Cancel: boolean);
begin
  // so that NULL is stored instead of -1
  if efmTask.edtParentID.Text = '-1' then
    efmTask.edtParentID.Text := '';
end;Code language: Delphi (delphi)

Adding a new example

On the forms for editing a class, function, event, property and task, there is a drop-down list with examples (1) that can be linked to the description of the entity. And next to it is a button for creating a new description (2). After creating a new description using this button, it is automatically bound to the entity. To do this, there is a button click handler, the same for all editing forms.

procedure UserApp_btnAddExample_OnClick(Sender: TObject; var Cancel: boolean);
var
  tmpForm:TAForm;
  tmpCombo: TdbComboBox;
begin
  CForm(Sender,tmpForm);
  // find dropdown list by name
  FindC(tmpForm,'cmbExample',tmpCombo);
  efmExample.NewRecord('example');
  // set added value
  tmpCombo.dbItemID := efmExample.btnSave.Tag;
end;Code language: Delphi (delphi)
Note. In this procedure, the convention for naming components is used, namely: the drop-down list should be called cmbExaple. More generally, the convention sounds like this: the name of an element on the edit form must consist of a three-character prefix of the element class and the name of the field to edit which it is 

Active element definition

The program has a global variable ActiveGrid, which stores a link to the table or tree that the user is currently working with: viewing or editing. Such an element visually differs from the rest by a frame (more precisely, by its absence), which is noticeable with any application design style. The ActiveGrid value is set via the UserApp_SetActiveGrid() procedure, which is used as an onClick event handler for all tables and trees.

procedure UserApp_SetActiveGrid(Sender: TObject);
// set active grid
// used as OnClick handler for table and tree
var
  tmpGrid: TdbStringGridEx;
begin
  tmpGrid := TdbStringGridEx(Sender);
  // if the grid has changed, then
  if ActiveGrid <> tmpGrid then
  begin
    // if the grid was installed earlier, then
    if ActiveGrid <> nil then
      // deselect - return to standard view
      ActiveGrid.BorderStyle := bsSingle;
    ActiveGrid := tmpGrid;
    // if the grid is installed, then
    if ActiveGrid <> nil then
      // highlight it, change its border
      ActiveGrid.BorderStyle := bsNone;
  end;
end;Code language: Delphi (delphi)

Updating detail and description

When you select a record in any table, the name of the entity and its description are displayed in the description display line on the main form. It is also necessary to update the filtering for dependent elements (tables). In particular, properties, methods, and events are class-specific, while function parameters are function-specific. To do this, you will need the UserApp_UpdateDD() procedure, which can be called as an onCellClick event handler, as well as the UserApp_Grid_OnKeyUp() procedure, which provides the above functionality when navigating the table

procedure UserApp_UpdateDD(Sender: TObject; ACol, ARow: Integer);
// update description (description) and detail (detail)
// used as an onCellClick event handler
var
  tmpGrid:TdbStringGridEx;
  tmpTableName:string;
  tmpID:string
  tmpSQL:string;
  tmpDesk:string;
  tmpForm:TAForm;
begin
  CForm(Sender,tmpForm);
  tmpGrid := TdbStringGridEx(Sender);
  tmpTableName := Grid_GetTableName(tmpGrid);
  tmpID := IntToStr(tmpGrid.dbIndexToID(ARow));
  // update description
  tmpSQL := 'SELECT name || "-" || description FROM '+tmpTableName+' WHERE id = '+tmpID;
  tmpDesk := SQLExecute(tmpSQL);
  // if class navigation is open
  if frmmain.pgcNavigation.ActivePage = frmMain.tshClass then
  case tmpTableName of
  'property','funcProc','classEvent': begin
    // for property, method and event
    if frmMain.pgcClassView.ActivePage = frmMain.tshClassList then
      tmpGrid := frmClassType_List.tgrMain
    else
      tmpGrid := frmClassType_Tree.trvMain;
    // add class name
    tmpDesk := tmpGrid.Cells[0,tmpGrid.SelectedRow]+'.'+tmpDesk;
  end;
  end;
  frmMain.labDesription.Caption := tmpDesk;
  // update delization
  case tmpForm of
  frmFuncProc_FunctionList,frmFuncProc_FunctionList : Form_UpdateData( frmFuncProcParam_Function );
  frmFuncProc_Method : Form_UpdateData( frmFuncProcParam_Method );
  frmClassType_List, frmClassType_Tree: begin
    Form_UpdateData( frmProperty );
    Form_UpdateData( frmFuncProc_Method );
    Form_UpdateData( frmClassEvent );
  end;
  end;
end;
 
procedure UserApp_Grid_OnKeyUp(Sender: TObject; var Key: Word; Shift, Alt, Ctrl: boolean);
// update when navigating with keys
begin
  UserApp_UpdateDD(Sender, 0, TdbStringGridEx(Sender).SelectedRow);
end;Code language: Delphi (delphi)

Loading an example from the database

A procedure that loads an example based on the information contained in the ActiveGrid variable.

procedure UserApp_LoadExample;
// loading an example from the database
var
  tmpData:string;
  tmpTableName:string;
  tmpTableID:string;
begin
  frmExampleView.edtCaption.Text := '';
  frmExampleView.redExample.Clear;
  tmpTableName := Grid_GetTableName( ActiveGrid );
  if tmpTableName <> '' then
  begin
    tmpTableID := IntToStr(ActiveGrid.dbItemID);
    try
      // get existing ID
      IDExample := SQLExecute('SELECT id_example FROM '+tmpTableName+' WHERE id = '+tmpTableID );
    except // and if the ID does not exist, it will be -1
      IDExample := -1;
    end;
    // load formatted text
    frmExampleView.redExample.TextRTF := SQLExecute('SELECT detail FROM example WHERE id = '+IntToStr(IDExample));
    // load title
    frmExampleView.edtCaption.Text := SQLExecute('SELECT caption FROM example WHERE id = '+IntToStr(IDExample));
  end;
end;Code language: Delphi (delphi)

Procedure names

Since there are a lot of procedures in the program, I decided to change their names, adding UserApp_ prefixes to some of them, thus making it clear that these procedures or functions contain common functionality that will be relevant only for this application. Below is a list of all prefixes:

  • App_ – universal procedures relevant for any application
  • Grid_ – procedures that complement the functionality of a table view
  • Form_ – procedures related to forms
  • Tree_ – procedures and functions that extend the functionality of trees
  • Splitter_ – procedures for creating and processing a separator
  • IniFile_ – procedures and functions for working with the initialization file
  • UserApp_ – general procedures and functions related to this application

Because of this, the names of some procedures in the above listings may not match the names of the same procedures in the final application. In the future, I plan to separate the procedures into different modules, since the size of the source text was more than 1100 rows. More information about using modules is written in my book “Visual Programming”, in Chapter 4. Putting things in order.

Result

The first version of the program is ready. It is still quite raw and most likely contains errors, but I plan to use it for myself, filling the database with information. And I invite my readers to test. For those who have registered subscription access to the “Visual Programming” library, the current project files and all future updates are available.

Links

Leave a Reply

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