Using VCL.NET with ECO

By: David Clegg

Abstract: Demonstrates using VCL.NET as the presentation framework for ECO applications. Updated to include TListConnector

Using VCL.NET with ECO

Table of Contents

Introduction

Creating the Application

Adding the ECO Factor

Creating the Model

Configuring the EcoSpace

Creating the VCL.NET User Interface

Showing a Contact List

Populating the List View

Creating an Edit Dialog

Updating the EcoSpace

Adding a Contact

Editing a Contact

Deleting a Contact

Persisting the EcoSpace

Databinding using the TListConnector class

Creating a DBGrid Form

Using the TListConnector

Adding and Deleting Contacts

Showing the Grid Form

Summary

Acknowledgements

    Introduction

When developing a desktop application using Borland's Enterprise Core Objects framework, it is often assumed that the WinForms framework must be used as the presentation layer. However, this doesn't have to be the case. There is nothing in the ECO framework that ties it to WinForms, apart from the support it offers for binding to WinForms components via the ExpressionHandle, ReferenceHandle, and CurrencyManagerHandle components, and the extensions to WinForms controls offered via components such as the EcoListActionExtender.

There is no technical reason why the VCL.NET framework can't be used instead of WinForms as the presentation layer, and the only significant functionality that is lost when doing so is support for binding instances of your ECO classes directly to the controls on your form. All the other ECO services and sub-frameworks can be accessed from a VCL.NET application in exactly the same manner as you would when writing a WinForms one.

This article will demonstrate how easy it is to write an ECO VCL.NET application, by creating a simple address book application.

    Creating the Application

We will start off by creating a new VCL.NET application by selecting File|New|VCL Forms Application - Delphi for .NET from the Delphi 2005 main menu

Hide image

Set the Name property of the form to MainForm, save the form file as Main_Form.pas, and the application as ECOContactsVCL.bdsproj.

    Adding the ECO Factor

At this stage we have simply created a standard VCL.NET application, so it is now time to bring ECO into the picture. To do this we will add an ECO UML package, and EcoSpace to our application. Activate the New Items dialog by selecting File|New|Other... from the main menu. Expand the Delphi for .NET Projects node, and select the New ECO Files node.

Hide image

Select the ECO UML Package icon, and click the OK button. Repeat the steps to open the New Items dialog and navigate to the New ECO Files node, select the EcoSpace icon, and click the OK button.

    Creating the Model

Now that we have all the files required to make our VCL.NET application an ECO one, it is time to design the model. Switch to the Model View tab, expand the ECOContactsVCL node, CoreClasses package node, right click the CoreClasses entry and select Open Diagram.

Hide image

Drag a new ECO Class onto the diagram and name it Contact. Right-click the Contact class and select Add|Attribute.

Hide image

Call the attribute name and set its type to string. Add three more string attributes called address, phone, and email. Our finished class should look like this :-

Hide image

    Configuring the EcoSpace

For this demonstration, we will save our information to an XML file. To do this we need to configure our EcoSpace to use the PersistenceMapperXml component. Double-click on the EcoSpace.pas file in the Project Manager, and switch to the Design view. Drag a PersistenceMapperXml component from the Tool Palette, and set the FileName property to Data.xml.

We also need to tell the EcoSpace which UML packages are associated with it. ECO uses Reflection to find available UML packages to use, so before we can do this we need to compile our application. Additionally, because we haven't referenced the TEcoSpace class, there will be no meta-data emitted for it in the executable at this stage. Therefore, when ECO is attempting to locate all valid UML packages, it won't find the one we have just created. In order to overcome this, we will use the the RuntimeRequiredAttribute to tell the compiler to emit meta-data for our TEcoSpace class even though it hasn't been referenced elsewhere yet. Switch to the Code view for our EcoSpace, and insert the following code directly before the implementation section :-

[assembly: RuntimeRequiredAttribute(typeof(TEcoSpace))]

We can now successfully select the UML package and associate it with our EcoSpace. Compile the application, and switch to the EcoSpace Design view. Right-click the EcoSpace designer, and select Select Packages from the context menu. Select CoreClassesUnit.CoreClasses in the Available UML Packages list, click the > button to move it to the Selected UML Packages list, and click the OK button. Hide image

    Creating the VCL.NET User Interface

