My Visual Database has the ability to manually adjust column widths for each table view. But in some cases, you want more intelligent behavior: automatic alignment of the data width or alignment of the width when the table size is changed. Using scripts you can achieve the desired effect.

I bring to the attention of readers several scripts that will allow the user to independently select the level of automation for column alignment he needs from four available options:

  • Default – manual alignment
  • Proportional – equal width for all columns, retained when the table size is changed
  • According to data – the width is set so that all data is displayed entirely, without cropping
  • Stretch last – the last column is stretched and fills the visible area of the table

Control

Management will be carried out through the grid pop-up menu, so first of all you will need the Grid_AddColWidthMenu() procedure, which creates the necessary menu items. During the creation process, we determine which setting was previously set by reading data from the settings.ini file using the IniFile_Read() function from the IniFile.pas module.

const
   // modes for automatically setting column widths
   GRID_CW_DEFAULT_NAME = 'Default'; // default - manual installation
   GRID_CW_PROPORTIONAL_NAME = 'Proportional'; // all visible columns are of equal width
   GRID_CW_DATA_NAME = 'Data'; // column width according to data width
   GRID_CW_FILL_NAME = 'Fill'; // stretch the last column to fill the grid

   GRID_CW_MIN = 20; // minimum column width; used in GRID_CW_FILL_NAME mode

   GRID_EXTRA_DATA_SECTION = 'GridsExt'; // name of the section for storing additional grid parameters
   GRID_CW_PARAM_NAME = 'CWMode'; // name of the parameter for storing the column alignment mode

procedure Grid_AddColWidthMenu(AGrid: TdbStringGridEx);
// add column behavior control items to the pop-up menu
var
   tmpMenu: TPopupMenu;
   tmpItem: TMenuItem;
   tmpSubItem: TMenuItem;
   tmpForm: TForm;
   tmpMode:string;
   tmpParamName: string;
begin
   CForm(AGrid, tmpForm);
   // read the mode from the settings
   tmpParamName := tmpForm.Name +'.'+ AGrid.Name+'.'+GRID_CW_PARAM_NAME;
   tmpMode := IniFile_Read(GRID_EXTRA_DATA_SECTION,tmpParamName,GRID_CW_DEFAULT_NAME); // if there is no data, then set the default mode
   tmpMenu := AGrid.PopupMenu; // we will work with the grid menu
   // add visual separator
   tmpItem := TMenuItem.Create(tmpForm);
   tmpItem.Caption := '-';
   tmpMenu.Items.Add(tmpItem);
   // add switch to tree mode, default
   tmpItem := TMenuItem.Create(tmpForm);
   tmpItem.Caption := 'Column width';
   tmpMenu.Items.Add(tmpItem);
   // add switch to list mode
   tmpSubItem := TMenuItem.Create(tmpForm);
   tmpSubItem.Name := T_MENU_ITEM + GRID_CW_DEFAULT_NAME+'_'+AGrid.Name;
   tmpSubItem.Caption := 'Default';
   tmpSubItem.Checked := tmpMode = GRID_CW_DEFAULT_NAME;
   tmpSubItem.RadioItem := True;
   tmpSubItem.GroupIndex := 1;
   tmpSubItem.onClick := 'Grid_CW_Click';
   tmpItem.Add(tmpSubItem);
   //
   tmpSubItem := TMenuItem.Create(tmpForm);
   tmpSubItem.Caption := 'Proportional';
   tmpSubItem.Checked := tmpMode = GRID_CW_PROPORTIONAL_NAME;
   tmpSubItem.RadioItem := True;
   tmpSubItem.GroupIndex := 1;
   tmpSubItem.Name := T_MENU_ITEM + GRID_CW_PROPORTIONAL_NAME+'_'+AGrid.Name;
   tmpSubItem.onClick := 'Grid_CW_Click';
   tmpItem.Add(tmpSubItem);
   //
   tmpSubItem := TMenuItem.Create(tmpForm);
   tmpSubItem.Caption := 'According to data';
   tmpSubItem.Checked := tmpMode = GRID_CW_DATA_NAME;
   tmpSubItem.RadioItem := True;
   tmpSubItem.GroupIndex := 1;
   tmpSubItem.Name := T_MENU_ITEM + GRID_CW_DATA_NAME+'_'+AGrid.Name;
   tmpSubItem.onClick := 'Grid_CW_Click';
   tmpItem.Add(tmpSubItem);
   //
   tmpSubItem := TMenuItem.Create(tmpForm);
   tmpSubItem.Caption := 'Stretch last';
   tmpSubItem.Checked := tmpMode = GRID_CW_FILL_NAME;
   tmpSubItem.RadioItem := True;
   tmpSubItem.GroupIndex := 1;
   tmpSubItem.NAME := T_MENU_ITEM + GRID_CW_FILL_NAME+'_'+AGrid.Name;
   tmpSubItem.onClick := 'Grid_CW_Click';
   tmpItem.Add(tmpSubItem);
