Tutorial: Creating Collection Lists Using LiveBindings

From RAD Studio
Jump to: navigation, search

Go Up to Database and LiveBindings Tutorials


This tutorial demonstrates how to construct the application used in the ListCollections sample project.

Note: You can find the ListCollections sample project at:

Start | Programs | Embarcadero RAD Studio Alexandria | Samples and then navigate to Object Pascal\FireMonkey Desktop\ListCollections .


This sample is described in FMX.ListCollections Sample.

Step 1: Creating the Project

  1. Create a new project: File > New > Multi-Device Application - Delphi.
    In the wizard, choose Blank Application.
  2. Select the form, and change its Caption property in the Object Inspector to "List Collections Demo".
  3. Add a TLabel component to the form, and change its Text property in the Object Inspector to "Collection: ".
  4. In the Tool Palette, locate a ComboBox component and drop it onto the form.
  5. In the Form Designer, right-click the ComboBox1 component and select Add Item > TListBoxItem.
    AddItem.png
  6. Add four TLabel components to the form, and change the Text property for the respective components to:
    "Control Component:", "Control Expression:", "Source Component:", "Source Expression:"
    Also change the Name property respectively to:
    "LabelControlComponent", "LabelControlExpression", "LabelSourceComponent"
  7. For each of of the labels ("LabelControlComponent", "LabelControlExpression", "LabelSourceComponent"), add a TEdit component, and change the Name property respectively to:
    "EditControlComponent", "EditControlExpression", "EditSourceComponent"
    Also for each TEdit set the Enabled property to False.
  8. Add a TEdit component to the form, and change the Name property to "EditSourceExpression".
  9. Add two TButton components to the form, and change the Text property to "Fill" and "Clear" respectively.
    Also change the Name property to "ButtonEvaluate" and "ButtonClear" respectively.
  10. Add three TCheckBox components to the form, and change the Text property respectively to:
    "Auto Active", "Active", "Auto Fill"
    Also change the Name property respectively to:
    "CheckBoxAutoActive", "CheckBoxActive", "CheckBoxAutoFill"
    LabelAndEdit.png
  11. In the Tool Palette, locate a TListBox component and drop it onto the form.
  12. Add a TBindingList component to the form.
  13. On the form, right-click the BindingList1 component and select Binding Components...
  14. In the Bindings List Editor, select New Binding (Ins).
    NewBinding.png
  15. In the New LiveBinding dialog box, select Lists > TBindList.
  16. In the Object Inspector, set the Control Component property to ListBox1.
    ControlComponent.png

Step 2:Implementation

  • The SampleCollections.pas unit implements the additional supported collections of elements that will be exported to the List Box by means of LiveBindings. The sample used a class Factory Design Pattern to dynamically build several different types of collections:
  • A dictionary
  • A list of objects
  • A list of generics
  • A list of strings
  • The ListCollections product sample is not appropriate for beginners (advanced knowledge is required; to read the code, you should know RTTI, and advanced language elements such as anonymous methods and Design Patterns).


Add the SampleCollections.pas unit to the project.

In the private section of TForm1 class, add these variables:

Delphi:

FChecking: Boolean;
FDataObjects: TDictionary<TCollectionFactory, TObject>;
FFactory: TCollectionFactory;
FChanging: Boolean;

Add to the unit variable:

Delphi:

var
  BindScope1: TBindScope;

1. Add an OnCreate Form Event Handler

1. In the Structure View, select the Form1 component.
2. In the Object Inspector, open the Events tab, and then double-click OnCreate.
3. In the Code Editor, add the following code:

Delphi:

procedure TForm1.FormCreate(Sender: TObject);
var
  LFactory: TCollectionFactory;
  LListItem: TListBoxItem;
begin
  BindScope1 := TBindScope.Create(Self);
  BindList1.SourceComponent := BindScope1;
  FDataObjects := TObjectDictionary<TCollectionFactory, TObject>.Create([doOwnsValues]);
  // List test data in combobox
  for LFactory in GetCollectionFactories do
  begin
    LListItem := TListBoxItem.Create(ComboBox1);
    ComboBox1.AddObject(LListItem);
    LListItem.Text := LFactory.DisplayName;
    LListItem.Data := LFactory;
  end;
  Application.OnIdle := OnIdle;
  UpdateDisplayFields;
end;

In the public section of TForm1 class, add these procedures:

Delphi:

procedure OnIdle(Sender: TObject; var Done: Boolean);;
procedure UpdateDisplayFields;


Implementation for the procedures above:

Delphi:

procedure TForm1.OnIdle(Sender: TObject; var Done: Boolean);
begin
  FChecking := True;
  try
    CheckBoxActive.IsChecked := BindList1.Active;
    CheckBoxAutoFill.IsChecked := BindList1.AutoFill;
    CheckBoxAutoActivate.IsChecked := BindList1.AutoActivate;
  finally
    FChecking := False;
  end;
end;

//  Display information about binding
procedure TForm1.UpdateDisplayFields;
var
  LSourceExpression: string;
  LControlExpression: string;
  LSourceComponent: string;
  LControlComponent: string;
