Using ClientDataSets in Delphi

Program ClientDataSetDemo demonstrates the use of a convenient local database comprising two ClientDataSets storing titles and descriptions of a few of our tutorials. Each tutorial has several sections but each section belongs to only one tutorial so we have a one-to-many relationship between Tutorial and Section. This is exactly what we need for a master-detail view of the data. The master ClientDataSet is cdsTutorial and the detail is held in cdsSection. The tutorials and the sections of the selected tutorial appear in separate DBGrids.

When starting from scratch, find the ClientDataSet and DataSource components under the Data Access tab and the DBGrid under the Data Controls tab. As usual, each DBGrid needs its DataSource. The DataSet property of the DataSource is set to the appropriate ClientDataSet. Create the necessary fields in the ClientDataSet using the field definitions editor, which shows when you click on the FieldDefs ellipsis in the Object Inspector. Set the properties of the fields using the Object Inspector. Right click on a ClientDataSet in the design form and select the menu item Create Dataset to populate it.

You can view the data in the xml files with a browser such as Internet Explorer, which formats and shows it clearly:

The start of the XML file for Tutorial Sections viewed in Internet Explorer

The start of the XML file for Tutorial Sections viewed in Internet Explorer

You can edit the XML file in a text editor; the Lazarus editor is one of many that provide syntax highlighting for XML documents.

By keeping ClientDataSets closed at design time we prevent the inclusion of the database data in the form. It is useful to make each ClientDataSet active when developing the application, particularly when using the Columns editor of its grid. (To access the Columns editor right click on the grid in the design view of the form and select the menu item Columns Editor...).

As with other demonstrations in this tutorial, there is very little code. It is necessary to make the correct links between components in the Object Inspector. Crucially, the MasterSource property of cdsSection must be set to dsTutorial. Click on the ellipsis by the MasterField property to access the Field Link Designer. Select the matching Detail and Master fields then press the Add button and check the joined fields shown in the memo box ("TutorialName -> TutorialTitle").

The screenshot below shows the filter set so that sections of the "Constants and Variables" tutorial are shown only if "string" occurs in the Section Title. The relevant code is:
cdsSection.Filtered := True;
cdsSection.FilterOptions := [foCaseInsensitive];
cdsSection.Filter := 'SectionTitle LIKE ' + quotedstr('%' + edtName.Text + '%*');
Program ClientDataSetDemo with active filter

Program ClientDataSetDemo with active filter

Filter shows all the records that match the filter criteria. We have a case insensitive filter and a wildcard before and after the search term. Search (demonstrated for the Tutorials grid) uses the locate function to find the first matching value. Click on the column heading of the Tutorials grid to sort by that column.

The code for the demonstration follows. We supply Tutorial.xml and Section.xml with the source for all demonstration programs in db_contributions.zip. Put both XML files in the program folder.

uClientDataSetDemo.pas

unit uClientDataSetDemo;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, DBClient, Grids, DBGrids, ExtCtrls, DBCtrls, StdCtrls, strutils;

type
  TfrmCDS = class(TForm)
    cdsTutorial: TClientDataSet;
    cdsSection: TClientDataSet;
    dsTutorial: TDataSource;
    dsSection: TDataSource;
    dbgrdTutorial: TDBGrid;
    dbgrdSection: TDBGrid;
    DBNavigator1: TDBNavigator;
    DBNavigator2: TDBNavigator;
    edtSearchTitle: TEdit;
    btnSearch: TButton;
    edtName: TEdit;
    btnFilter: TButton;
    btnRemove: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure dbgrdTutorialTitleClick(Column: TColumn);
    procedure btnSearchClick(Sender: TObject);
    procedure btnFilterClick(Sender: TObject);
    procedure btnRemoveClick(Sender: TObject);
  end;

var
  frmCDS: TfrmCDS;

implementation

{$R *.dfm}

procedure TfrmCDS.FormCreate(Sender: TObject);
begin
  cdsTutorial.Open;
  cdsSection.Open;
  cdsTutorial.LogChanges := False;
  cdsSection.LogChanges := False;
end;

procedure TfrmCDS.FormClose(Sender: TObject; var Action: TCloseAction);
begin    
  cdsTutorial.SaveToFile(cdsTutorial.FileName);
  cdsSection.SaveToFile(cdsSection.FileName);
end;

procedure TfrmCDS.dbgrdTutorialTitleClick(Column: TColumn);
begin
  cdsTutorial.IndexFieldNames := Column.Field.FieldName;
end;

