Do you really need a custom CodeDomSerializer?
When you author a new component for .NET for which you want to offer a smooth user experience within the Visual Studio designers, you may find you need to write a few more classes to get it working the way you want. For example, you may need to write a designer for it to customize how it looks and behaves at design time. You may need to write TypeConverters for new types that you add, so that, among other things, they can be configured from the property grid. Maybe you will need to write a UITypeEditor.
Sometimes, you may feel you need to write a custom CodeDomSerializer for the component too. Now, the CodeDomSerializer class hierarchy is definitely designed to be extensible and customizable. In fact, in Whidbey, we have refactored this class hierarchy a bit to expose more common functionality you may need in your custom serializer.
However, before you start writing a custom CodeDomSerializer, you should ask yourself this question: do I really need a custom CodeDomSerializer? After all, very very few of the built in controls and components in Windows Forms have their own custom serializers. Even for the 2-3 components that do, they mostly do only a few lines of work and delegate the rest to the base serializers.
Let me first explain why I believe you should think twice before writing a custom CodeDomSerializer:
- You may not need it in the first place. I will describe below some ways in which you can customize code generation without writing a custom serializer.
- Writing a well behaved custom serializer is not trivial. There are many things you need to be careful about. For example, what happens when your component is placed on a Localizable Form? What about if it is in a collection or array? What if someone derives from it and configures some metadata differently? Does your serializer work fine when invoked in a different context than normal code generation - like for undo/redo (in Whidbey)?
- What if your component is hosted in a design time environment that doesn't use CodeDom at all? For example, the component may be hosted on a DesignSurface that has a DesignerLoader that uses an xml based serialization scheme (like Xaml). Your custom CodeDomSerializer will not work in this situation, and serialization may not happen as you expect it to.
How then do you customize code generation? Well, it is mostly through the use of metadata. Here are a few possibilities:
- The DesignerSerializationVisibilityAttribute helps you hide properties that you don't want serialized from the serializer and also tell it not to serialize the value, but only its content (typically useful for collections).
- The DefaultValueAttribute and ShouldSerializeBlah methods (where 'Blah' is the name of a property) help control under what conditions to serialize a particular property.
- The LocalizableAttribute controls whether a property is serialized into code or into the resource file when the component is on a Localizable Form.
- The DesignOnlyAttribute, as the name suggests, can be applied to properties that are only meaningful at design time and don't need to be serialized as runtime property sets.
- You can write a TypeConverter for your component (or any new type you have authored) that knows how to convert to InstanceDescriptor to control how objects of the type are instantiated by the serializer.
- You can also control how objects are serialized into resx files by writing a TypeConverter that can convert to and from string, for example.
Now, I do understand there are legitimate scenarios where you will need to implement custom CodeDomSerializers, and this post is not meant to discourage doing so (in fact, as I said, we have made it easier in Whidbey to write these). However, before you implement one, just remember to consider the issues and suggestions mentioned here. I repeat: for our own components, we have extremely few that implement custom serialization and when they do, the serializer is only a few lines of code and lets the base serializer do most of the work.
See also this post that explains some serialization rules. If you are new to this topic, you will find this excellent article by Shawn Burke very useful.
Comments
- Anonymous
April 03, 2005
The comment has been removed - Anonymous
April 03, 2005
The comment has been removed - Anonymous
April 03, 2005
The comment has been removed - Anonymous
April 06, 2005
The comment has been removed - Anonymous
April 24, 2005
Consider the following code:
public class MyControl : Control {
[TypeConverter(typeof(SomePropertyConverter))]... - Anonymous
June 11, 2005
I'm trying to implement a web control that references another control on the page. Visual Studio correctly finds the available instances to select from automatically, but, because it is an object reference, the selected value isn't serialized to the aspx page and the default serializer doesn't serialize properties to the InitializeComponent method. I could tell users to data-bind the property t the variable name, but I don't want to make them do that. So, I'd like to serialize that property, and only that property, to the InitializeComponent method. Since a varible is declared in the .cs file when the control is added, there must be some element of serialization that happens on that file instead of the .aspx page, but I can't find any references to it in documentation. Am I making this too hard? Do I need a custom CodeDomSerializer? - Anonymous
June 16, 2005
jcard: Your question seems specific to ASP.NET and I don't have a lot of context in that area. I would recommend asking it on one of the ASP.NET forums and someone should be able to help you. - Anonymous
August 28, 2005
I want to make some properties serializable depending on the form context. As i can't make the attribute DesignerSerializationVisibility depend on the control states, i decided to create a custom CodeDomSerializer which removes the uneeded properties from serialization. It works but i'm unable to prevent the uneeded propreties from being saved to the resources (and applied in InitializeComponent with ApplyResources). And i can't figure out when the form serializer decides to save the properties to code or to resources. It doesn't depend on the number of properties or the number of controls to serialize !?!? - Anonymous
August 29, 2005
Properties marked Localizable(true) are serialized to resources when you set the Localizable property on the form to True.
If you want to dynamically control serialization of properties, an easier mechanism would be to implement ShouldSerialize methods. This post may be of help: http://blogs.msdn.com/rprabhu/archive/2004/02/03/66560.aspx - Anonymous
August 29, 2005
Thank you for your answer.
1. Sometimes when a property is marked Localizable(true) (for example the location and size properties) it is either serialized to code or to resources. Depending on the form the serialization for the same control is performed to resource or to code. Is there another rule ?
2. I have already tried to use ShouldSerializeXXX to remove a property from serialization but it doesn't work. Here is my code, BackColor and Location are still serialized when i use the UserControl :
public class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public bool ShouldSerializeBackColor()
{
return false;
}
public bool ShouldSerializeLocation()
{
return false;
}
} - Anonymous
August 30, 2005
The comment has been removed - Anonymous
November 16, 2005
The comment has been removed - Anonymous
November 21, 2005
The comment has been removed - Anonymous
December 01, 2005
The comment has been removed - Anonymous
December 01, 2005
The comment has been removed - Anonymous
December 29, 2005
I try to serialize reference to IList.
I can select lists from drop down list in designer using ReferenceConverter but my IList propery never serialized until its value become null.
IList _list;
[TypeConverter(typeof(MyListConverter))]
public IList List
{
get { return _list; }
set { _list = value; }
}
class MyListConverter : ReferenceConverter
{
public MyListConverter(Type type) : base(type) {}
protected override bool IsValueAllowed(ITypeDescriptorContext context, object value)
{
return value is IList;
}
}
Thanks - Anonymous
January 18, 2006
Demid: There is no built in serializer for a generic IList. I recommend using a custom type that implements IList instead, and attaching a TypeConverter to it that can convert to/from instance descriptor. See also this post: http://blogs.msdn.com/rprabhu/archive/2005/04/25/411615.aspx.