Windows Develop Bookmark and Share   
 index > Windows Forms Designer > UserControl serializing embedded classes
 

UserControl serializing embedded classes

I have a user control that has an embedded class:

Code Snippet

public class MyUserControl : UserControl {

//

// A list of custom attributes of this control

//

private List<MyAttribute> myAttributes = new List<MyAttribute>();



//

// A public property so form designer can

// modify it

//

public List<MyAttribute> MyAttributeProperty {

get { return myAttributes; }

set { myAttributes = value; }

}


[Serializable]
public class MyAttribute
{
private int x;
private int y;

public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}
...
}



Then I created a form that has a MyUserControl on it. This works great; the control shows up in the toolbox, I can place it on a form, and the form designer can see the property. It can also add new elements to the list.

However, once I rebuild the control, the form editor will no longer edit my form; in fact, I get a big red X with an error message talking about serialization.

I attempted to rectify this by implementing GetObjectData() and a de-serialization constructor. I verified that these methods do indeed get called by the form designer. What I infer is that the form designer realizes that the object has changed and that the former persistent copy is no longer valid.

Can anybody explain what I am doing wrong, or, even better, point me to some documentation or examples that show how to have a List<ofSomething> property in a UserControl that interacts correctly with the form designer?

--Chris





wz2b  Tuesday, May 01, 2007 7:54 PM
I'm banging my head against the exact same issue here. Is there no-one that can answer this question?
mchagers  Tuesday, May 22, 2007 10:28 AM

Hi . Simply your control have some control that is not Serializable such as DataGridView or some else . So you can try to replace this unSerializable control with another one or even use previuos versions of this control if there is prevuios version of it which may be serializable such as DataGrid Instead of DataGridView .

If you send me your Control i can help you more to solve your problem , because i faced it before in custom control .I maked custom control with TableLayOut Control, Then i wanted to serialize it to file on disktop & it generated error like yours . So either check your controlor send mecome code or your control & i will check it for you & search for solutions for it .

Pr.Wael  Saturday, May 26, 2007 11:55 AM
I have been having the same problem. I believe the difficulty lies in how Designer handles an accessor to a List<T> type. I think it gets confused at trying to create a collection of a custom type in the little designer properties window, but I'm not sure exactly what the problem is. I just know it's very annoying!

I circumvented the problem by having functions to set/get the List<T> object instead of having accessors. So instead of:

//

// A list of custom attributes of this control

//

private List<MyAttribute> myAttributes = new List<MyAttribute>();


//

// A public property so form designer can

// modify it

//

public List<MyAttribute> MyAttributeProperty {

get { return myAttributes; }

set { myAttributes = value; }

}



I have:

//

// A list of custom attributes of this control

//

private List<MyAttribute> myAttributes = new List<MyAttribute>();


//

// A public property so form designer can

// modify it

//

public List<MyAttribute> GetMyAttribute()

{

return myAttributes;

}

public void SetMyAttribute(MyAttribute a)

{

myAttributes = a;

}


I realize this is coming about a year late, but it might help others who have the same problem.
E Reynolds  Wednesday, May 28, 2008 6:41 PM

Chris,

What's going on:

TheMyAttributeProperty is a generic List.When you add elements to this list it signals the designer that it needs to persist the value of the property. The value is persisted in the Form's resource file using a binary serializer:

Code Snippet

<data name="myUserControl1.MyAttributeProperty" mimetype="application/x-microsoft.net.object.binary.base64">

<value>

AAEAAAD/////AQAAAAAAAAAMAgAAAEpXaW5kb3dzQXBwbGljYXRpb24yLCBWZXJzaW9uPTEuMC4wLjAs

IEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAQBAAAAkAFTeXN0ZW0uQ29sbGVjdGlv

bnMuR2VuZXJpYy5MaXN0YDFbW1dpbmRvd3NBcHBsaWNhdGlvbjIuTXlBdHRyaWJ1dGUsIFdpbmRvd3NB

cHBsaWNhdGlvbjIsIFZlcnNpb249MS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tl

bj1udWxsXV0DAAAABl9pdGVtcwVfc2l6ZQhfdmVyc2lvbgQAACFXaW5kb3dzQXBwbGljYXRpb24yLk15

QXR0cmlidXRlW10CAAAACAgJAwAAAAMAAAANAAAABwMAAAAAAQAAAAQAAAAEH1dpbmRvd3NBcHBsaWNh

dGlvbjIuTXlBdHRyaWJ1dGUCAAAACQQAAAAJBQAAAAkGAAAACgUEAAAAH1dpbmRvd3NBcHBsaWNhdGlv

bjIuTXlBdHRyaWJ1dGUCAAAABnhWYWx1ZQZ5VmFsdWUAAAgIAgAAAAAAAAAAAAAAAQUAAAAEAAAAAAAA

AAAAAAABBgAAAAQAAAAAAAAAAAAAAAs=

</< FONT>value>

</< FONT>data>

Why you're getting an error:

You probably have the version information for your assembly set to auto-increment. Therefore, when you rebuild your application, the version of the types contained in your assembly are now different. When the designer deserializes this value it throws an exception, because the types are no longer identical. You should be getting a type conversion exception:

Object of type 'WindowsApplication2.MyAttribute[]' cannot be converted to type 'WindowsApplication2.MyAttribute[]'.


