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.
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___ MethodsWhen 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:
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 XamlIn 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 SerializationWhy 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 frameworkWhy 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 LocalizableWhy 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 ThemingWhy 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.