Delphi Programming

and software in general.

Wednesday, July 23, 2008

Reusable Grid View - part 2

The Controller

Our basic TGridViewController will hold a list of TGridViewColumns. I could make a simple array, or I could use TStringList. TStringList is probably one of the best and one of the worst classes in Delphi. So easy to use and so easy to abuse. In this case, I will only need to manage a handful of columns, instead of thousands of lines of text - so it will be ok.

On the abuse side: before you start filling TStringList - consider adjusting the capacity. Resizing the list is EXPENSIVE. Also - fill first, sort later.

Ok, lets take a look at the initial skeleton of the controller class:

TGridViewController = class (TStringList)
private
FGrid : TStringGrid;
function GetIsEmpty:Boolean;
function GetCurrentSelectionIndex: Integer;
protected
procedure DefineAttributes; virtual; abstract;
function GetRowCount: Integer; virtual; abstract;
procedure SetGrid(const Value: TStringGrid); virtual;
procedure DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
function MakeColumn(ColType : TGridViewColumnType; aTitle:String; aWidth:Integer; anAlign:TAlignment):TGridViewColumn;
public
constructor Create; virtual;
destructor Destroy; override;
procedure Refresh;
function CreateTextColumn(aTitle:String; aGetText:TGetTextMethod; aWidth:Integer = 64; anAlign:TAlignment = taLeftJustify):TGridViewTextColumn;
property CurrentSelectionIndex:Integer read GetCurrentSelectionIndex;
property Grid: TStringGrid read FGrid write SetGrid;
property IsEmpty: Boolean read GetIsEmpty;
property RowCount:Integer read GetRowCount;
end;


I won't go into the details on the TGridViewColumn yet, but note the two abstract methods in the controller: DefineAttributes and GetRowCount. To make a working grid, these are what we need to override.

The Grid property identifies the grid that we will control and DrawCell is the method we plug into the grid onDrawCell event. MakeColumn is called by the CreateTextColumn with the appropriate TGridColumnView descendant - hiding the interaction with our parent string list. I guess you already have figured out that IsEmpty simply check the number of rows.

A simple Example

Lets consider the simplest possible example: a grid showing rows with the numbers 1 to 5, and their values squared. Note that the unit is pretty ignorant about what a grid is.


unit DemoNumbersClass;

interface

uses
Classes, SysUtils, FDCGridViewController;

type
TViewNumbers = class(TGridViewController)
private
function GetNumber(const Row: Integer): String;
function GetNumberSquared(const Row: Integer): String;
protected
procedure DefineAttributes; override;
function GetRowCount: Integer; override;
end;

We define two columns, and refer each column to a data retrival method.

implementation

{ TViewNumbers }

procedure TViewNumbers.DefineAttributes;
begin
CreateTextColumn('Number', GetNumber);
CreateTextColumn('Squared', GetNumberSquared, 64, taRightJustify);
end;

function TViewNumbers.GetNumber(const Row: Integer): String;
begin
Result := IntToStr(Row);
end;

function TViewNumbers.GetNumberSquared(const Row: Integer): String;
begin
Result := IntToStr(SQR(Row));
end;

function TViewNumbers.GetRowCount: Integer;
begin
Result := 5;
end;

end.



Then we create a Form, drop a string grid on it, tweak some row size settings etc., and add the controller.


unit DemoFDCGridViewController;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, Forms,
StdCtrls, Grids, FDCGridViewController, DemoNumbersClass;

type
TGridViewForm = class(TForm)
StringGrid1: TStringGrid;
procedure FormCreate(Sender: TObject);
private
Numbers : TViewNumbers;
public
{ Public declarations }
end;

var
GridViewForm: TGridViewForm;

implementation

{$R *.dfm}

{ TGridViewForm }

procedure TGridViewForm.FormCreate(Sender: TObject);
begin
// Create our controller
Numbers := TViewNumbers.Create;
// and introduce the controller to the host grid
Numbers.Grid := StringGrid1;
end;

end.

And the result:

As you can see, there are a few tweaks that I haven't described yet. Also, I still haven't revealed the simple steps done to invade our host grid.

Stay tuned :)