Compact Framework Controls (Part 1): Creating Custom Controls and Designers
Posted by Dan Vanderboom on March 14, 2008
I’ve been having a lot of fun learning how to create rich design-time experiences for custom controls in Compact Framework for the past few days. It’s been frustrating, and the documentation is hard to find unless you’re already familiar with the metaphors and know the buzzwords. While this first article won’t be completely comprehensive, I will continue to write about this topic until I have covered all of the bases necessary for you to create professional, polished controls and design-time experiences suitable for commercial use or sale.
I’m going to assume you have at least some experience in creating custom controls, at least for desktop applications. Creating custom controls for Compact Framework applications is another story, however. Not because of any particular constraints of memory of screen real estate, but because the typical method for attaching metadata to specify design-time behavior is different. There are some other subtleties and oddness as well that are specific to the Compact Framework.
In the full .NET Framework, we have many attributes at our disposal to specify design-time behavior; most of these can be found in System.ComponentModel. We have access to a lot of these for mobile device applications, but because they’re not included in the Compact Framework, we need another way to associate them with our control classes and their members. This is done through something called the “design-time attributes file”, which has an extension of .xtma.
There are two ways to add this file. The method that people typically suggest is to add a class diagram to your project (right-click on your project in Solution Explorer, and select View Class Diagram), select an item in the diagram, edit the Custom Attributes property in the property grid (see screenshot below), click on the ellipsis (…) to open a pop-up window, and then enter an attribute such as DesktopCompatible(true).
When you click the OK button, a new file will be added to your project called DesignTimeAttributes.xtma, and this file will open in Visual Studio.
This is quite a round-about way to create the file in my opinion, and it presumes that you know a valid attribute to add right from the beginning. I personally don’t use the class diagrams, and I think it’s easier just to add the .xtma file directly, by right-clicking on your project in Solution Explorer, Add—New Item, and selecting Design-Time Attribute File. Editing your attributes directly in this XML file instead of the pop-up window in the class designer has the benefit of providing you with some Intellisense (which it gets from the associated XSD file).
I get a little disappointed when I see people list only the most common attributes that I already know about, and then fail to mention the rest of the attributes that are supported in Compact Framework, and since the list isn’t unmanagable—and easily determined from Intellisense—I will list most of them that are supported .xtma file here, and in a future artile will provide the remaining ones.
Here’s a screenshot showing the full list of tags that you can use at the class level. Most of them are attributes, while some of them like Event, Method, and Property allow you to specify specific members of the class so that you can apply attributes to just those members.
This is what they mean:
- ApplyDeviceDefaults – I couldn’t find any mention of this in the normal area of MSDN that enumerates all of the classes, but I did find it explained well in this MSDN article.
- ComplexBindingProperties – For complex data binding, of course.
- DefaultBindingProperty – This is for simple data binding.
- DefaultEvent – The default event for Button is Click. When you double-click on a control in the designer, Visual Studio switches to the code behind file and will automatically generate this event’s handler for you. Very handy.
- DefaultProperty – When you select a control in the designer, this is the property in the properties window that gets focus first. For the Label control, this is the Caption property.
- Description – The description text appears in the properties window below the grid of property names and values, and can provide useful information about the meaning of a property or event.
- Designer – The Designer attribute is one of the primary gateways for providing rich design-time experiences for your controls. Creating custom designers and associating your controls with them is a tricky business, and one I intend to explore in depth in this and future articles. Because MSDN documentation in this area is rather sparse, I’ve begun contributing to the community content on the MSDN pages, and you’ll see the help I’ve added at the very bottom of the page if you follow the link for this item.
- DesignerSerializer – Located in System.ComponentModel.Design.Serialization, a DesignerSerializer is used to provide a custom way of serializing your control to code in the designer partial class file. I haven’t had a need yet to use this, but if someone can think of a real-world scenario that requires it, I will be happy to explore the subject further and write about it.
- DesignTimeVisible – Indicates whether a control can be shown on a designer, and is supposedly ignored by “components with a UI presense”, whatever that means. A little more usefully, MSDN states that it is useful when you have a control that accepts child components, such as the TreeView whose node items “should not appear in the component tray because they are drawn by the TreeView control”. This will require some more exploration.
- DesktopCompatible – This is the first design-time attribute I became familiar with in working with Compact Framework controls. If you have a control that makes P/Invoke calls, Visual Studio is uncertain whether they’re device specific OS calls, and to be safe, it doesn’t execute your control’s code, which means your control can’t render itself even on the designer surface. You get a message telling you that it’s unsafe. To get around this, and presuming that it is indeed safe to run your control’s code on the desktop (I’ll explain more about this later, or you can read this article by XinYan), just add a DesktopCompatible attribute to the control class, and you’ll be back in business. I’m not sure where this attribute is located; it’s not a desktop attribute, so it’s not in the full .NET Framework’s System.dll, and a search through Reflector didn’t locate it, so I’ll have to keep looking. But it’s there, and it works.
- Docking – Specifies the default docking behavior for controls. This is located in System.Windows.Forms.
- Editor – If you need a more powerful experience for editing complex properties within the properties window itself, this attribute will allow you to hook into one. I’ll be covering over custom editors in detail.
- ImmutableObject – Specifies that an object has no subproperties capable of being edited, per MSDN documentation. Not sure what the benefits of that would be: perhaps to hide all properties for a class without having to hide each one separately?
- InitializationEvent – This is used to support auto data-binding (by Visual Studio wizards I’m guessing), so it knows which event to hook into when generating the data-binding code.
- LookupBindingProperties –
- RootDesignerSerializer – I’m not sure what this was for originally, but according to bug report, this attribute is both deprecated and broken.
- Supported – Explained in this blog as part of Platform Verification Task. Located in Microsoft.CompactFramework.Build.Tasks. You can use this attribute to indicate that a class or member is not supported for the specific platform.
- SuppressFiltering – Located in Microsoft.CompactFramework.Build.Tasks.
- ToolboxBitmap – This simply specifies the bitmap that will be displayed in the toolbox. Located in System.Drawing.
- TypeConverter – Specifies the TypeConverter class used for a property. TypeConverters are an important aspect of the design-time experience and merit further explanation.
- Browsable – This determines whether a property or event will be displayed in the properties window, and is typically used to hide properties or events that must be public for some reason but aren’t meant to be manipulated during design time.
- Category – If you set this to an existing category name, your property or event will appear in there; if you create your own category name, a new category will appear in the properties window.
There are a few more that are specific to properties, events, and methods, but this will give us a good start. I’ll cover the remainder in future articles.
That seems like a lot at first glance, but chances are that you’ll only need a handful at any given time. With a little exploration and practice (and guidance from myself and others whose blogs and other resources I’ll share), you could soon be a master of rich, no-compromise custom control development for Compact Framework applications. I believe this is a worthy cause, for one because there is a scarcity of good third-party controls, especially when compared to the abundance that we have for the desktop world of .NET development; but also because I have personally struggled with Compact Framework development and have always thought it would be nice to have a step-by-step series of tutorial to follow for creating custom controls. I’ve found many useful and helpful articles, but nothing that really brings it all together in a clear manner.
Here is an example .xtma file for a new user control I created that has Category and Description attributes on a property I created called HighlightForeColor:
The magic happens when you build your custom control project. Visual Studio builds not only your control project in its runtime form, but also generates another assembly with .asmmeta.dll after your own target assembly name.
If you use Reflector to disassemble it, you will see that this is a design-time component that contains all of your classes and their members with real attributes attached. Strangely, it does not contain any code, and parameters have no names. The purpose is simply to be a carrier of attributes. This is a screen shot from Reflector, showing the attached attributes:
I’m puzzled by this implementation detail. Attributes are so insignificant in size, being just metadata tags. If Microsoft had just included them in the Compact Framework, it seems they could have avoided the requirement for this second, oddly-empty assembly (and there is a third, for custom designers and editors, as you’ll see later). Unless there’s something else to it that I’m missing…
[This article is part of a series that continues in this article.]