end;Code language: PHP (php)

For all four menu items, one handler is used – Grid_CW_Click(), which promptly changes the width of the columns and saves the selected mode to the settings.ini file.

procedure Grid_CW_Click(Sender: TObject);
// processing menu item selection - change column width
var
   tmpItem: TMenuItem;
   tmpMenu: TPopupMenu;
   tmpGrid: TdbStringGridEx;
   tmpName: string;
   tmpParamName: string;
   tmpForm: TForm;
begin
   CForm(Sender,tmpForm);
   tmpItem := TMenuItem(Sender);
   tmpMenu := TPopupMenu(tmpItem.GetParentMenu);
   tmpGrid := TdbStringGridEx(tmpMenu.PopupComponent);
   tmpName := DeleteClassName( DeleteSuffix( tmpItem.Name ) );
   tmpItem.Checked := True; // select a menu item.
   // in some cases an immediate reaction (leveling) is required
   case tmpName of
   GRID_CW_PROPORTIONAL_NAME: Grid_OnResize(tmpGrid);
   GRID_CW_DATA_NAME: tmpGrid.BestFitColumns(bfBoth);
   GRID_CW_FILL_NAME: Grid_OnResize(tmpGrid);
   end; {case}
   tmpParamName := tmpForm.Name +'.'+ tmpGrid.Name+'.'+GRID_CW_PARAM_NAME;
   // save the setting in the parameters
   IniFile_Write(GRID_EXTRA_DATA_SECTION,tmpParamName,tmpName);
end;Code language: PHP (php)

Alignment

Depending on the type of alignment, it must be done when the data is updated and/or when the grid size changes. Therefore, alignment algorithms are found in two procedures:

  • Grid_OnChange() – data change (by value)
  • Grid_OnResize() – resizing (proportional alignment and filling)

In the Grid_OnChange() handler, the BestFitColumns() grid method is called, which actually performs the desired action. The method has a parameter – alignment type TBestFitMode:

  • bfCells – based on data in cells
  • bfHeader – by header labels
  • bfBoth – by data and by title
procedure Grid_OnChange(Sender: TObject);
// default handler with support for data alignment
var
   tmpGrid:TdbStringGridEx;
   tmpForm:TForm;
begin
   CForm(Sender,tmpForm);
   tmpGrid := TdbStringGridEx(Sender);
   // data alignment
   if Grid_CW_IsMode(tmpGrid,GRID_CW_DATA_NAME) then
     tmpGrid.BestFitColumns(bfBoth)
   else // other alignments
   if Grid_CW_IsMode(tmpGrid,GRID_CW_PROPORTIONAL_NAME) or Grid_CW_IsMode(tmpGrid,GRID_CW_FILL_NAME) then
     Grid_OnResize(tmpGrid);
end;Code language: JavaScript (javascript)

The Grid_OnResize() handler implements the remaining types of alignment, including the special case when the grid has one column. In this case, the column is stretched to the entire width of the grid, even if a different autosizing type is set. This seemed reasonable to me, but I did not create a separate control option for this. But for convenience, I moved the algorithm for determining the number of visible columns into a separate function – Grid_VisibledColCount(). We also needed a function with which you can determine whether a particular auto-alignment mode is enabled – Grid_CW_IsMode().

function Grid_VisibledColCount(AGrid:TdbStringGridEx):integer;
// returns the number of visible columns in the grid
var
   i:integer;
begin
   Result := 0;
   for i:=0 to AGrid.Columns.Count - 1 do
     if AGrid.Columns[i].Visible then
       inc(Result);
end;

function Grid_CW_IsMode(AGrid:TdbStringGridEx; AModeName:string):boolean;
// determine whether the specified mode is enabled for the table
var
   tmpForm: TForm;
   tmpItem: TMenuItem;
   tmpName: string;
