Design-Time Integration—Custom Type Code Serialization with TypeConverters
Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio
| CSharp-Online.NET:Articles |
| C# Articles |
|
Design-Time Integration of Windows Forms Components
|
| © 2004 Chris Sells |
Custom Type Code Serialization with TypeConverters
Although the Hand type now plays nicely with the Property Browser, it doesn't yet play nicely with code serialization. In fact, at this point it's not being serialized to InitializeComponent at all. To enable serialization of properties exposing complex types, you must expose a public ShouldSerialize<PropertyName> method that returns a Boolean:
public class ClockControl : Control { public Hand SecondHand { ... } bool ShouldSerializeSecondHand() { // Only serialize nondefault values return( (secondHand.Color != Color.Red) || (secondHand.Width != 1) ); } ... }
Internally, the Designer looks for a method named ShouldSerialize <PropertyName> to ask whether the property should be serialized. From the Designer's point of view, it doesn't matter whether your ShouldSerialize<PropertyName> is public or private, but choosing private removes it from client visibility.
To programmatically implement the Property Browser reset functionality, you use the Reset<PropertyName> method:
public Hand SecondHand { ... } void ResetSecondHand() { SecondHand = new Hand(Color.Red, 1); }
Implementing ShouldSerialize lets the design-time environment know whether the property should be serialized, but you also need to write custom code to help assist in the generation of appropriate InitializeComponent code. Specifically, the Designer needs an instance descriptor, which provides the information needed to create an instance of a particular type. The code serializer gets an InstanceDescriptor object for a Hand by asking the Hand type converter:
public class HandConverter : ExpandableObjectConverter { public override bool CanConvertTo( ITypeDescriptorContext context, Type destinationType) { // We can be converted to an InstanceDescriptor if( destinationType == typeof(InstanceDescriptor) ) return true; return base.CanConvertTo(context, destinationType); } public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { if( value is Hand ) { // Convert to InstanceDescriptor if( destinationType == typeof(InstanceDescriptor) ) { Hand hand = (Hand)value; object[] properties = new object[2]; Type[] types = new Type[2]; // Color types[0] = typeof(Color); properties[0] = hand.Color; // Width types[1] = typeof(int); properties[1] = hand.Width; // Build constructor ConstructorInfo ci = typeof(Hand).GetConstructor(types); return new InstanceDescriptor(ci, properties); } ... } return base.ConvertTo(context, culture, value, destinationType); } ... }
To be useful, an instance descriptor requires two pieces of information. First, it needs to know what the constructor looks like. Second, it needs to know which property values should be used if the object is instantiated. The former is described by the ConstructorInfo type, and the latter is simply an array of values, which should be in constructor parameter order. After the control is rebuilt and assuming that ShouldSerialize<PropertyName> permits, all Hand type properties will be serialized using the information provided by the HandConverter-provided InstanceDescriptor:
public class ClockControlHostForm : Form { ... void InitializeComponent() { ... this.clockControl1.HourHand = new ClockControlLibrary.Hand(System.Drawing.Color.Black, 2); ... } }
Type converters provide all kinds of help for the Property Browser and the Designer to display, convert, and serialize properties of custom types for components that use such properties.
|