procedure TfrmCDS.btnSearchClick(Sender: TObject);
begin
  if not cdsTutorial.Locate('TutorialTitle', edtSearchTitle.Text, [loPartialKey]) then
    ShowMessage(edtsearchTitle.Text + ' not found');
end;

procedure TfrmCDS.btnFilterClick(Sender: TObject);
begin
  cdsSection.Filtered := True;
  cdsSection.FilterOptions := [foCaseInsensitive];
  cdsSection.Filter := 'SectionTitle LIKE ' + quotedstr('%' + edtName.Text + '%*');
end;

procedure TfrmCDS.btnRemoveClick(Sender: TObject);
begin
  cdsSection.Filtered := False;
end;

end.

uClientDataSetDemo.dfm

object frmCDS: TfrmCDS
  Left = 232
  Top = 132
  Width = 859
  Height = 333
  Caption = 'Client Datasets showing Master-Detail Relationship'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  OnCreate = FormCreate
  PixelsPerInch = 120
  TextHeight = 16
  object dbgrdTutorial: TDBGrid
    Left = 0
    Top = 0
    Width = 841
    Height = 97
    DataSource = dsTutorial
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -13
    TitleFont.Name = 'MS Sans Serif'
    TitleFont.Style = []
    OnTitleClick = dbgrdTutorialTitleClick
    Columns = <
      item
        Expanded = False
        FieldName = 'TutorialTitle'
        Title.Caption = 'Tutorial Title'
        Width = 300
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'TutorialShortDesc'
        Title.Caption = 'Short Description of Tutorial'
        Width = 500
        Visible = True
      end>
  end
  object dbgrdSection: TDBGrid
    Left = 0
    Top = 128
    Width = 841
    Height = 121
    DataSource = dsSection
    TabOrder = 1
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -13
    TitleFont.Name = 'MS Sans Serif'
    TitleFont.Style = []
    Columns = <
      item
        Expanded = False
        FieldName = 'SectionTitle'
        Width = 300
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'SectionShortDesc'
        Title.Caption = 'Short Description of Section'
        Width = 500
        Visible = True
      end>
  end
  object DBNavigator1: TDBNavigator
    Left = 312
    Top = 98
    Width = 234
    Height = 25
    DataSource = dsTutorial
    VisibleButtons = [nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel]
    TabOrder = 2
  end
  object DBNavigator2: TDBNavigator
    Left = 312
    Top = 253
    Width = 234
    Height = 25
    DataSource = dsSection
    VisibleButtons = [nbFirst, nbPrior, nbNext, nbLast, nbInsert, nbDelete, nbEdit, nbPost, nbCancel]
    TabOrder = 3
  end
  object edtSearchTitle: TEdit
    Left = 0
    Top = 98
    Width = 121
    Height = 24
    TabOrder = 4
    Text = 'Debugging'
  end
  object btnSearch: TButton
    Left = 128
    Top = 98
    Width = 75
    Height = 25
    Caption = 'Search'
    TabOrder = 5
    OnClick = btnSearchClick
  end
  object edtName: TEdit
    Left = 0
    Top = 253
    Width = 121
    Height = 24
    TabOrder = 6
    Text = 'Arrays'
  end
  object btnFilter: TButton
    Left = 125
    Top = 253
    Width = 75
    Height = 25
    Caption = 'Filter'
    TabOrder = 7
    OnClick = btnFilterClick
  end
  object btnRemove: TButton
    Left = 206
    Top = 253
    Width = 97
    Height = 25
    Caption = 'Remove Filter'
    TabOrder = 8
    OnClick = btnRemoveClick
  end
  object cdsTutorial: TClientDataSet
    Aggregates = <>
    FileName = 'Tutorial.xml'
    Params = <>
    Left = 672
    Top = 96
  end
  object cdsSection: TClientDataSet
    Aggregates = <>
    AggregatesActive = True
    FileName = 'Selection.xml'
    IndexFieldNames = 'TutorialName'
    MasterFields = 'TutorialTitle'
    MasterSource = dsTutorial
    PacketRecords = 0
    Params = <>
    Left = 568
    Top = 248
  end
  object dsTutorial: TDataSource
    DataSet = cdsTutorial
    Left = 720
    Top = 96
  end
  object dsSection: TDataSource
    DataSet = cdsSection
    Left = 608
    Top = 248
  end
end

ClientDataSetDemo.dpr

program ClientDataSetDemo;

uses
  Forms,
  uClientDataSetDemo in 'uClientDataSetDemo.pas' {frmCDS};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TfrmCDS, frmCDS);
  Application.Run;
end.
Programming - a skill for life!

Creating, editing, and searching a Firebird database, printing a report, stored procedures and ClientDataSets