I didn’t know you could do this with Record Helpers…

By | January 4, 2018

Before Xmas I started implementing code checks and metrics in my Browse and Doc It plug-in to help me write better code and where I’m maintaining legacy code, tell me where I need to refactor the code. So there are many files I have which are littered with hard coded integers, numbers and string which later on, may cause issues If I don’t update all the references at the same time.

Today I’ve been working on my Integrated Testing Helper plug-in as its long over due for some TLC. In one of the forms I’m using a TListView to display information about processes to run before or after the main application has compiled and these were indexed using hard coded integers. So I thought to replace them with an enumerate as below:

Type
  TITHColumn = (coTitle, coEXE, coParams, coDir);

However to use these enumerates I have to cast them to integers. Also the TListView has SubItems where the index is out by one so I was writing code as follows:

strProgramme := Item.SubItems[Pred(Integer(coEXE))];

and…

LV.Columns[Integer(coExe)].Width := Trunc(i * dblEXEPctWidth);

While walking to the local shops, I thought there must be a better way of doing this! Can I write a (record) helper to do all of this for me and also make it more readable for maintenance. So once I was back, I found I could write a record helper for my enumerate as follows:

Type
  TITHColumnHelper = Record Helper For TITHColumn
    Function ColumnIndex : Integer;
    Function SubItemIndex : Integer;
  End;

The implementations for the two functions are as follows:

Function TITHColumnHelper.ColumnIndex: Integer;

Begin
  Result := Integer(Self);
End;

Function TITHColumnHelper.SubItemIndex: Integer;

Begin
  Result := Pred(Integer(Self));
End;

This then allows me to simplify the coding in the form as shown in the following 2 examples:

Procedure TfrmITHConfigureDlg.btnEditClick(Sender: TObject);

Var
  iIndex: Integer;
  strTitle, strProgramme, strParameters, strWorkingDirectory: String;
  Item    : TListItem;

Begin
  iIndex := lvCompile.ItemIndex;
  If iIndex > -1 Then
    Begin
      Item                := lvCompile.Items[iIndex];
      strTitle            := Item.Caption;
      strProgramme        := Item.SubItems[coEXE.SubItemIndex];
      strParameters       := Item.SubItems[coParams.SubItemIndex];
      strWorkingDirectory := Item.SubItems[coDir.SubItemIndex];
      If TfrmITHProgrammeInfo.Execute(strTitle, strProgramme, strParameters, strWorkingDirectory,
        FGlobalOps.INIFileName) Then
        Begin
          Item.Caption     := strTitle;
          Item.SubItems[coEXE.SubItemIndex] := strProgramme;
          Item.SubItems[coParams.SubItemIndex] := strParameters;
          Item.SubItems[coDir.SubItemIndex] := strWorkingDirectory;
        End;
    End;
End;

Procedure TfrmITHConfigureDlg.lvResize(Sender: TObject);

Const
  dblTitlePctWidth = 0.15;
  dblEXEPctWidth = 0.35;
  dblParamsPctWidth = 0.20;
  dblDirPctWidth = 0.30;

Var
  i: Integer;
  LV: TListView;

Begin
  LV := Sender As TListView;
  i := LV.ClientWidth;
  LV.Columns[coTitle.ColumnIndex].Width := Trunc(i * dblTitlePctWidth);
  LV.Columns[coExe.ColumnIndex].Width := Trunc(i * dblEXEPctWidth);
  LV.Columns[coParams.ColumnIndex].Width := Trunc(i * dblParamsPctWidth);
  LV.Columns[coDir.ColumnIndex].Width := Trunc(i * dblDirPctWidth);
End;

Now I know I can do this, it opens up a number of options for simplifying column layouts in Virtual String Trees and the like.

regards
Dave.