poor man's copy constructor

  • Thread starter Thread starter Hamilton Woods
  • Start date Start date
H

Hamilton Woods

I am attempting to copy a Windows Form Control. I can do this by manually
creating a control, Label2 = new Label, and then iterating through all of
the properties, copying from Label1 to Label2. A Mr. Jon Box, with Quilogy,
Inc., steered me to reflection for a possible mechanism to make this more
generic. I have a partial solution, but can't get all the way there. So I
am asking for help.

Windows Form Controls do not implement ICloneable or ISerializable, so I
can't write a control to disk and read it back in to another control, nor
can I do even a shallow copy. Bummer.

I am lazy, so I want to write a piece of code that I can REUSE. In a
perfect world, I would not even have to use a SELECT CASE statement to
determine the class of the original object that I am trying to copy. I
think I would not be the only person interested in said piece of code.

Here's what I sent Mr. Box this morning:

Jon,

A follow-up on yesterday's email. Based on your steering me to reflection,
here's what I have so far. You can create a form, place a Button on that
form, and paste the following two procedures into the code section.

This is the event handler for a Button.

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Dim newButton As Button
Dim sourceButton As Button

sourceButton = CType(sender, Button) ' cast the source as its type
newButton = CType(CopyObject(sourceButton), Button) ' call the function
that does the copy
newButton.Top = sourceButton.Top + sourceButton.Height ' move the
newButton to a new location so we can see it
newButton.Visible = True ' make sure it is visible
AddHandler newButton.Click, AddressOf Me.Button1_Click ' allow
Button1_Click to handle newButton's Click event
Me.Controls.Add(newButton) ' add the newly created control to the
controls collection
newButton = Nothing
End Sub

The event handler calls CopyObject. CopyObject returns an object (Button)
whose properties are the same as the original.

Public Function CopyObject(ByVal obj As Object) As Object
Dim strObjType As String
'Dim strPropertyName As String
Dim srcPropertyArray As Reflection.PropertyInfo()
Dim srcPropertyElement As Reflection.PropertyInfo
Dim newObject As Object

strObjType = obj.GetType().Name
srcPropertyArray = obj.GetType().GetProperties() ' retrieve all
properties of source object
Select Case obj.GetType.Name ' I don't know another way of creating a
new object of a specific type.
Case "Label"
newObject = New Label()
Case "Button"
newObject = New Button()
Case "CASE for each Windows Forms Control"
End Select
For Each srcPropertyElement In srcPropertyArray ' iterate through the
list of properties
'strPropertyName = srcPropertyElement.Name
If srcPropertyElement.CanRead And srcPropertyElement.CanWrite Then ' if
the property can be read from and written to,
srcPropertyElement.SetValue(newObject, srcPropertyElement.GetValue(obj,
Nothing), Nothing) ' copy the value to the new object
End If
Next
Return newObject
End Function

I think the properties get copied, but the newly created control does not
show up on the form. I tried this with a Label control on which I paint a
wireframe using gdi+ and added the Paint event handler, but the Paint event
doesn't work properly. I get a Label which is white and it doesn't respond
to the click event. I don't know if I need to do anything to copy event or
method information from one object to another. I wouldn't think so. I'll
also post this to some usenet news groups to see if I get any response.

Thanks for your help,
Hamilton Woods
Accutrol, LLC
7500 S. Memorial Pkwy, 215-V
Huntsville, AL 35802
256-882-7433
(e-mail address removed)
 
I found this thing called InstanceDescriptor which can be used to Invoke a
new object of the same type as a source object. The complete package still
does not work. However, I get the same result when I instantiate a Label
using Label2 = new Label. Therefore, it appears that the problem is in the
copying of properties from one object to the other. I suspect that I need to
filter which properties I copy, but I don't know what filter to apply. Any
suggestions?

Dim srcPropertyArray As Reflection.PropertyInfo()
Dim srcPropertyElement As Reflection.PropertyInfo
Dim Instantiator As
System.ComponentModel.Design.Serialization.InstanceDescriptor
Dim ci As System.Reflection.ConstructorInfo =
SourceObject.GetType.GetConstructor(Type.EmptyTypes)
Instantiator = New
System.ComponentModel.Design.Serialization.InstanceDescriptor(ci, Nothing)
newObject = Instantiator.Invoke '
instead of SELECT CASE statement
srcPropertyArray = obj.GetType().GetProperties()
For Each srcPropertyElement In srcPropertyArray
If srcPropertyElement.CanRead And srcPropertyElement.CanWrite Then
srcPropertyElement.SetValue(newObject,
srcPropertyElement.GetValue(obj, Nothing), Nothing)
End If
Next

Thanks,
Hamilton Woods
Accutrol, LLC
7500 S. Memorial Pkwy, 215-V
Huntsville, AL 35802
256-882-7433
(e-mail address removed)
 
Back
Top