Rolandas Ulevicius
Rolandas Ulevicius

Reputation: 304

how to retain connections between controls when copying?

i want to ask how to retain controlls when im making a copy of a control. for example i have an edit box that can be controlled with a slider for value change. when i make a copy using this code i achieve a copy of the items but the slider stops controlling editbox values. how can i fix that?

 TypInfo;

procedure CloneProperties(const Source: TControl; const Dest: TControl);
var
 ms: TMemoryStream;
 OldName: string;
begin
 OldName := Source.Name;
 Source.Name := ''; // needed to avoid Name collision
 try
   ms := TMemoryStream.Create;
   try
     ms.WriteComponent(Source);
     ms.Position := 0;
     ms.ReadComponent(Dest);
   finally
     ms.Free;
   end;
 finally
   Source.Name := OldName;
 end;
end;

procedure CloneEvents(Source, Dest: TControl);
var
 I: Integer;
 PropList: TPropList;
begin
 for I := 0 to GetPropList(Source.ClassInfo, [tkMethod], @PropList) - 1 do
   SetMethodProp(Dest, PropList[I], GetMethodProp(Source, PropList[I]));
end;

procedure DuplicateChildren(const ParentSource: TWinControl;
 const WithEvents: Boolean = True);
var
 I: Integer;
 CurrentControl, ClonedControl: TControl;
begin
 for I := ParentSource.ControlCount - 1 downto 0 do
 begin
   CurrentControl := ParentSource.Controls[I];
   ClonedControl := TControlClass(CurrentControl.ClassType).Create(CurrentControl.Owner);
   ClonedControl.Parent := ParentSource;
   CloneProperties(CurrentControl, ClonedControl);
   ClonedControl.Name := CurrentControl.Name + '_';
   if WithEvents then
     CloneEvents(CurrentControl, ClonedControl);
 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 DuplicateChildren(Panel1);
end;

Upvotes: 2

Views: 114

Answers (1)

MartynA
MartynA

Reputation: 30715

Unless I'm misunderstanding you, your CloneProperties doesn't seem to have anything to do with the question you're asking. In your example of an edit control E1 and a slider S1, you can clone both of them to produce E2 and S2, but somewhere in your code there must be a statement that changes the value in E1 depending on the value of S1. However, in the way you've most likely written it, that statement doesn't apply to E2 and S2.

The simplest way around that is to write a method which takes the component instances and links the operation of the two together. e.g.

  procedure TForm1.SetEditControlFromSlider(AnEdit : TEdit; ASlider : { TWhatever the slider actually is);
  begin
    // Set AnEdit's value from ASlider's properties
  end;

Then, you can call this with Edit/Slider pairs like this

  SetEditControlFromSlider(E1, S1);
  [...]
  SetEditControlFromSlider(E2, S2);

I can imagine you might not like having to do that.

IMO, the cleanest solution is to avoid attempting to clone components altogether and create a TFrame containing the Edit, Slider and the code that connects them, and then add to your form as many instances of the frame as you need. It's as easy as falling off a log.

type
  TEditFrame = class(TFrame)  // needs to be in its own unit, Used by your form
    Edit1: TEdit;
    TrackBar1: TTrackBar;
    procedure TrackBar1Change(Sender: TObject);
  private
  public
  end;
  [...]
  procedure TEditFrame.TrackBar1Change(Sender: TObject);
  begin
    Edit1.Text := IntToStr(TrackBar1.Position)
  end;

Then, you can add clones of the frame to TForm1 by

  procedure TForm1.Button1Click(Sender: TObject);
  var
    AFrame : TEditFrame;
  begin
    Inc(FrameCount); // Field of TForm1
    AFrame := TEditFrame.Create(Self);
    AFrame.Name := AFrame.Name + IntToStr(FrameCount);
    AFrame.Parent := Self;
    AFrame.Top := AFrame.Height * FrameCount;
  end;

Note that because the code which links the two components, TrackBar1Change, it compiled into the frame's unit, it is automatically shared by every instance of the frame you create, without any need to "clone" the code.

Upvotes: 5

Related Questions