Now that we've created our ECO model and configured the EcoSpace, it's time to design our VCL.NET user interface. We will show the instances of our Contact class in a TListView, and provide a form to be shown as a modal dialog for adding new Contact instances, or editing existing ones. We will also provide the ability to delete existing Contacts.

    Showing a Contact List

The first step is to add and configure a TListView to our main form, which will be used to show all the Contact instances in the EcoSpace.

Double-click on the Main_Form.pas file in the Project Manager, and switch to the Design view. Drag a TListView control from the Tool Palette, and set the Name property to lvContacts, ViewStyle to vsReport, ReadOnly to True, and RowSelect to True. Click the ellipsis button next to the Columns property to invoke the Columns editor, and add columns for each attribute in our Contact class.

Drop on a TButton from the Tool Palette, and set the Name property to btnAdd, and the Caption property to &Add. Repeat the process to add Edit, Delete and Save buttons, changing the Name and Caption properties as appropriate. Below is a design-time representation of this form.

Hide image

And here is the contents of the .nfm file for it.

object MainForm: TMainForm
  Caption = 'Contacts'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  Height = 350
  Width = 554
  Left = 0
  Top = 0
  DesignSize = (
    546
    316)
  PixelsPerInch = 96
  TextHeight = 13
  object lvContacts: TListView
    Anchors = [akLeft, akTop, akRight, akBottom]
    Columns = <
      item
        Caption = 'Name'
        Width = 150
      end
      item
        Caption = 'Address'
        Width = 150
      end
      item
        Caption = 'Phone'
        Width = 100
      end
      item
        Caption = 'E-Mail'
        Width = 100
      end>
    ReadOnly = True
    RowSelect = True
    TabOrder = 0
    ViewStyle = vsReport
    Left = 8
    Top = 8
    Width = 529
    Height = 265
  end
  object btnAdd: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Add'
    TabOrder = 1
    Left = 8
    Top = 280
    Width = 75
    Height = 25
  end
  object btnEdit: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Edit'
    TabOrder = 2
    Left = 88
    Top = 280
    Width = 75
    Height = 25
  end
  object btnDelete: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Delete'
    TabOrder = 3
    Left = 168
    Top = 280
    Width = 75
    Height = 25
  end
  object btnSave: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Save'
    TabOrder = 4
    Left = 248
    Top = 280
    Width = 75
    Height = 25
  end
end

    Populating the List View

We now have to add the functionality to interact with our EcoSpace. The first step is to add code to create an instance of our EcoSpace and store a reference to it with our form. Add a reference to EcoSpace in the interface uses section of the Main_Form.pas unit. Add a private variable called FEcoSpace of the type TEcoSpace to the private section of the TMainForm class.

In the TMainForm.OnCreate event handler, add the following code :-

procedure TMainForm.FormCreate(Sender: TObject);
begin
  FEcoSpace := TEcoSpace.Create;
  FEcoSpace.Active := True;
end;

Now we will add the code to populate the TListView with the Contact instances from our EcoSpace. Add a reference to CoreClassesUnit to the interface uses section, and Borland.Eco.ObjectRepresentation to the implementation uses section. In the TMainForm.OnShow event, add the following code :-

procedure TMainForm.FormShow(Sender: TObject);
begin
  RefreshContacts;
end;

procedure TMainForm.RefreshContacts;
var
 lObjects: IObjectList;
 i: Integer;
 lContact: Contact;
 lItem: TListItem;
begin
  lvContacts.Clear;
  lObjects := FEcoSpace.OclService.Evaluate('Contact.allInstances') 
    as IObjectList;
  if Assigned(lObjects) then
  begin
    for i := 0 to lObjects.Count -1 do
    begin
      lContact := Contact(lObjects[i].AsObject);
      lItem := lvContacts.Items.Add;
      lItem.Caption := lContact.name;
      lItem.SubItems.Add(lContact.address);
      lItem.SubItems.Add(lContact.phone);
      lItem.SubItems.Add(lContact.email);
      //Save a reference to the Contact instance so we can pass it 
      //to our edit dialog form
      lItem.Data := lContact;
    end;
  end;
