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.