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
- DataKeeper 1.8 – project source code