3
votes

I have a form that has two combo boxes, both of which contain the same list of items, and need to remain perfectly in sync with one another. (They represent the same list of options on two different tabs of a TPageControl.)

To make this work, I set up a LiveBinding to bind both controls to the same field of a ClientDataset that exists for no other purpose than to keep controls on this form synchronized. My TBindingsList has a TLinkControlToField for both combo boxes, linking them both to the same field.

Everything works fine, as long as I make all changes within the GUI.

However, if an unrelated action changes the selection state of one of the boxes, they get out of sync:

cboMainValue.Items.InsertObject(0, 'ALL', TObject(-1));
cboAltValue.Items.Clear;
cboAltValue.Items.Assign(cboMainValue.Items);
cboMainValue.ItemIndex := 0;
cboAltValue.ItemIndex := 0;

After this point, for whatever reason, cboMainValue shows the expected text, while cboAltValue remains blank (ie ItemIndex = -1).

I've tried setting the Text property rather than ItemIndex, and editing the value of the backing field on the ClientDataset, but none of these produces a different result.

Does anyone know how to programmatically change the state of one combo box and make the LiveBindings update the other one to match it?

1
An alternative to having two combos, could be to have only one and change its parent in the TPageControl.OnChange event. The need to sync would not exist anymore.Tom Brunberg

1 Answers

1
votes

I'm tried your code in Seattle in a newly created project and it doesn't exhibit the problem you describe. I'm posting it as an answer as it's pretty minimal in the MCVE sense, does most of its set-up in code (so there are no "funnies" lurking in the DFM), and might allow you to "spot the difference" compared with yours.

So I think the literal answer to your q is "The way you're doing now." Good luck!

Btw, personally I think that Tom Brunberg's suggestion might be a better way to go, but obviously it would be good to get to the bottom of what's causing the problem in your project.

Code:

type
  TForm1 = class(TForm)
    CDS1: TClientDataSet;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    CDS1ID: TIntegerField;
    CDS1Value: TStringField; // String 20 field
    cboMainValue: TComboBox;
    BindSourceDB1: TBindSourceDB;
    DBNavigator1: TDBNavigator;
    cboAltValue: TComboBox;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
    LinkControlToField1: TLinkControlToField;
    LinkControlToField2: TLinkControlToField;
  end;
[...]
procedure TForm1.Button1Click(Sender: TObject);
begin
  cboMainValue.Items.InsertObject(0, 'ALL', TObject(-1));
  cboMainValue.Items.InsertObject(1, 'Other', TObject(-1));
  cboAltValue.Items.Clear;
  cboAltValue.Items.Assign(cboMainValue.Items);
  cboMainValue.ItemIndex := 0;
  cboAltValue.ItemIndex := 0;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  cboMainValue.Items.Insert(0, 'apple');
  cboMainValue.Items.Insert(1, 'orange');
  cboMainValue.Items.Insert(2, 'banana');
  cboAltValue.Items.Assign(cboMainValue.Items);

  LinkControlToField1 := TLinkControlToField.Create(Self);
  LinkControlToField1.DataSource := BindSourceDB1;
  LinkControlToField1.FieldName := 'Value';
  LinkControlToField1.Control := cboMainValue;

  LinkControlToField2 := TLinkControlToField.Create(Self);
  LinkControlToField2.DataSource := BindSourceDB1;
  LinkControlToField2.FieldName := 'Value';
  LinkControlToField2.Control := cboAltValue;

  CDS1.CreateDataSet;
  CDS1.InsertRecord([1, 'apple']);
  CDS1.InsertRecord([2, 'banana']);
  CDS1.InsertRecord([3, 'orange']);
end;