end;

The key to this procedure is the call to the FEcoSpace.OclService.Evaluate function. This allows us to evaluate an Object Constraint Language expression to return all instances of our Contact class from the EcoSpace. The result is returned as an IObjectList interface which we can then use to populate our TListView.

    Creating an Edit Dialog

It is all well and good to be able to show all Contact instances in our EcoSpace, but at this stage we haven't provided a way to enter new Contacts into it, so we have nothing to show for our efforts. The next step is to create a new form which will be invoked as a modal dialog to allow us to add new Contacts and edit existing ones.

Invoke the New Items dialog, expand the Delphi for .NET Projects node, select New Files, click on the VCL Form icon, and click the OK button. Set the Name property to ContactForm, the Caption property to Edit Contact and save the form as Contact_Form.pas.

Drop on TLabeledEdit components for each attribute of our Contact class, and set the Name and EditLabel.Caption accordingly. We will also need an OK and Cancel button, and to configure the form to show up as a non-resizable dialog which will display in the center of the screen when shown. Below is a design-time representation of this form, followed by the contents of the .nfm file for it :-

Hide image

object ContactForm: TContactForm
  BorderIcons = [biSystemMenu]
  Caption = 'Edit Contact'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  Height = 224
  Position = poScreenCenter
  Width = 346
  Left = 0
  Top = 0
  PixelsPerInch = 96
  TextHeight = 13
  object leName: TLabeledEdit
    EditLabel.Caption = 'Name'
    EditLabel.Height = 13
    EditLabel.Width = 27
    TabOrder = 0
    Left = 16
    Top = 24
    Width = 193
    Height = 21
  end
  object leAddress: TLabeledEdit
    EditLabel.Caption = 'Address'
    EditLabel.Height = 13
    EditLabel.Width = 39
    TabOrder = 1
    Left = 16
    Top = 72
    Width = 305
    Height = 21
  end
  object lePhone: TLabeledEdit
    EditLabel.Caption = 'Phone'
    EditLabel.Height = 13
    EditLabel.Width = 30
    TabOrder = 2
    Left = 16
    Top = 120
    Width = 121
    Height = 21
  end
  object leEmail: TLabeledEdit
    EditLabel.Caption = 'E-Mail'
    EditLabel.Height = 13
    EditLabel.Width = 28
    TabOrder = 3
    Left = 144
    Top = 120
    Width = 177
    Height = 21
  end
  object btnCancel: TButton
    Cancel = True
    Caption = 'Cancel'
    ModalResult = 2
    TabOrder = 4
    Left = 248
    Top = 152
    Width = 75
    Height = 25
  end
  object btnOK: TButton
    Caption = 'OK'
    Default = True
    ModalResult = 1
    TabOrder = 5
    Left = 160
    Top = 152
    Width = 75
    Height = 25
  end
end

In order to make invoking this form as easy as possible, we will provide a class function which will take an instance of our Contact class as a parameter, and return the TModalResult as its result. The code for this is as follows :-

class function TContactForm.ShowContactForm(
  AContact: Contact): TModalResult;
var
  lForm: TContactForm;
begin
  lForm := TContactForm.Create(nil);
  try
    //Save a reference to the Contact instance in a
    //private field of the TContactForm instance.
    lForm.FContact := AContact;
    Result := lForm.ShowModal;
  finally
    lForm.Free;
  end;
end;

As the above code is referencing the Contact class, we will need to ensure that we add CoreClassesUnit to the interface section of the Contact_Form unit.

We will add code to the TContactForm.OnShow event to populate our controls from the Contact instance, and to the btnOK.OnClick event to populate our Contact instance from the controls. Because we configured the ModalResult property of our TButton components, this the only code we need to put in the form.

procedure TContactForm.FormShow(Sender: TObject);
begin
  leName.Text := FContact.name;
  leAddress.Text := FContact.address;
  lePhone.Text := FContact.phone;
  leEmail.Text := FContact.email;
end;

