0

I have X (more than 1) comboboxes declared on a form. (Designtime)

All these comboboxes have the same properties (except position, handle, and a few others they can't share)

I would to give them all the same behavior during runtime, which means if e.g. I add/delete an item or change the ItemIndex or stuff like that, then all comboboxes should do the same.

How can I "clone" all properties/events/etc. from one component at runtime to X other components without doing an operation over and over again for each component?

Ben
  • 3,380
  • 2
  • 44
  • 98
  • which Delphi version? – Jens Borrisholt Apr 09 '15 at 08:54
  • @JensBorrisholt Delphi 2010 (added tag) – Ben Apr 09 '15 at 08:59
  • Make a function that iterates all comboboxes and set properties/events accordingly. You still have to find all comboboxes inside that function, but the task is encapsulated. – LU RD Apr 09 '15 at 09:10
  • 1
    As a variant you can get `Form1.Components` array and its size `Form1.ComponentCount`. Then using operators **is** and **as** filter your ComboBox component and do needed operations. – 0xFF Apr 09 '15 at 09:19
  • I'd simply create them all at runtime – David Heffernan Apr 09 '15 at 09:21
  • @0xFF This sounds like a good idea. David This works for a one time task, further changes won't apply. I guess I have no choice but to iterate (at least once) and find a way to specify. – Ben Apr 09 '15 at 09:27
  • Also you can create private array of ComboBoxes. In `FormCreate` you'll add needed ComboBox in array. All listener procedures like `OnClick` you should setup identical for all your ComboBoxes manually. Then in code you can update all changed properties for all elements of private array. – 0xFF Apr 09 '15 at 09:41
  • You could go and make your own custom component which will use external class or classes to store all data that is common for your custom components. So basically your component will be reading data from fields of that external component instead from its own fields. Main advantages of this are that you are not duplicating the same (common) data for different components and that you only need to change data only on one place to affect all of your components. The main drawback is that your need to make your components constantly checking to see if data has changed or notify them of the change. – SilverWarior Apr 09 '15 at 10:30
  • Now I'm not on my development machine so I can't show you an example but I could do this when I get home. Unfortunately I don't know the ComboBox implementation so my example will not show exactly what changes you need to make to that specific component instead it will be more general example of how you can use external classes for data reusability. – SilverWarior Apr 09 '15 at 10:34
  • @Benjamin What's the problem? If you need to make a change, make it. You'll have to do work no matter if you change something. – David Heffernan Apr 09 '15 at 19:53

2 Answers2

2

You can use ReadComponent and WriteComponent from TStream too.

procedure TForm1.Button1Click(Sender: TObject);
var
  oStream: TMemoryStream;
  i: integer;
  cbCombos: array[0..4] of TComboBox;
begin
  oStream := TMemoryStream.Create;
  ComboBox1.Tag := '666'; { \m/ }
  try
    oStream.WriteComponent(ComboBox1);
    for i := 0 to 4 do
    begin
      cbCombos[i] := TComboBox.CreateParented(Self.Handle);
      oStream.Position := 0;
      oStream.ReadComponent(cbCombos[i]);
      cbCombos[i].Name := 'AnotherComboBox' + IntToStr(i+1);
      cbCombos[i].Parent := Self;
      cbCombos[i].Tag := cbCombos[i].Tag + i + 1;
      cbCombos[i].Left := 16;
      cbCombos[i].Top := 36 * (i + 2);
      cbCombos[i].OnMouseEnter := ComboBox1MouseEnter;
    end;
  finally
    FreeAndNil(oStream);
  end;
end;

procedure TForm1.ComboBox1MouseEnter(Sender: TObject);
begin
  TWinControl(Sender).Hint := IntToStr(TWinControl(Sender).Tag);
end;
oPsDCadarn
  • 106
  • 3
1

You can do that via Extended RTTI

This is a start - by no means complete:

procedure TForm62.CloneComponent(const aSource, aDestination: TComponent);
var
  ctx: TRttiContext;
  RttiType, DestType: TRttiType;
  RttiProperty: TRttiProperty;
  Buffer: TStringlist;

begin
  if aSource.ClassType <> aDestination.ClassType then
    raise Exception.Create('Source and destiantion must be the same class');

  Buffer := TStringlist.Create;
  try
    Buffer.Sorted := True;
    Buffer.Add('Name');
    Buffer.Add('Handle');

    RttiType := ctx.GetType(aSource.ClassType);
    DestType := ctx.GetType(aDestination.ClassType);
    for RttiProperty in RttiType.GetProperties do
    begin
      if not RttiProperty.IsWritable then
        continue;

      if Buffer.IndexOf(RttiProperty.Name) >= 0 then
        continue;

      DestType.GetProperty(RttiProperty.Name).SetValue(aDestination, RttiProperty.GetValue(aSource));
    end;
  finally  
    Buffer.Free;
  end;
end;
Jens Borrisholt
  • 6,174
  • 1
  • 33
  • 67
  • Thank you. At least this gives me an idea on how to clone "some" properties. Good snippet, I will try to work with this :) – Ben Apr 09 '15 at 09:28