begin
  if BindList1.FormatExpressions.Count > 0 then
  begin
    LSourceExpression := BindList1.FormatExpressions[0].SourceExpression;
    LControlExpression := BindList1.FormatExpressions[0].ControlExpression;
  end;
  if BindList1.ControlComponent <> nil then
    LControlComponent := BindList1.ControlComponent.ClassName;
  if BindList1.SourceComponent <> nil then
  begin
    LSourceComponent := BindList1.SourceComponent.ClassName;
    if BindList1.SourceComponent is TBindScope then
      with TBindScope(BindList1.SourceComponent) do
        if DataObject <> nil then
          LSourceComponent := LSourceComponent + ' (' +
            DataObject.ClassName + ')'
      else if Component <> nil then
        LSourceComponent := LSourceComponent + ' (' +
          Component.ClassName + ')';
  end;
  EditSourceExpression.Text := LSourceExpression;
  EditControlExpression.Text := LControlExpression;
  EditControlComponent.Text := LControlComponent;
  EditSourceComponent.Text := LSourceComponent;
end;

2. Add an OnChange ComboBox Event Handler

1. Select from Structure ComboBox1 component.
2. In the Object Inspector, open the Events tab, and then double-click OnChange.
3. In the Code Editor, add the following code:

Delphi:

procedure TForm1.ComboBox1Change(Sender: TObject);
var
  LDataObject: TObject;
begin
  FChanging := True;
  try
    if ComboBox1.ItemIndex <> -1 then
    begin
      FFactory := ComboBox1.ListBox.Selected.Data as TCollectionFactory;
    end
    else
    begin
      FFactory := nil;
    end;
    if FFactory <> nil then
    begin
      if BindList1.FormatExpressions.Count = 0 then
        BindList1.FormatExpressions.Add;
      BindList1.FormatExpressions[0].SourceExpression := FFactory.GetExpression;
      BindList1.FormatExpressions[0].ControlExpression := 'Text';
      LDataObject := FFactory.CreateCollection;
      FDataObjects.AddOrSetValue(FFactory, LDataObject);  // Track objects in order to free when not in use
      // Set DataObject last because this can trigger activation and auto fill
      BindScope1.DataObject := LDataObject;
    end
    else
      BindScope1.DataObject := nil;
  finally
    FChanging := False;
    UpdateDisplayFields;
  end;
end;

3. Add OnClick Button Event Handler

1. In the Structure pane, select the ButtonEvaluate component.
2. In the Object Inspector, open the Events tab, and then double-click OnClick.
3. In the Code Editor, add the following code:

Delphi:

procedure TForm1.ButtonEvaluateClick(Sender: TObject);
begin
  BindList1Activating(Self); // Update expression
  BindList1.FillList;
end;
4. Repeat the steps above for ButtonClear, adding the following code in the Code Editor:

Delphi:

procedure TForm1.ButtonClearClick(Sender: TObject);
begin
  BindList1.ClearList;
end;

4. Add an OnChange CheckBox Event Handler

1. Select the CheckBoxAutoActive component from Structure.
2. In the Object Inspector, open the Events tab, and then double-click OnChange.
3. In the Code Editor, add the following code:

Delphi:

procedure TForm1.CheckBoxAutoActivateChange(Sender: TObject);
begin
  if not FChecking then
    BindList1.AutoActivate := CheckBoxAutoActivate.IsChecked;
end;
4. Repeat the steps above for CheckBoxActive, and CheckBoxAutoFill, adding the following code in the Code Editor:

Delphi:

procedure TForm1.CheckBoxActiveChange(Sender: TObject);
begin
  if not FChecking then
    BindList1.Active := CheckBoxActive.IsChecked;
end;

Delphi:

procedure TForm1.CheckBoxAutoFillChange(Sender: TObject);
begin
  if not FChecking then
    BindList1.AutoFill := CheckBoxAutoFill.IsChecked;
end;

5. Add OnActivating and OnEvalError BindList Event Handlers

1. Select the BindList1 component from Structure.
2. In the Object Inspector, open the Events tab, and then double-click OnActivating.
3. In the Code Editor, add the following code:

Delphi:

procedure TForm1.BindList1Activating(Sender: TObject);
begin
  if not FChanging then
    if BindList1.FormatExpressions.Count > 0 then
      BindList1.FormatExpressions[0].SourceExpression := EditSourceExpression.Text;
end;
4. In the Object Inspector, open the Events tab, and then double-click OnEvalError.
5. In the Code Editor, add the following code:

Delphi:

procedure TForm1.BindList1EvalError(Sender: TObject; AException: Exception);
begin
  // Generate a new exception with more information
  raise TBindCompException.CreateFmt(
    'Evaluation Exception'#13#10 +
    'Component Name: %s'#13#10 +
    'Exception Class: %s'#13#10 +
    'Exception Message: %s',
    [TComponent(Sender).Name, AException.ClassName, AException.Message]);
end;

The Results

Press F9 or choose Run > Run.

Classes

List Collections Demo represents the main window of the sample. It contains the following components:

Uses

See Also