procedure TContactForm.btnOKClick(Sender: TObject);
begin
  FContact.name := leName.Text;
  FContact.address := leAddress.Text;
  FContact.phone := lePhone.Text;
  FContact.email := leEmail.Text;
end;

    Updating the EcoSpace

We now have a form that can be shown modally with one line of code which can be used to edit the attributes of new and existing Contact instances. It is now time to add the code to invoke this from our main form.

    Adding a Contact

Lets examine the code required to add a new Contact to the EcoSpace, and to update the GUI to show it.

procedure TMainForm.btnAddClick(Sender: TObject);
var
  lContact: Contact;
begin
  FEcoSpace.UndoService.StartTransaction;
  lContact := Contact.Create(FEcoSpace);
  if TContactForm.ShowContactForm(lContact) = mrOK then
  begin
    FEcoSpace.UndoService.CommitTransaction;
    AddContactItem(lContact);
  end
  else
    FEcoSpace.UndoService.RollbackTransaction;
end;

procedure TMainForm.AddContactItem(AContact: Contact);
var
  lItem: TListItem;
begin
  lItem := lvContacts.Items.Add;
  lItem.Caption := AContact.name;
  lItem.SubItems.Add(AContact.address);
  lItem.SubItems.Add(AContact.phone);
  lItem.SubItems.Add(AContact.email);
  //Save a reference to the Contact instance so we can pass it to our
  //edit dialog form
  lItem.Data := AContact;
end;

First we need to create a new instance of our Contact class, passing in a reference to the active EcoSpace. Before this is done, we have started a transaction using the IUndoService, so if the user clicks the Cancel button on the modal dialog, we can rollback the created Contact instance. If the OK button is clicked, we commit the change to the EcoSpace, and add the details for the new Contact to the TListView. To do this, I've extracted the code that was used when populating the TListView into a common AddContactItem procedure which can then be called from both places.

Note: There is a bug in the ECO II IUndoService implementation which will cause an 'Object reference not set to an instance of an object' exception the second time a Contact instance is added. This has been addressed in an unofficial patch, which can be downloaded from http://homepages.borland.com/ecoteam/pmwiki/pmwiki.php?n=Eco.EcoIIPatches. This patch has been applied to the source code which accompanies this article.

    Editing a Contact

Now let's examine the code required to edit an existing Contact instance :-

procedure TMainForm.btnEditClick(Sender: TObject);
var
  lContact: Contact;
begin
  if Assigned(lvContacts.Selected) then
  begin
    lContact := lvContacts.Selected.Data as Contact;
    if TContactForm.ShowContactForm(lContact) = mrOK then
    begin
      UpdateContactItem(lvContacts.Selected);
    end;
  end;
end;

procedure TMainForm.UpdateContactItem(AItem: TListItem);
var
  lContact: Contact;
begin
  lContact := AItem.Data as Contact;
  AItem.Caption := lContact.name;
  AItem.SubItems[0] := lContact.address;
  AItem.SubItems[1] := lContact.phone;
  AItem.SubItems[2] := lContact.email;
end;

Because the Contact instance will only have its attributes set when the OK button of the TContactForm dialog is clicked, there is no need to use a transaction to rollback any change to the EcoSpace if the Cancel button is clicked.

    Deleting a Contact

The final operation we need to support is the ability to delete a Contact instance from the EcoSpace. Below is the code for this :-

procedure TMainForm.btnDeleteClick(Sender: TObject);
begin
  if Assigned(lvContacts.Selected) then
  begin
    if MessageDlg('Are you sure you wish to delete this contact?',
      mtConfirmation, mbYesNo, 0) = mrYes then
    begin
      (lvContacts.Selected.Data as Contact).AsIObject.Delete;
      lvContacts.Items.Delete(lvContacts.Selected.Index);
    end;
  end;
end;

This code should be pretty self-explanatory, with the only thing of note being that we need to use the AsIObject method of our Contact instance to return a reference to the IObject interface so we can call its Delete method.

    Persisting the EcoSpace

While the above code snippets allow us to manipulate the objects in our EcoSpace at run-time, there is one final piece of the puzzle missing. We need to provide a way to persist these changes to the XML file we configured earlier. This is simply one line of code which we will add to the OnClick event our Save button :-