begin
   // function looks for a checker at the corresponding menu element
   CForm(AGrid,tmpForm);
   tmpName := T_MENU_ITEM+AModeName+'_'+AGrid.Name;
   FindC(tmpForm,tmpName,tmpItem,False);
   if tmpItem <> nil then
   begin // if the element is found, then return the checker
     Result := tmpItem.Checked;
   end
   else // if not found for some reason, then default alignment
   begin
     Result := AModeName = GRID_CW_DEFAULT_NAME;
   end;
end;

procedure Grid_OnResize(Sender: TObject);
// handler responsible for automatically sizing columns for the table
var
   tmpGrid: TdbStringGridEx;
   tmpColumnCount: integer;
   tmpColWidth: integer;
   tmpCol:integer;
   i:integer;
   tmpGridLineWidth: integer;
begin
   tmpGrid := TdbStringGridEx(Sender);
   tmpGridLineWidth := 1; // TODO: the goGrid and GridLineStyle properties affect this value and can make it zero. add an algorithm for determining the actual thickness of the vertical marking line
   // auto-width of columns is affected by the number of columns and the selected alignment mode
   if tmpGrid.Columns.Count = 1 then // only if one column
     tmpGrid.Columns[0].Width := tmpGrid.ClientWidth // stretch it to the entire client area of the table
   else // if there are many columns, then we follow the rules
   if Grid_CW_IsMode(tmpGrid,GRID_CW_PROPORTIONAL_NAME) then
   begin // proportional to the number of visible columns
     tmpColumnCount := Grid_VisibledColCount(tmpGrid);
     tmpColWidth := trunc( tmpGrid.ClientWidth / tmpColumnCount );
     for i:=0 to tmpGrid.Columns.Count - 1 do
       if tmpGrid.Columns[i].Visible then
         tmpGrid.Columns[i].Width := tmpColWidth;
   end
   else // stretch the last visible column
   if Grid_CW_IsMode(tmpGrid,GRID_CW_FILL_NAME) then
   begin // stretch the last column to fill the grid
     // find the last visible column
     for i := tmpGrid.Columns.Count - 1 downto 0 do
       if tmpGrid.Columns[i].Visible then
       begin
         tmpCol := i;
         Break
       end;
     // count the width of all columns except the last visible one
     tmpColWidth := 0;
     for i:=0 to tmpCol - 1 do
       if tmpGrid.Columns[i].Visible then
         inc(tmpColWidth, tmpGrid.Columns[i].Width+tmpGridLineWidth );
     tmpColWidth := tmpGrid.ClientWidth - tmpColWidth;
     if tmpColWidth <= GRID_CW_MIN then
       tmpGrid.Columns[tmpCol].Width := GRID_CW_MIN
     else
       tmpGrid.Columns[tmpCol].Width := tmpColWidth;
   end;
end;
Code language: PHP (php)

Automation

In order to create an alignment settings menu and connect handlers to all application grids, we will need a couple more procedures:

  • From_AddColWidthMenu() – add menus and handlers for all grids on the specified form
  • App_AddColWidthMenu() – add menus and handlers for all forms

Thus, in order for all tables to have the ability to configure automatic alignment, it is enough to call procedure – App_AddColWidthMenu().

procedure App_AddColWidthMenu();
// add to all forms a menu for controlling the autowidth of table columns
var
   i:integer;
begin
   for i := 0 to Screen.FormCount - 1 do
     From_AddColWidthMenu(Screen.Forms[i]);
end;

procedure From_AddColWidthMenu(AForm: TForm);
// add a menu for controlling the autowidth of table columns to all tables
var
   i:integer;
   tmpGrid: TdbStringGridEx;
begin
   for i:=0 to AForm.ComponentCount - 1 do
   begin
     if AForm.Components[i] is TdbStringGridEx then
     begin
       tmpGrid := TdbStringGridEx(AForm.Components[i]);
       Grid_AddColWidthMenu(tmpGrid);
       // if there is a custom handler, then you need to add a call to the default handler directly in it
       // if not, a default handler will be added
       if tmpGrid.OnResize = '' then // to change the grid size
         tmpGrid.OnResize := 'Grid_OnResize';
       if tmpGrid.OnChange = '' then // to change the data in the grid
         tmpGrid.OnChange := 'Grid_OnChange';
     end;
   end;
end;Code language: JavaScript (javascript)

Links

Leave a Reply

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