Introduction
Add design-time support for your custom collection of custom objects in 10 minutes.
The sample above adds a MyList
of ImageEntry
objects to a Usercontrol
, and displays them one at a time in a picturebox (using a timer). It took me a full 20 minutes to write that entire program.
Background
For my image transitions project, I needed to be able to pre-load a collection of images at design time. Originally, I did this by creating a TypeConverter
for the ImageEntry
type, and loading that into the list. This was difficult to implement, and really never did work particularly well (sometimes I was losing the images that had been loaded, and I never did figure out why).
Recently, I needed to upgrade the project so that the list of images was thread-safe, and after creating this thread-safe list of ImageEntry
objects (TList<ImageEntry>
), I just could not get them into the designer. All the articles I read were talking about creating custom UITypeEditor
forms, creating full implementations of ITypeConveter
objects or implementing IComponent
on my object, etc. They were all very complicated.
Eventually, I got to the bottom of what everyone was saying, and why, and came up with this.
Using the Code
There are three things to do to enable your list to appear in the designer:
- Prepare the list
- Prepare the content type
- Decorate the property
Preparing the List
Most articles describing the creation of design-time collections support inherited their collections from CollectionBase
. This is all well and good, but not always appropriate for any number of reasons. Your collection may already be inherited from another object, and multi-inheritance is not permitted. Or perhaps, you want your internal collection to have more features than the CollectionBase
offers, which was the case with my TList
.
The bottom line is you have two choices here. First, you can inherit from CollectionBase
if that works for you, or second, you need to implement the non-generic IList
interface. This second option was the thing that had me stuck for ages, as none of the existing articles mentioned it. My TList
implemented IList<ImageEntry>
, ICollection<ImageEntry>
and IEnumerable<ImageEntry>
, but when the collections form appeared in the designer, I still could not add my ImageEntry
objects to the collection.
Implementing the non-generic IList
was easy - I just passed all the calls through to the Generic implementation. Took me all of 2 minutes.
Preparing the Content Type
Once again, most of the existing articles go into a lot of detail about creating a TypeConverter
for your collection's content type. I found this very difficult to get right, and spent hours just playing around with this to get it working with some semblance of usability. They all seem to glance over the fact that you can alternatively implement IComponent
to have your object appear in the designer. IComponent
is very easy to implement (one field and one property is all that's needed), or even simpler than that, if possible, you can inherit your object from Component
, and that implementation is done for you.
The only problem with using the IComponent
is that your object tends to appear all over the place in the designer. This is solvable, however, with one line of decoration mentioned below.
Decorating the Property
When adding the collection as a property of your control, there are 2 obligatory attributes required - the [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
and the Editor(typeof(CollectionEditor), typeof(UITypeEditor))
attributes. Once that is done, everything just works.
In order to prevent every instance of your content appearing in the designer, you just need to decorate your object with the DesignTimeVisible(false)
attribute.