Example: TreeList Provider Mode
- 5 minutes to read
This example shows how to populate a tree list with the data in provider mode when the smart load feature is disabled (the TreeList control’s OptionsData.SmartLoad property is False).
It uses a file named DEPARTMENTS.TXT as the data store. This file was created by exporting data from the self-referencing DEPARTMENTS table that is shipped with the product. The data in the file is tabulated, and it contains no qualifiers. The first record’s values are field names.
The following image demonstrates the code execution result:
unit TLProviderModeUnit;
interface
uses
Windows, Messages, SysUtils, StrUtils, Variants, Classes, Controls, Forms, Dialogs, cxCustomData, cxTL, cxControls, cxTLData, cxClasses, cxCalendar, cxTextEdit, cxGraphics, cxInplaceContainer, cxStyles, StdCtrls, Math, cxTLdxBarBuiltInMenu;
type
TTLProviderModeForm = class(TForm)
VirtualTreeList: TcxVirtualTreeList;
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
end;
{ TcxProviderRecordHandle }
TcxProviderRecordHandle = class
private
FParent: TcxProviderRecordHandle;
FValues: TStringList;
protected
property Parent: TcxProviderRecordHandle read FParent;
public
constructor Create(AKey: Integer; AParent: TcxProviderRecordHandle);
function AddChild(AKey: string): TcxProviderRecordHandle;
property Values: TStringList read FValues write FValues;
end;
{ TcxCustomDemoDataSource }
TcxCustomDemoDataSource = class(TcxTreeListCustomDataSource)
private
FModified: boolean;
FRecordsList: TList;
FRootHandle: TcxProviderRecordHandle;
protected
function GetParentRecordHandle(ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle; override;
function GetRecordCount: Integer; override;
function GetRecordHandle(ARecordIndex: Integer): TcxDataRecordHandle; override;
function GetChildRecordHandle(AParentHandle: TcxDataRecordHandle; AChildIndex: Integer): TcxDataRecordHandle; override;
function GetRootRecordHandle: TcxDataRecordHandle; override;
function GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant; override;
procedure SetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle; const AValue: Variant); override;
property RootHandle: TcxProviderRecordHandle read FRootHandle;
public
constructor Create; virtual;
destructor Destroy; override;
procedure LoadData;
procedure SaveData;
// define a flag to check if an end-user has changed data when the application is about to be closed
property Modified: boolean read FModified;
property RecordsList: TList read FRecordsList;
end;
const
AFileName = 'DEPARTMENTS.TXT';
ATabChar = #9;
var
TLProviderModeForm: TTLProviderModeForm;
implementation
{$R *.dfm}
{ TcxProviderRecordHandle }
constructor TcxProviderRecordHandle.Create(
AKey: Integer; AParent: TcxProviderRecordHandle);
begin
FParent := AParent;
Values := TStringList.Create;
end;
function TcxProviderRecordHandle.AddChild(AKey: string): TcxProviderRecordHandle;
begin
Result := TcxProviderRecordHandle.Create(StrToInt(AKey), Self);
end;
{ TcxCustomDemoDataSource }
constructor TcxCustomDemoDataSource.Create;
begin
FRecordsList := TList.Create;
FRootHandle := TcxProviderRecordHandle.Create(-1, nil);
LoadData;
end;
destructor TcxCustomDemoDataSource.Destroy;
begin
FRootHandle.Free;
FRecordsList.Free;
inherited Destroy;
end;
function TcxCustomDemoDataSource.GetParentRecordHandle(
ARecordHandle: TcxDataRecordHandle): TcxDataRecordHandle;
begin
Result := TcxProviderRecordHandle(ARecordHandle).Parent;
end;
function TcxCustomDemoDataSource.GetRecordCount: Integer;
begin
Result := FRecordsList.Count;
end;
function TcxCustomDemoDataSource.GetRecordHandle(
ARecordIndex: Integer): TcxDataRecordHandle;
begin
Result := FRecordsList[ARecordIndex];
end;
function TcxCustomDemoDataSource.GetRootRecordHandle: TcxDataRecordHandle;
begin
Result := RootHandle;
end;
function TcxCustomDemoDataSource.GetValue(ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle): Variant;
var
I: Integer;
begin
with TcxProviderRecordHandle(ARecordHandle) do
begin
I := Integer(AItemHandle);
Result := Values[I];
end;
end;
procedure TcxCustomDemoDataSource.SetValue(
ARecordHandle: TcxDataRecordHandle; AItemHandle: TcxDataItemHandle;
const AValue: Variant);
var
I: Integer;
begin
with TcxProviderRecordHandle(ARecordHandle) do
begin
I := Integer(AItemHandle);
if VarIsNull(AValue) then
Values[I] := ''
else
Values[I] := AValue;
end;
FModified := True;
end;
procedure TcxCustomDemoDataSource.LoadData;
const
AFieldNamesRecordNumber = 0;
AKeyField = 0;
AParentKeyField = 1;
var
ARecords, AValues: TStringList;
procedure CreateColumns();
var
I: Integer;
begin
AValues.DelimitedText := ARecords[AFieldNamesRecordNumber];
for I := 0 to AValues.Count - 1 do
with TLProviderModeForm.VirtualTreeList.CreateColumn() do
begin
Caption.Text := AValues[I];
Caption.AlignHorz := taCenter;
end;
ARecords.Delete(AFieldNamesRecordNumber);
end;
function AddRecordHandle(AParentHandle: TcxProviderRecordHandle;
const ARecord: string): TcxProviderRecordHandle;
var
ADelimiterPos, AOffset, AStrLength: Integer;
AValue: string;
begin
AValues.DelimitedText := ARecord;
Result := AParentHandle.AddChild(AValues[AKeyField]);
AStrLength := Length(ARecord);
AOffset := 1;
// decompose the record string (recall that record values are delimited by a tabulator)
repeat
ADelimiterPos := PosEx(ATabChar, ARecord, AOffset);
if ADelimiterPos = 0 then
AValue := Copy(ARecord, AOffset, AStrLength - (AOffset - 1))
else
begin
AValue := Copy(ARecord, AOffset, ADelimiterPos - AOffset);
AOffset := ADelimiterPos + 1;
end;
Result.Values.Add(AValue);
until ADelimiterPos = 0;
end;
procedure AddRecordHandles(AParentHandle: TcxProviderRecordHandle;
const AParentKeyValue: string);
function GetFieldValue(ARecord: string; AFieldIndex: Integer): string;
begin
AValues.DelimitedText := ARecord;
Result := AValues[AFieldIndex];
end;
var
I: Integer;
ARecordHandle: TcxProviderRecordHandle;
begin
// build up the tree structure
for I := 0 to ARecords.Count - 1 do
if GetFieldValue(ARecords[I], AParentKeyField) = AParentKeyValue then
begin
ARecordHandle := AddRecordHandle(AParentHandle, ARecords[I]);
FRecordsList.Add(ARecordHandle);
AddRecordHandles(ARecordHandle, GetFieldValue(ARecords[I], AKeyField));
end;
end;
begin
if not FileExists(AFileName) then
raise Exception.Create('No data file was found');
ARecords := TStringList.Create;
AValues := TStringList.Create;
TLProviderModeForm.VirtualTreeList.BeginUpdate;
try
ARecords.LoadFromFile(AFileName);
CreateColumns;
AddRecordHandles(RootHandle, '0');
finally
TLProviderModeForm.VirtualTreeList.EndUpdate;
ARecords.Free;
AValues.Free;
end;
end;
procedure TcxCustomDemoDataSource.SaveData;
var
ARecords: TStringList;
AValue, AValues: string;
procedure AddColumnCaptions();
var
I: Integer;
begin
with TLProviderModeForm.VirtualTreeList do
begin
for I := 0 to ColumnCount - 1 do
begin
AValue := Columns[I].Caption.Text;
if I <> (ColumnCount - 1) then
AValues := AValues + AValue + ATabChar
else
AValues := AValues + AValue;
end;
ARecords.Add(AValues);
end;
end;
procedure AddRecords();
const
AKeyField = 0;
var
I, J: Integer;
function CompareKeyValues(Item1, Item2: Pointer): Integer;
var
AKeyValue1, AKeyValue2: Integer;
begin
AKeyValue1 := StrToInt(TcxProviderRecordHandle(Item1).Values[AKeyField]);
AKeyValue2 := StrToInt(TcxProviderRecordHandle(Item2).Values[AKeyField]);
Result := CompareValue(AKeyValue1, AKeyValue2);
end;
begin
// originate the order of records as it was in the file store before loading data into the buffer
FRecordsList.Sort(@CompareKeyValues);
for I := 0 to FRecordsList.Count - 1 do
begin
AValue := '';
AValues := '';
with TcxProviderRecordHandle(FRecordsList[I]) do
begin
for J := 0 to Values.Count - 1 do
begin
AValue := Values[J] ;
if J <> (Values.Count - 1) then
AValues := AValues + AValue + ATabChar
else
AValues := AValues + AValue;
end;
end;
ARecords.Add(AValues);
end;
end;
begin
ARecords := TStringList.Create;
AddColumnCaptions;
AddRecords;
try
ARecords.SaveToFile(AFileName);
finally
ARecords.Free;
end;
end;
procedure TTLProviderModeForm.FormCreate(Sender: TObject);
begin
VirtualTreeList.CustomDataSource := TcxCustomDemoDataSource.Create;
VirtualTreeList.FullExpand;
end;
procedure TTLProviderModeForm.FormDestroy(Sender: TObject);
begin
VirtualTreeList.DataController.CustomDataSource.Free;
VirtualTreeList.DataController.CustomDataSource := nil;
end;
procedure TTLProviderModeForm.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
var
I: Integer;
begin
with VirtualTreeList.DataController.CustomDataSource as TcxCustomDemoDataSource do
begin
if Modified then
I := MessageDlg('Save changes to the ' + AFileName + ' file?', mtConfirmation, [mbYes, mbNo, mbCancel], 0);
case I of
mrYes: SaveData;
mrCancel: CanClose := False;
end;
end;
end;
end.
See Also