Creating a Data Aware Component

A Design Patterns Example

To create a data aware component you are basically interfacing with a TFieldDataLink object. This controls the interaction with the BDE. In this example I'm going to make an edit box data aware. To do this I'm going to use two design patterns: Wrapper and Mediator. Here's the initial declaration:

type
    TMyDBEdit = class( TEdit )
    private
    protected
        FDataLink : TFieldDataLink;
    public
        constructor Create( AOwner : TComponent ); override;
        destructor Destroy; override;
    end;

constructor TMyDBEdit.Create( AOwner : TComponent );
begin
    inherited Create( AOwner );
    FDataLink := TFieldDataLink.Create;
end;

destructor TMyDBEdit.Destroy;
begin
    FDataLink.Free;
    inherited Destroy;
end;

Straight forward enough so far. The first step is to surface the FieldName and DataSource properties of the FDataLink object. This is a classic Wrapper pattern.

type
    TMyDBEdit = class( TEdit )
    private
    protected
        FDataLink : TFieldDataLink;
        function GetDataSource: TDataSource;
        function GetFieldName: string;
        procedure SetDataSource(Value: TDataSource);
        procedure SetFieldName(Value: string);
    public
        constructor Create( AOwner : TComponent ); override;
        destructor Destroy; override;
    published
        property DataSource: TDataSource read GetDataSource write SetDataSource;
        property FieldName: string read GetFieldName write SetFieldName;
    end;

{the constructor and destructor are identical to before}

function TMyDBEdit.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

function TMyDBEdit.GetFieldName: string;
begin
  Result := FDataLink.FieldName;
end;

procedure TMyDBEdit.SetDataSource(Value: TDataSource);
begin
  FDataLink.DataSource := Value;
end;

procedure TMyDBEdit.SetFieldName(Value: string);
begin
  FDataLink.FieldName := Value;
end;

(This is a doddle). Right, the next bit is to write the Mediator pattern. This wires two events (OnDataChange and OnUpdateData) in FDataLink to procedures in TMyDBEdit.

type
    TMyDBEdit = class( TEdit )
    private
    protected
        FDataLink : TFieldDataLink;
        function GetDataSource: TDataSource;
        function GetFieldName: string;
        procedure SetDataSource(Value: TDataSource);
        procedure SetFieldName(Value: string);
        procedure DataLinkDataChange(Sender: TObject);
        procedure DataLinkUpdateData(Sender: TObject);
        procedure UnwireDataLink;
        procedure WireDataLink;
    public
        constructor Create( AOwner : TComponent ); override;
        destructor Destroy; override;
    published
        property DataSource: TDataSource read GetDataSource write SetDataSource;
        property FieldName: string read GetFieldName write SetFieldName;
    end;

procedure TMyDBEdit.WireDataLink;
begin
  FDataLink.OnDataChange := DataLinkDataChange;
  FDataLink.OnUpdateData := DataLinkUpdateData;
end;

procedure TMyDBEdit.UnwireDataLink;
begin
  FDataLink.OnDataChange := nil;
  FDataLink.OnUpdateData := nil;
end;

{Change the constructor and destructor to call these two functions}
constructor TMyDBEdit.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);
  FDataLink := TFieldDataLink.Create;
  WireDataLink;
end;

destructor TMyDBEdit.Destroy;
begin
  UnwireDataLink;
  FDataLink.Free;
  inherited Destroy;
end;

{Implement the wired functions}
procedure TMyDBEdit.DataLinkDataChange(Sender: TObject);
begin
  if FDataLink.Field = nil then
    Text := ''
  else
    Text := FDataLink.Field.AsString;
end;

procedure TMyDBEdit.DataLinkUpdateData(Sender: TObject);
begin
  FDataLink.Field.AsString := Text;
end;

There's only one thing left to do and that's to implement the overriden Change procedure.

type
    TMyDBEdit = class( TEdit )
    private
    protected
        FDataLink : TFieldDataLink;
        function GetDataSource: TDataSource;
        function GetFieldName: string;
        procedure SetDataSource(Value: TDataSource);
        procedure SetFieldName(Value: string);
        procedure DataLinkDataChange(Sender: TObject);
        procedure DataLinkUpdateData(Sender: TObject);
        procedure UnwireDataLink;
        procedure WireDataLink;
        procedure Change; override;
    public
        constructor Create( AOwner : TComponent ); override;
        destructor Destroy; override;
    published
        property DataSource: TDataSource read GetDataSource write SetDataSource;
        property FieldName: string read GetFieldName write SetFieldName;
    end;

{Nothing else changes apart from this}
procedure TMyDBEdit.Change;
begin
  FDataLink.Modified;
  inherited Change;
end;

And, er, that's it.