Skip to main content

Provider Mode

  • 8 minutes to read

This topic explains how to setup your grid control to work in provider mode. Provider mode is a data loading mode that allows a grid control to display information when it is not stored in a standard database and cannot be connected via a TDataSource object or a TCustomConnection descendant. In this instance, you need to create a custom data source (TcxCustomDataSource descendant) and link it to a non data-aware View designed to work in unbound and provider modes. Non data-aware Views obtain data from the DataController.CustomDataSource property once it is assigned to a TcxCustomDataSource object.

When creating a custom data source, usually you only need to override the methods responsible for getting/setting data and for item (column) identification.

Refer to the Provider Mode: Master-Detail topic for information on how to create master-detail relationships in provider mode.

This topic describes the base steps when implementing provider mode based on the UnboundList demo.

Class Structure

The UnboundList demo displays records from a text file. Each record contains the customer and company names displayed in corresponding grid columns.

When the main form is created, these records are loaded into memory. Two classes (TCustomer and TCustomerList) have been designed to represent the records in memory. TCustomerList organizes records into a list and every record is represented by a TCustomer object. The classes provide a convenient way to access particular records and record fields.

The TCustomer class contains the Name and Description properties and stores the values of the corresponding fields in the text file. The TCustomer.ID property specifies a value which uniquely identifies the current record within TCustomerList. This property is automatically initialized when records are loaded from the file.

To display data from the TCustomerList, the custom data source of class TCustomerDataSource is used. TCustomerDataSource contains the FCustomers field identifying a TCustomerList object to access (i.e. get/set) data. The TCustomerDataSource class is inherited from the TcxCustomDataSource class and overrides the following methods:

Retrieves the number of records in the FCustomers list.

GetValue returns a value contained in a specific record and field (ID, Name or Description). SetValue updates this data.

Methods used to add and delete individual records respectively from the FCustomers list.

Item Identification

Data from TCustomerList is displayed within three columns: ID, Customer and Company. When a value of a specific record and column is needed, the grid control calls the GetValue method passing a record handle and an item (column) handle as parameters. By default, an item handle specifies the item’s index (its current visible position amongst the other items). However, this index is not constant (it will change if the column moves), so it cannot be used as an item identifier. To solve this problem, every column must be associated with a unique value thus guaranteeing columns are correctly identified. For this purpose, the GetDefaultItemID method can be used. When an item (column) is created, the data controller designates a new unique identity value for it, thus allowing columns to be identified in the GetValue and SetValue methods. The following code lists the GetValue method from the UnboundList demo:

function TCustomerDataSource.GetValue(ARecordHandle: TcxDataRecordHandle;
  AItemHandle: TcxDataItemHandle): Variant;
var
  AColumnId: Integer;
  ACustomer: TCustomer;
begin
  //Get a specific record from the FCustomers list
  ACustomer := FCustomers[Integer(ARecordHandle)];
  //Get column's identity value based on its index
  AColumnId := GetDefaultItemID(Integer(AItemHandle));
  //Return a value according to the identity value
  case AColumnId of
    IndexOfID:
      Result := ACustomer.ID;
    IndexOfName:
      Result := ACustomer.Name;
    IndexOfDescription:
      Result := ACustomer.Description;
  end;
end;

Alternatively, you can override the default behavior of the TcxCustomDataSource.GetItemHandle method. For instance, this can be necessary if you want to provide a real item handle for GetValue (instead of the item’s index) and for other methods requiring AItemHandle as a parameter.

Record Identification

The methods of the TcxCustomDataSource class get a record handle as a parameter. By default, a record handle is an integer value between 0 and (GetRecordCount - 1) which uniquely identifies a record in the custom data source. Record handles always refer to the same data regardless of sorting/grouping/filtering applied. They are determined by the virtual GetRecordHandle function before records are retrieved from a data source for the first time. Usually you do not need to override this function.

Creating a View

When working in provider mode, make certain that you are linking your custom data source to a non data-aware View. In the UnboundList demo, a non data-aware Table View called tvCustomers is linked to the custom data source.

Creating Columns

The following code for the GenerateColumns method in the UnboundList demo illustrates how columns are added in provider mode. The column’s DataBinding.ValueTypeClass property is initialized with a class that indicates the type of the column’s values. The last statement assigns an instance of TCustomerDataSource to the View’s DataController.CustomDataSource property by connecting the Table View to the custom data source.

procedure TUnboundListDemoMainForm.GenerateColumns;
begin
  with tvCustomers as TcxGridTableView do
  begin
    //Clear existing columns from the View if any
    ClearItems;
    //Create the ID column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'ID';
      Name := 'tvCustomersID';
      DataBinding.ValueTypeClass := TcxIntegerValueType;
      Width := 50;
    end;
    //Create the Customer column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'Customer';
      Name := 'tvCustomersCustomer';
      DataBinding.ValueTypeClass := TcxStringValueType;
      Width := 200;
    end;
    //Create the Company column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'Company';
      Name := 'tvCustomersCompany';
      DataBinding.ValueTypeClass := TcxStringValueType;
      Width := 200;
    end;
  end;
  tvCustomers.DataController.CustomDataSource := CustomerDataSource;
end;

Since the data controller designates a unique identity value for each column created in a View, there is no need to explicitly assign a unique value to a column to identify it. However, if you want to implement custom item (column) identification see the corresponding section below.

The constants used to address the columns are declared as follows:

const
  IndexOfID = 0;
  IndexOfName = 1;
  IndexOfDescription = 2;

Other TCustomerDataSource methods are not discussed here. Their implementation is quite simple. Refer to the UnboundList demo for the complete example.

The following image shows the grid control from this demo:

Custom Item Identification

In the UnboundList demo the identity values used to address the items (columns) correspond to the order in which they were created and also to their indexes. To designate a unique value for a column in this demo, you can assign the column’s DataBinding.Data property to the required value and also override the GetDefaultItemID method in the TCustomerDataSource class to provide access to this value as shown in the following code:

TCustomerDataSource = class(TcxCustomDataSource)
...
protected
  ...
  function GetDefaultItemID(AItemIndex: Integer): Integer; override;
  ...
end;
function TCustomerDataSource.GetDefaultItemID(AItemIndex: Integer): Integer;
var
  GridItem: TcxCustomGridTableItem;
begin
  GridItem := TcxCustomGridTableItem(DataController.GetItem(AItemIndex));
  Result := Integer(GridItem.DataBinding.Data);
end;
procedure TUnboundListDemoMainForm.GenerateColumns;
begin
  with tvCustomers as TcxGridTableView do
  begin
    //Create the ID column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'ID';
      Name := 'tvCustomersID';
      DataBinding.Data := Pointer(IndexOfID);
      //...
    end;
    //Create the Customer column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'Customer';
      Name := 'tvCustomersCustomer';
      DataBinding.Data := Pointer(IndexOfName);
      //...
    end;
    //Create the Company column
    with CreateColumn as TcxGridColumn do
    begin
      Caption := 'Company';
      Name := 'tvCustomersCompany';
      DataBinding.Data := Pointer(IndexOfDescription);
      //...
    end;
  end;
  tvCustomers.DataController.CustomDataSource := CustomerDataSource;
end;

The constants required to initialize the DataBinding.Data property and address the columns can now be assigned to arbitrary integer values as follows:

const
  IndexOfID = 10;
  IndexOfName = 11;
  IndexOfDescription = 12;

Multi-Threading Support

In order to enable support for multi-threaded data processing in your thread-safe TcxCustomDataSource descendant, override its IsMultiThreadingSupported function to return True.

See Also