procedure TMainForm.btnSaveClick(Sender: TObject);
begin
  FEcoSpace.UpdateDatabase;
end;

    Databinding using the TListConnector class

While the above approach demonstrates how easy it is to use VCL.NET with ECO, there is still code required to populate our user interface elements.

Delphi 2005 introduced a class called the TListConnector. This class is designed to be a bridge between the VCL.NET TDataSet and .NET framework collection classes. If a class implements the IList interface, it can be associated with a TListConnector. As TListConnector is a TDataSet descendant, it can then be bound to all VCL.NET data-aware controls via a TDataSource. Also, if the class associated with the TListConnector implements the IBindingList and IEditableObject interfaces, even more standard TDataSet features can be supported.

And because the ECO ExpressionHandle class implements the IList interface, we can use the TListConnector class to bind a list of Contact instances to a TDBGrid.

    Creating a DBGrid Form

We will now create an additional form, which will contain a TDBGrid which we will bind to our Contact class instances.

Invoke the New Items dialog, expand the Delphi for .NET Projects node, select New Files, click on the VCL Form icon, and click the OK button. Set the Name property to GridForm, the Caption property to Contacts (Grid) and save the form as Grid_Form.pas.

Drop on a TDBGrid from the Tool Palette onto the form, and set the Name property to dbgContacts. Click the ellipsis button next to the Columns property to invoke the Columns editor, and add columns for each attribute in our Contact class. The FieldName property should be set to the name of the attribute.

Drop on a TButton from the Tool Palette, and set the Name property to btnAdd, and the Caption property to &Add. Repeat the process to add Delete and Save buttons, setting the Name and Caption properties accordingly. Below is a design-time representation of this form :-

Hide image

And here is the contents of the .nfm file :-

object GridForm: TGridForm
  Caption = 'Contacts (Grid)'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  Height = 350
  Width = 554
  Left = 0
  Top = 0
  DesignSize = (
    546
    316)
  PixelsPerInch = 96
  TextHeight = 13
  object dbgContacts: TDBGrid
    Anchors = [akLeft, akTop, akRight, akBottom]
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -11
    TitleFont.Name = 'Tahoma'
    TitleFont.Style = []
    Left = 8
    Top = 8
    Width = 529
    Height = 265
    Columns = <
      item
        Expanded = False
        FieldName = 'name'
        Title.Caption = 'Name'
        Width = 150
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'address'
        Title.Caption = 'Address'
        Width = 150
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'phone'
        Title.Caption = 'Phone'
        Width = 95
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'email'
        Title.Caption = 'E-mail'
        Width = 95
        Visible = True
      end>
  end
  object btnAdd: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Add'
    TabOrder = 1
    Left = 8
    Top = 280
    Width = 75
    Height = 25
  end
  object btnDelete: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Delete'
    TabOrder = 2
    Left = 88
    Top = 280
    Width = 75
    Height = 25
  end
  object btnSave: TButton
    Anchors = [akLeft, akBottom]
    Caption = '&Save'
    TabOrder = 3
    Left = 168
    Top = 280
    Width = 75
    Height = 25
  end
end

    Using the TListConnector

We will now bind a list of our Contact instances to the DBGrid. Drop a TListConnector and TDataSource from the Tool Palette, and set the DataSource1.DataSet property to ListConnector1. Set the grdContacts.DataSource property to DataSource1.

In order to get a list of Contact instances for the TListConnector to bind to, we will use the ExpressionHandle and ReferenceHandle classes. The ReferenceHandle class will be the root handle which binds to our EcoSpace instance, and the ExpressionHandle class will allow us to use an Ocl expression to query the EcoSpace. We will initialize these classes in a constructor which takes a TEcoSpace instance as a parameter. The code for this is below :-

constructor TGridForm.Create(AOwner: TControl; AEcoSpace: TEcoSpace);
begin
  inherited Create(AOwner);
  FRootHandle := ReferenceHandle.Create;
  FRootHandle.EcoSpace := AEcoSpace;

  FContactsHandle := ExpressionHandle.Create;
  FContactsHandle.RootHandle := FRootHandle;
  FContactsHandle.Expression := 'Contact.allInstances';

  ListConnector1.DataObject := FContactsHandle.GetList;
  ListConnector1.Active := True;