You could turn off the auto-incrementation of the assembly's version, but that can cause further problems, especially when writing custom controls. (i.e. the Windows Forms Designer caches types, designers, converters, and editors. If you aren't incrementing the version, the Windows Forms Designer might not reload these elements.)

Solution:

Step 1:

First, instruct the designer to serialize the elements of the list and not the list itself by using the DesignerSerializationVisibilityAttribute:

Code Snippet

[DesignerSerializationVisibility( DesignerSerializationVisibility.Content )]

public List<MyAttribute> MyAttributeProperty

{

get

{

return myAttributes;

}

set

{

myAttributes = value;

}

}

This solves the problem and the designer produces the following output:

Code Snippet

this.myUserControl1.MyAttributeProperty.Add(((WindowsApplication2.MyAttribute)(resources.GetObject("myUserControl1.MyAttributeProperty"))));

this.myUserControl1.MyAttributeProperty.Add(((WindowsApplication2.MyAttribute)(resources.GetObject("myUserControl1.MyAttributeProperty1"))));

this.myUserControl1.MyAttributeProperty.Add(((WindowsApplication2.MyAttribute)(resources.GetObject("myUserControl1.MyAttributeProperty2"))));

And in the form's resource file:

Code Snippet

<data name="myUserControl1.MyAttributeProperty" mimetype="application/x-microsoft.net.object.binary.base64">

<value>

AAEAAAD/////AQAAAAAAAAAMAgAAAEpXaW5kb3dzQXBwbGljYXRpb24yLCBWZXJzaW9uPTEuMC4wLjAs

IEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAH1dpbmRvd3NBcHBsaWNhdGlv

bjIuTXlBdHRyaWJ1dGUCAAAABnhWYWx1ZQZ5VmFsdWUAAAgIAgAAAAEAAAAAAAAACw==

</< FONT>value>

</< FONT>data>

<data name="myUserControl1.MyAttributeProperty1" mimetype="application/x-microsoft.net.object.binary.base64">

<value>

AAEAAAD/////AQAAAAAAAAAMAgAAAEpXaW5kb3dzQXBwbGljYXRpb24yLCBWZXJzaW9uPTEuMC4wLjAs

IEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAH1dpbmRvd3NBcHBsaWNhdGlv

bjIuTXlBdHRyaWJ1dGUCAAAABnhWYWx1ZQZ5VmFsdWUAAAgIAgAAAAAAAAAAAAAACw==

</< FONT>value>

</< FONT>data>

<data name="myUserControl1.MyAttributeProperty2" mimetype="application/x-microsoft.net.object.binary.base64">

<value>

AAEAAAD/////AQAAAAAAAAAMAgAAAEpXaW5kb3dzQXBwbGljYXRpb24yLCBWZXJzaW9uPTEuMC4wLjAs

IEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49bnVsbAUBAAAAH1dpbmRvd3NBcHBsaWNhdGlv

bjIuTXlBdHRyaWJ1dGUCAAAABnhWYWx1ZQZ5VmFsdWUAAAgIAgAAAAAAAAAAAAAACw==

</< FONT>value>

</< FONT>data>

Step 2: Improvements

Perhaps you've noticed that some properties in windows formshave designer-generated code that looks like the following:

Code Snippet

this.myUserControl1.Size = new System.Drawing.Size(150, 150);

This ismuch more readable than a line of code that loads binary data from a resource file, so let's instruct the designer to serialize our class this way. We do that by implementing a type converter and linking our coverter to our class.

Implementation of the TypeConverter:

Code Snippet

public class MyAttributeConverter : TypeConverter

{

public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)

{

if (destinationType == typeof(InstanceDescriptor))

{

return true;

}

return base.CanConvertTo(context, destinationType);

}

public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)

{

if (destinationType == typeof(InstanceDescriptor))

{

ConstructorInfo ci = typeof(MyAttribute).GetConstructor(new Type[] { typeof(int), typeof(int) });

MyAttribute a = (MyAttribute)value;

return new InstanceDescriptor(ci, new object[] { a.x, a.y });

}

return base.ConvertTo(context, culture, value, destinationType);

}

}

Associating the TypeConverter with our class:

Code Snippet

[Serializable(),TypeConverter(typeof(MyAttributeConverter))]

public class MyAttribute

{ ...

After changing the elements in the property the designer removes the binary data from the resource file, and produces the following code in the auto-generated file:

Code Snippet
...

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(0, 30));

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(0, 0));

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(0, 0));

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(55, 0));

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(0, 0));

this.myUserControl1.MyAttributeProperty.Add(new WindowsApplication2.MyAttribute(0, 0));

...

This is much more readable and won't cause versioning errors prone to binary serialization.

Nathan W Jones

Senior Software Engineer

Murata Machinery Inc.

See Also:

TypeConverters:

http://msdn.microsoft.com/en-us/library/ayybcxe5(VS.71).aspx

InstanceDescriptors:

http://msdn.microsoft.com/en-us/library/system.componentmodel.design.serialization.instancedescriptor(VS.71).aspx

lpszJones  Thursday, June 05, 2008 5:12 PM

You can use google to search for other answers

Custom Search

More Threads

• ComboBox.ClientSize.Width - broken?
• SnapToGrid and GridSize in custom form designer
• Custom control newbie needs help.
• how to NOT go to the designer
• Inheriting from DocumentDesigner
• MDI child and parent
• Using IMenuCommandService
• Accessing the MDI Parent form controls from child forms
• Repetitive Text Inserting in Internet Explorer
• Controls in MDI form