It took me a long time to learn how to think in WPF. I had spent years plunging into the depths of Windows Forms, and before that I learned the ropes of MFC. Since WPF is a complete departure from the world of the HWND, most of my knowledge and “second instincts” gained from working with those other platforms had to be unlearned. That’s not easy to do.
I realized the other day that my thinking about WPF has changed dramatically over the past five or six months. Some of my older blog posts solve problems in ways that now seem to be “swimming upstream” to me. Back then I was trying to solve WPF problems by applying a WinForms way of thinking. I feel that now I have found the “spirit of WPF”, so to speak.
Below is a list of ten tips which, I feel, capture the essence of thinking in WPF. This list is by no means complete or “correct.” The tips are rules of thumb, not hard and fast absolutes. Take these ideas with a grain of salt; perhaps your way of approaching WPF is completely different than mine.
- Do not hesitate to write code. Just because you can do so many things with XAML does not mean that you should. WPF is an application development platform. Applications are written in code, and use markup when appropriate.
- Keep your XAML dumb. Avoid being the geek who spends an hour concocting a dozen lines of abstruse XAML to do something which could easily be done with one or two lines of code. XAML is great for declaring objects and the relationships between them. It is possible to express behavior in XAML somewhat, but think twice before doing so. Eventually your functional requirements might become more complicated, and you might find yourself rewriting that XAML logic in code anyways. Always remember, there is no XAML debugger (except for your brain) and you cannot handle exceptions in XAML!
- Keep visual resources out of value converters. You should avoid referencing brushes, images, colors, etc. in your value converter code. Keep that stuff in XAML. For instance, do not create a value converter which returns a brush. Instead, create a value converter which returns a value that a trigger can use to select the appropriate brush. Doing so makes the value converter more reusable, and allows you to keep brush declarations in the XAML file, where they belong.
- Create modular value converters. Avoid creating value converters which will only be used once. It is better to build up a library of simple, modular value converters, which can be combined using the ValueConverterGroup.
- Expose functionality as services, not subclasses. Many features of WPF are exposed as services. A service can be enabled for a particular element/control by setting an attached property on it. Examples of this include validation, spellchecking, scrolling, drag-and-drop, and more. It is preferable to expose extra functionality through one or more attached properties, as opposed to creating subclasses, so that the behavior can be used or not used with ease. The consumer of that functionality will appreciate not having to change the type of element in his/her UI just to make use of it. Also keep in mind that this “service oriented” approach is not always desirable or technically feasible. It’s just a rule of thumb.
- Think thrice before creating a custom control. It is rarely necessary to create a custom control in WPF. Before you go ahead and subclass Control, consider your options. Read this great article for an overview of what those options are: http://msdn.microsoft.com/msdnmag/issues/07/05/WPF/default.aspx
- Do not micromanage, let panels do the work. One of the best parts about WPF, in my opinion, is the layout system. You put visual elements in a panel, and the panel manages the location and/or size of those elements for you. You can put elements in a Canvas panel if you want to specify absolute coordinates for them, but you normally should not be doing so. If you find that you are specifying coordinates for elements a lot, consider using other kinds of panels to make your life easier (and UIs better).
- Carefully consider where to put resources. The resource system in WPF is brilliant, if used properly. When designing an application be sure to keep the hierarchical nature of resource lookups in mind so that you avoid duplicating brushes, images, data sources, etc. Get into the habit of creating separate resource dictionaries for logically distinct resources. You can use the MergedDictionaries property of ResourceDictionary to import physically separate resources at runtime.
- Strive for elegance, not eye candy. With all of the visual goodies at your disposal it is tempting to add superfluous crap into a user interface. Avoid this temptation. If you find yourself adding animations that do not enhance the usability of a user interface, remove them. Above all, keep it consistent. When you find a visual style which is appropriate for your application, create Styles that can be used to promote that look and feel throughout the application. You don’t want your UI to look like a ransom note!
- Avoid being a Blender. I am starting to see a lot of people who tinker with the Expression Blend program and suddenly call themselves WPF developers. Blend is a nice comfy way to get familiar with many of the basics of WPF, but it is not a good way to really learn WPF. If you want to really understand the platform, write your XAML by hand. Look at the WPF assemblies in Reflector. Subclass some WPF classes and see what it takes to customize their behavior. Frequent the WPF Forum. Read the documentation! Those are the things that will truly show you how vast and incredible the platform is, not fiddling around in Blend. In my opinion, Blend should be used as a productivity enhancer for people who already know WPF.