Checklist for industrial-strength controls

This is another document that Tyler Barton wrote during his internship.  The idea was to concisely capture the expectations for an industrial strength control -- i.e., controls that meet the same standards as those that come with WPF.  (one-off controls that you write just for your own application usually don't need to meet such a high standard, but you might want to give this list to read anyway as some of the items may apply to your situation)  The idea was just to outline what's expected, rather than explain any of these concepts in detail -- someday I'd like to link to overviews of the relevant technologies.

Also, this is a draft document -- I'd love your feedback on what guidelines & expectations we should be adding to this document.

1 Declare Dependency Properties and Wrap them with CLR Accessors

Why use dependency properties? The Avalon property engine automatically provides support for a wide range of features including styling, databinding, animation, property inheritance, default values and storyboarding.

For properties that should be dependency properties:

  • Declare a public static readonly field to hold your DependencyProperty object

  • Declare CLR accessors for these dependency properties

  • Use metadata during registration to customize the behaviour of your dependency properties.

2 Declare Routed Events, Wrap them with CLR Accessors, and Define On___ Methods
When to use routed events? Routed events allow parents elements to hear about the event conveniently and in a well structured manner. Routed events may also be used to trigger animations, and may be specified in styles.

For events that should be routed events:

  • Declare a public static readonly field to hold your RoutedEvent object

  • Declare CLR accessors for these routed events

  • Declare a protected virtual void On<event name>(<something> EventArgs e) method for subclasses to use. Call this method from your “class handler” -- EventManager.RegisterClassHandler(typeof(YourComponent), YourClassHandler)

  • Routed events using RoutingStrategy.Tunnel should have names that begin with “Preview”.

  • Many bubbling events don’t have corresponding tunnelling events, but almost all tunnelling events should have corresponding bubbling events

  • A tunnelling event should be raised before its corresponding bubbling event. If the tunnelling event was marked as handled, the bubbling event should be marked as handled before the event is raised.

3 Support Xaml
In order for your type to be used in xaml, you need to follow a few simple rules:

  • The type must be public

  • It has a public default (no argument) constructor

  • Properties can be set in any order

  • Events can be hooked up in any order

  • Dependency properties & routed events have corresponding CLR accessors

  • The types for each property on your class must themselves to support xaml, or the parser must be able to find a TypeConverter for that type (specified by putting the TypeConverterAttribute on the type to convert, or on the property using that type)

4 Support Serialization
Why Support Serialization? Components must be serializable if they are to be used in a visual design tool. Components must be serializable to be used in applications or other components that are also serializable. Make sure that your components can be written out to xaml using Parser.SaveAsXml().

  • XamlSerializationVisibility: Mark items that should never be serialized or always be serialized with the XamlSerializationVisibility attribute.

  • Default Values: provide default values where appropriate. Default values should not be serialized.

  • Runtime Serialization Checks: Are runtime checks necessary when serializing some members? If so, provide a ShouldSerialize[Foo] method for each property [Foo] that requires a check at runtime during serialization

  • TypeConverters: Are the default TypeConverters good enough for each of your properties? If not, build custom TypeConverters and include them with your control. Mark properties with the TypeConverter attribute.

  • Custom Serializer: Will the default serializer produce an accurate, efficient representation of your control? If not, you should consider creating a custom serializer. Include this class with your control and mark your control with the XamlDesignerSerializer attribute

5 Support the new Accessibility framework
Why make your component accessible? The accessibility and UI automation framework can help you make your class accessible to customers using alternative input/output devices. It will also allow developers to easily write automated tests involving your component.

Control Patterns: Pick out and implement all the control patterns in the Avalon library that accurately reflect your control.

Automation Events: Fire automation events when your class changes visually. These will alert accessibility clients like screen readers.

6 Make Your Component Localizable
Why make your component localizable? You expand the reach of your component by supporting multiple locales. When you support only a single locale, you exclude foreign developers and you dissuade local developers who might want to target international markets.

  • Separate Locale Specific Data: Move all strings displayed in your control to a separate assembly. Create a mirror of this assembly for each locale you wish to support. Let the resource manager choose the satellite assembly at runtime.

  • Left to Right Layout: If you’re building a layout component, remember that it may be necessary for this component to change orientation in some parts of the world. Most built in Avalon layout components do this automatically. Avalon automatically applies a mirror transform to most layout components when the locale changes. Be careful! This might affect the way your keyboard input works.

7 Provide Implementation for Valid Commands

Why support commanding? Commands allow developers to quickly connect components in a loosely coupled way (i.e., without the components understanding each other). Commands are especially valuable for invoking from a menu or toolbar. Commands can also be used for a small control to talk to its template parent control.

Support Standard Commands: Take a look through the Avalon Standard Command Library. Pick out commands that fit your component and provide handlers (“command bindings”) to those that make sense.

Define new commands if:

  • There’s no equivalent existing command

  • You want to make it easy for developers to raise the command from menus and toolbars

  • You want to let developers change what input sequences trigger the command

8 Support Theming
Why support theming? Your component will stand out in a themed application if it does not provide appropriate styles.

Supporting theming involves defining styles for multiple themes or using resource references to system colors and fonts.