I've been spending a bunch of time today doing some customization of some ASP.NET custom controls (yes, I know!). One of the things I needed to get working was decent design time support for a property of the control that returned an array of a simple custom structure.

[Browsable(true)]
public MyStruct[] StructData {
   get { return _structData; }
   set { _structData = value; }
}

This should've been simple, but it took me a far longer time than I expected to get working correctly. Getting the basic design time work done wasn't too much of a problem; since the property type was an array and not a custom collection, the default editors did the right thing and offered a good design time experience out of the box.

However, you still need to provide the designers and ASP.NET a way to store the property contents as an string, so that you can use markup to specify the value of the property.

Time to write a TypeConverter! I went ahead and wrote a simple TypeConverter that would take a string and return an array of structs based on its contents, or take an array and return a string based on the values in it. Nothing fancy.

I then enabled the type converter by using the TypeConverterAttribute on the property declaration:

[Browsable(true),
TypeConverter(typeof(MyStructArrayConverter))]
public MyStruct[] StructData {
   get { return _structData; }
   set { _structData = value; }
}

Once I added this, the designer worked right away. I could change the array contents in the designer, and it would get serialized into the aspx page markup correctly. Loading the page worked as well and all values would be correctly deserialized.

I thought I was home free until I tried to actually build the ASP.NET site (this was on a VS WebSite, not a Web Application Project). As soon as the ASP.NET compiler tried to compile the ASPX page hosting the control, it would error out with the dreaded error message:

Page.aspx (web): Object reference not set to an instance of an object.

That's it. No stack trace, no more details at all. I knew it was related to the TypeConverter, but that one was working correctly (already tested and verified). Checking further, I came to the conclusion that the error would appear every time that my TypeConverter.ConvertFrom() implementation would return an array with a non-zero size (useful, huh?).

So the array conversion was working perfectly, but ASP.NET was choking on the values contained in the array. At this point I realized the problem wasn't with the TypeConverter itself, but with how the ASP.NET was manipulating the values returned to generate the code from the ASPX page.

After much head-scratching and finding little explicit documentation about the issue I ran into this documentation piece on MSDN. The last section in the article talks about how "To implement a type converter that produces constructor-based property initialization code".

Based on that, I wrote my yet another custom TypeConverter that, following the guidelines on the article, implemented ConvertTo to convert from a MyStruct value to a corresponding InstanceDescriptor object. I applied this second TypeConverter to the struct declaration itself:"

[Serializable,
TypeConverter(typeof(MyStructConverter))]
public struct MyStruct {
   //...
}

And lo and behold, this time it worked perfectly: No complication errors anymore! Hopefully, next time I run against this and have forgotten again all about TypeConverters and its brethren, I'll remember to look here for hints :-).

Technorati tags: ,


Tomas Restrepo

Software developer located in Colombia.