Not Shooting Yourself in the Foot – WPF Best Practices

I had been meaning to write this article for a while now. To preface it, let me say I love Microsoft’s WPF! There are some brilliant concepts in it and it is a joy to develop UIs with it. However, it seems to have an inherent tendency to make projects go horribly wrong unless you know exactly what you are doing. So these are some general best practices or rather lessons learned the hard way.

1. WPF is a UI framework, not an app framework

The typical WPF projects starts with a main window, put some code in it, add some controls, add more code … and next thing the majority of the application code is distributed among the UI layer. This usually leads to code that is hard to maintain and impossible to test automatically. So as a general rule, design the architecture of your app as if it had no UI. If you can take the non-UI pieces of the application and easily write a console application or another application that has a different UI, you’re on the right path.

I like to think of apps as SDKs where the UI sits on top of it. So if the user for example can perform a save operation, the controls should just trigger it but the actually complexity should be in a lower layer of the application. If done right, the UI becomes so trivial that it usually suffices to do unit/integration testing on the application layer.

2. The main thread is for UI only

A UI should always be responsive. As a corollary, any operation that can take longer than a few milliseconds should be performed on a background thread such that the UI can update in the meantime – for example to render a progress bar, do mouse over highlights, etc. iOS development was a very good way to learn this … the hard way. Upon launching an app, the iOS gives you a couple of seconds and if the app doesn’t start processing message events by then, it is killed!

The easiest and most elegant way to put work into a background thread is to use Task.Run() with a lambda expression.

3. INotifyPropertyChanged has nothing to do with WPF

Yes, of course, INotifyPropertyChanged, bindings and dependency properties are at the very heart of WPF. However, change observation is actually a design pattern that has its merits without a user interface. Ever wondered why it is in a namespace that is different from the controls? That’s why. So while some people dislike supporting INotifyPropertyChanged directly on the model classes, there isn’t really any real argument against doing so.

4. DependencyProperties are for UI only

Oh man, that one also seems to be a common trap. There are two main ways to make a property such that it can be used in a binding: Implement a .NET property and support INotifyPropertyChanged or use a DependencyProperty. Since people are lazy and/or tech-loving, the latter seems to be much more elegant. No extra notify code, just make it a dependency property, bind to it and the UI updates correctly.

The problem is: DependencyProperty is thread-aware and will throw an exception when it is accessed from a thread that is different from the one it was created on. The usual remedy people employ is to use the WPF dispatcher to perform the change operation on the creator-thread – usually the main thread. This is wrong in so many ways, I’m not even sure where to start.

For one, in contrast to INotifyPropertyChanged, DependencyProperty is part of the UI namespace and therefore gives a clear hint that it should not be used in your model classes. The thread-awareness is the next hint that your actually working against the frameworks intend. What people overlook is that dispatching actually comes at a cost: enqueuing the job into one of the WPF dispatcher queues. If your property changes a lot, it can actually flood the dispatcher queue and based on the priority level you used might even prevent the UI from rendering. The main reason to not do it this way is simple: You don’t have to dispatch if you do it right, WPF will do it for you!

As few know, a binding automatically dispatches any INotifyPropertyChanged event automatically to the UI thread. And starting with .NET 4.5, collection access can also be made thread-synchronized very easily.

So use DependencyProperty only for the targets of a potential binding, not for the source. Let WPF do the work for you. You’ll avoid a lot of potential exceptions or other thread-problems like dead-locks.

5. Think in themes

One of the most powerful concepts in WPF is the separation of logical and visual tree. I have found that if you imagine your app having multiple themes/skins and the visual parts of a control might come in multiple flavors, you’ll write better code automatically.

For example, don’t put to much logic or converter magic into the XAML files. When starting with WPF, a lot of people fall to the lure of having no code behind at all. It’s surprising what amount of logic you can put into the XAML if you really put your mind to it, but it doesn’t result in the most effective code and for me, maintainability always is key.

Now, getting back to the idea of skins, imagine you have multiple XAML-portions for each control. Suddenly, it’s no longer as handy that you are able to put those command bindings into the XAML because you have to repeat it for every variant. Also you have to use Control (and OnApplyTemplate) instead of UserControl to be able to switch themes and it gets harder to access code behind from the XAML. And as a third benefit, you can no longer make any assumptions about visual parts being ready when InitializeComponents is done. To sum up, it separates XAML from code behind.

6. Don’t put theme resource dictionaries into every control

People seem to do this such that the Visual Designer works. First of all, screw the visual designer! If you do the visual/logic-tree split as described above, you won’t be able to use it anyway.

More importantly, every control that directly uses the resource dictionary creates its own copy of the dictionary and each control therefore does the XAML parsing. Put the theme dictionary into the App.xaml and use StaticResource to reference from it. This also serves as a single point where you can switch themes at runtime if you ever want to implement it.

7. Screw MVVM

A lot of people love MVVM but in my experience, you should rather design your data model classes in a good away directly (see the INotifyPropertyChanged discussion above) and be aware how much of code behind is too much. The view model just inserts an unnecessary indirection that you’ll have to maintain.

The single most brilliant discussion on the topic I have found is this article by Ward Bell. It basically argues – very convincingly – the only reasons that justify using MVVM are if you a) want to work with Expression Blend or b) have multiple views on a single model. All other characteristics that are attributed to MVVM can just as easily (or even more so) be achieved with MVP.

So my advice is to simply be aware that you are developing a control and thus the code behind should be thin. All important logic should be in the application or model layer of your application (so it can be tested in unit tests), then you’re fine.

8. Converters can have arguments and properties, damn it!

Some people rather seem to write three different converters than using CommandArgument or noticing that you can just add a normal .NET property to it and set it from the XAML. The typical example is a something-to-bool converter where you also need the negated version.

Also: Derive converters from MarkupExtension and use them directly where you need them. Creating an instance in the control’s resource and then referencing it with StaticResource only makes code harder to read.

9. Never use UI elements as your primary data storage

Remember, always imagine your app not having a user interface. Some people tend to temporarily store values inside UI controls – for example a text in a textbox – and not having a backing variable in the model classes. Instead, when the user completes his operation, they read them from the control directly. Don’t do this! Create a backing variable and bind to it.

A similar example is people trying to access a specific TreeViewItem and wonder why there is nothing to help them iterate through the tree. The answer is “because you’re not supposed to iterate through them”! Change values on the data classes and let WPF reflect them in the TreeViewItem through bindings.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>