end;

In order to use the ReferenceHandle and ExpressionHandle classes, we have to add Borland.Eco.Handles to the interface uses section. We also need to create the private variables FRootHandle and FContactsHandle.

The above code should be pretty straightforward, and just like we did when using the IOclService interface, we are evaluating the 'Contact.allInstances' OCL expression to return a list of Contact instances. This list is then assigned to the ListConnector1.DataObject property.

    Adding and Deleting Contacts

Because the TListConnector class is a TDataSet descendant, the code to add and delete object instances should come as no surprise to anyone with a background in Delphi database development. Below is the code for the btnAdd and btnDelete OnClick event handlers :-

procedure TGridForm.btnAddClick(Sender: TObject);
begin
  ListConnector1.Append;
end;

procedure TGridForm.btnDeleteClick(Sender: TObject);
begin
  ListConnector1.Delete;
end;

procedure TGridForm.btnSaveClick(Sender: TObject);
begin
  if ListConnector1.State in [dsInsert, dsEdit] then
    ListConnector1.Post;
  TEcoSpace(FRootHandle.EcoSpace).UpdateDatabase;
end;

Included above is also the code in the btnSave.OnClick event handler, which persists the changes in the EcoSpace. It is similar to the code used in our TMainForm class, with the additional requirement to typecast the FRoothandle.EcoSpace instance to the TEcoSpace class which implements the UpdateDatabase method. And prior to saving the changes to the EcoSpace, it posts any changes made to the TListConnector instance.

Simply using the above code will result in a System.InvalidOperationException exception being thrown with the message 'Operation is not valid due to the current state of the object.', when the TListConnector attempts to save the changes to a new Contact. To get around this issue, we will change the behavior of the TListConnector class so rather than inserting a new Contact instance, the TListConnector will edit a Contact instance we've created manually. To do this, insert the following code in the ListConnector1.OnNewRecord event handler:-

procedure TGridForm.ListConnector1NewRecord(DataSet: TDataSet);
begin
  Contact.Create(FRootHandle.EcoSpace);
  FContactsHandle.EnsureBindingList;
  ListConnector1.CheckBrowseMode;
  ListConnector1.Edit;
end;

    Showing the Grid Form

We will now add a button to our TMainForm class to show an instance of our TGridForm modally.

Switch to the Design view for TMainForm, drop on a TButton from the Tool Palette, set the Name property to btnShowGrid, and Text property to Show &Grid Form.

In the btnShowGrid.OnClick event handler, add the following code :-

procedure TMainForm.btnShowGridClick(Sender: TObject);
var
  lGridForm: TGridForm;
begin
  lGridForm := TGridForm.Create(nil, FEcoSpace);
  try
    lGridForm.ShowModal;
    RefreshContacts;
  finally
    lGridForm.Free;
  end;
end;

There is nothing complicated about this code, with the only difference compared to showing another TForm descendant being the TEcoSpace reference passed to the constructor. We also force a refresh of the Contacts list view when the modal dialog is closed, so any Contact updates are shown.

    Summary

While it is generally assumed that WinForms must be used as the presentation framework when developing ECO desktop applications, there is no reason why the VCL.NET framework cannot be used instead.

All the usual ECO services and sub-frameworks are still available to VCL.NET applications, with the only features missing being the ability to bind to graphical user interface elements, and the features provided by the ECO WinForm extenders.

This article demonstrated how easy it is to create an ECO model and use the VCL.NET framework to create a user interface to interact with the run-time instances of classes within this model.

It also demonstrated how to bind a list of ECO objects to standard VCL.NET data aware controls by using the TListConnector class.

The source code to accompany this article can be downloaded from CodeCentral.

    Acknowledgements

Thanks to Kostas Terzides for giving me direction about using the TListConnector class, and to John Kaster for his help in getting this article and its revisions published. And of course, thanks to the Borland ECO team for creating such a wonderful framework to work with.


Server Response from: ETNASC03