Twelve Ways to Improve WPF Performance

There is no shortage of information out there on how to speed up the performance of WPF applications, but too often the focus is on the weird stuff instead of the simpler issues. I’m not going to talk about things like writing to WritableBitmaps to optimize drawing—it’s a topic covered to death elsewhere. Instead, this is meant to be a slightly more practical guide to squeezing performance out of WPF in ways that are probably more likely affecting you.

Some general notes

ItemsControl and its subclasses ListBox and ListView exacerbate performance problems because these controls are highly dynamic (resolution happens “late”), involve WPF collections (which are slow), and have difficult and unpredictable lifetimes for their child controls. Scrollbar performance is often a big problem in larger WPF apps because of problems that seem trivial for small collections, but suddenly blow up with larger data sets.

Also, it can be difficult in WPF to know exactly when the system is finished with an object. For views, you get the FrameworkElement.Unloaded event, but it gets raised at times you might not expect (such as system theme changes) and not at times when you might (application shutdown). On viewmodels associated with views, you’ll never get a WPF notification that a viewmodel is about to go unused by a view. Blend-style behaviors also have their own set of lifetime problems.

Then there are some problems (like this and this) where WPF leaks for you too.

Finally, there are things (this, this, this, this, this, and this) that simply perform worse than you likely expect.

Finally, there are old UI/WinForms problems (this, this, and this) that never really went away—they’re just less likely to happen.

  1. Fix Binding Errors
  2. Hard-code widths and heights where possible
  3. Avoid CollectionView.Grouping
  4. Optimize bindings to collections that change
  5. Avoid DynamicResources
  6. Avoid ResourceDictionary
  7. Simplify your visual tree
  8. Be wary of System.Windows.Interactivity.Behavior<T>.OnDetaching
  9. Do not use DependencyPropertyDescriptor for any reason…ever
  10. Be careful of viewmodel events
  11. Batch up Dispatcher.BeginInvoke
  12. In general, beware of memory leaks


I. Fix Binding Errors and Exceptions

Every time a binding error occurs, your app hangs for just a split second as it writes out errors to the trace log. If you have a lot of binding errors, then those split seconds start to add up. Make sure to go through your bindings, especially those on ItemsControls (ListViews, custom grids, etc.) and verify that there are no binding errors.

Open up your app in the debugger and play around, especially where there is slowness. Make sure all bindings resolve without errors.

RelativeSource in DataTemplates may also result in bindings that break initially, but then later resolve properly. Be wary of them, and try to use inherited attached properties instead of relying on RelativeSource in DataTemplates.

Viewmodel bindings

  1. Make sure that your views and view models are in sync. Use ReSharper 6 to help you find broken bindings.
  2. If you’re binding to a collection of objects with mixed types, add different DataTemplates so that none of them refer to non-existent properties.
  3. Make sure that your converters aren’t throwing exceptions. These have a cost too.

View-based RelativeSource bindings

  1. When using ListBoxes and ListViews, it’s a common problem to have this problem. Avoid RelativeSource.FindAncestor expressions at all cost here, because the deferred behavior of templates cause the object and its bindings to be created (and resolved) before the ListBoxItem/ListViewItem is added to the visual tree.
  2. An alternative is to define an attached dependency property on the ListBoxItem/ListViewItem, and use property inheritance to give your child items the necessary property values. This essentially pushes property values down the visual tree instead of searching up.


II. Hard-code sizes where possible

This may not always be a practical or desirable solution, but layout passes perform faster when widths and heights do not have to be recalculated. They may also help stop a layout pass from rippling through an entire visual tree.

And always set specific widths on columns in a grid (be it a ListView + GridView or any third-party control), because these tend to be very expensive, especially with larger data sets.


III. Avoid CollectionView.Grouping

Grouping in WPF doesn’t perform terribly well, especially with ListViews and GridViews. Create a collection with mixed viewmodel types–your original collection, and one that represents the “group”. Use DataTemplates to change the appearance of your “group” objects.

For example, if you have a PersonViewModel class with a property that you want to group by (let’s say Region), it is faster to create a mixed collection of MyGroupViewModel and PersonViewModel objects, ordered correctly by group, with different DataTemplates, than it is to bind to a grouped collection. Unfortunately, it’s a lot more work.


IV. Optimize bindings to collections that change

Repeatedly calling ObservableCollection<T>.Add when the collection is data-bound can be a prohibitively expensive operation, especially with thousands of rows. Unfortunately, the framework provides no easy, satisfactory fix.

Fix 1: Use ObservableCollection as-is, but break bindings

  1. Break the binding to the collection.
  2. Update the collection while not data-bound.
  3. Re-bind.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// some methods removed for brevity
public partial class MyViewModel : INotifyPropertyChanged
{
    private ObservableCollection<T> _people;
 
    public IList People
    {
        get { return _people; }
        private set
        {
            if (_people != value)
            {
                _people = value;
                OnPropertyChanged("People");
            }
        }
    }
 
    void BatchAddPeople(IEnumerable<Person> newPeople)
    {
        var currentPeople = _people;
 
        // stop WPF from listening to the changes that we're about
        // to perform
        this.People = null;
 
        // change
        foreach (var person in newPeople)
        {
            currentPeople.Add(person);
        }
 
        // cause WPF to rebind--but only once instead of once for
        // each person
        this.People = currentPeople;
    }
}

Fix 2: Use the older .NET 2.0-era collections instead

  1. Use System.ComponentModel.BindingList<T> (from the old days) instead; it has an API for suppressing change notifications.

Fix 3: Reimplement ObservableCollection.

  1. Create your own collection that implements INotifyCollectionChanged.
    • Raise INotifyCollectionChanged as sparingly as you can.
    • Raise the event with a NotifyCollectionChangedAction.Reset event for anything more trivial than a simple single-item add, remove, change, or move. Do not take advantage of the NotifyCollectionChangedEventArgs constructors that take collections of items; you will find support for it spotty at best.
  2. Implement System.Collections.IList on your collection. WPF does not use the generic System.Collections.Generic.IList<T> interface; it is completely ignored. If you don’t implement the interface, WPF will perform almost all operations (including accessing rows by number!) strictly by the IEnumerable implementation, and it won’t be very optimal or fast about it.
  3. (Probably) implement System.Collections.Generic.IList<T> as well. WPF doesn’t use it, but you probably will (through LINQ, Rx, etc.)


V. Avoid DynamicResources

Even in .NET 4.0, DynamicResource access is still slower than StaticResource access. And worse, once you start nesting DynamicResources (for example, a ListView whose Style contains a ControlTemplate that references objects through DynamicResources), you’re likely to run into situations where you leak controls.


VI. Avoid ResourceDictionary

This advice is practically impossible to follow, but do your best. There is a huge cost in constructing ResourceDictionaries, and depending on where you place them, you are probably constructing many more objects than you realize.

A common, sensible, and logical pattern is to keep usages of elements as close to where you use them as possible. Many people place resources in UserControl.Resources, or break up their themes into multiple ResourceDictionaries for clarity and separation. Although this is arguably good programming practice, it also tends to be insanely slow. If your windows/controls or your ListBoxItem/ListViewItems in a ListBox/ListView are coming up more slowly than you would like, it’s probably a combination of too much ResourceDictionary construction and/or DynamicResources. (Yes, even in .NET 4.0.) Collapse ResourceDictionaries as much as you can, and try to ensure that the contents of these dictionaries is only loaded once. The easiest and surest way is to include it in the resources of your System.Windows.Application object, almost always difficult or infeasible for composite applications.

I have also frequently taken to creating static classes that contain nothing but highly reusable resources (think static framework classes like the Brushes class) because it’s easier to guarantee that objects are only being created once, and hopefully at app startup instead of triggered by the user interacting with the application and forcing a lazy load at an undesirable time. Not necessary the healthiest design, but the performance is quite a bit better.

Using implicit ControlTemplate/DataTemplate styles will also help keep your code and XAML organized without the need for either StaticResource or DynamicResource.


VII. Simplify your visual tree

Shallow visual trees are better than deeper visual trees. Again, ItemsControls will usually exacerbate performance problems with deep visual trees because if they’re not being virtualized, they’re being destroyed and recreated; if they are being virtualized, changing DataContext in a deeper tree takes more time than changing DataContext in a shallower one.


VIII. Be wary of System.Windows.Interactivity.Behavior<T>.OnDetaching

Sadly, System.Windows.Interactivity.Behavior<T>.OnDetaching will generally not get called. Put a breakpoint and see for yourself.

OnAttached signals the addition of a behavior to a control (generally at instantiation of your XAML); OnDetaching signals the removal of a behavior from a control (generally never, as behaviors don’t get removed from controls). Don’t put sensitive disposing behavior in OnDetaching. The Unloaded event is a better place for that, but be aware that it will get raised every time the control is removed from the visual tree.


IX. Do not use DependencyPropertyDescriptor for any reason…ever

DependencyPropertyDescriptor.AddValueChanged classes cause the WPF framework to take a strong reference to the source of the event that isn’t removed until you call DependencyPropertyDescriptor.RemoveValueChanged. This class is frequently used in conjunction with Behaviors, so if you have RemoveValueChanged in OnDetaching, you’re likely leaking memory. Because of WPF’s references to your objects, it is not just enough to drop references to your view and view model.

A better alternative is to rely on data binding where you can; create a DependencyProperty for the sole purpose of listening to changes on your target property, and use the change notifications in DependencyProperty in order to listen to changes on the target property. It keeps the observed object (generally, your view model) from accidentally holding a strong reference to your view.


X. Be careful of view model events

If your views or behaviors rely on events being raised from a viewmodel (as innocuous as INotifyPropertyChanged.PropertyChanged or INotifyCollectionChanged.CollectionChanged), subscribe to them weakly. Viewmodels tend to have a longer lifetime than views (consider a virtualized ItemsControl), so it’s possible that your view model will inadvertently gather references to views. Use classes like PropertyChangedEventManager or CollectionChangedEventManager, or (painfully) use the WeakEventManager to create your own event manager for your custom events. It’s painful, but usually necessary in order to prevent view models from taking references to views.


XI. Batch up Dispatcher.BeginInvoke

If your application displays data from a network, you’re probably using background threads to accomplish the task (which is good). However, you’re probably not consciously counting your Dispatcher.BeginInvoke calls (which is not as good). The more you’re able to coalesce multiple Dispatcher.BeginInvokes into a single call, the more likely WPF will be able to help you out by making single subsequent layout and render passes, and the lower your overall CPU usage.

To be fair, WPF is much better at trying to help you here than VB6/WinForms—you won’t often see apps that dance and flicker incessantly any more—but there is still a non-zero cost to updating the screen, and if you have a particularly large application, this could be a problem for you.


XII. In general, beware of memory leaks

This is a bit of a generalization of the last few points, but memory leaks make apps behave worse over time. Fixing memory leaks goes a long way in fixing the performance of an application. And since most developers are constantly restarting WPF apps as they work on them, they often go undetected until the software is delivered.

—DKT

Finding all of the bindings on an object in WPF

Disclaimer: Don’t do it. Whatever brought you here, chances are you’re doing something wrong, because you’re most likely about to do something very gnarly that you shouldn’t be doing with your view and a whole pile of code-behind. You could make your viewmodel more intelligent, you could implement IDataErrorInfo or otherwise use an attached behavior and put your errors on the viewmodel where they belong—chances are, there is something that you could be doing that won’t require you to need to identify all of the bindings on an object.

That being said, here’s how it’s done.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static Dictionary<DependencyProperty, BindingBase> GetAllBindings(DependencyObject d)
{
    if (d == null)
    {
        throw new ArgumentNullException("d");
    }
 
    var bindings = new Dictionary<DependencyProperty, BindingBase>();
    var lve = d.GetLocalValueEnumerator();
    while (lve.MoveNext())
    {
        DependencyProperty dp = lve.Current.Property;
        var expr = BindingOperations.GetBindingBase(d, dp);
        if (expr != null)
        {
            bindings.Add(dp, expr);
        }
    }
    return bindings;
}

If you are doing (or are thinking about doing) anything fancy with reflection to determine all properties and values on an object, you may want to consider using DependencyObjects instead. You get everything that DependencyObjects give you for free, and you can do everything without resorting to slow (and often obfuscating) reflection. —DKT

Bindable Validation Errors

Wouldn’t it be great if you could do this in XAML:

1
2
<TextBox Text="{Binding Path=IngredientName}"
         Validation.Error="{Binding Path=IngredientNameError}"/>

Turns out you can:

1
2
<TextBox Text="{Binding Path=IngredientName}"
         vh:ValidationHelper.Error="{Binding Path=IngredientNameError}"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
 
namespace Pelebyte.ValidationUtils
{
  /// <summary>
  /// Provides validation helper methods and utilities.
  /// </summary>
  public static class ValidationHelper
  {
    /// <summary>
    /// The <see cref="DependencyProperty"/> that identifies the
    /// <see cref="P:ValidationHelper.Error"/> attached property.
    /// </summary>
    public static readonly DependencyProperty ErrorProperty =
      DependencyProperty.RegisterAttached(
        "Error", typeof(string), typeof(ValidationHelper),
        new PropertyMetadata(null, OnErrorChanged));
 
    /// <summary>
    /// Gets the custom error applied to the control.
    /// </summary>
    /// <param name="d">
    /// The control to get the current custom error for.
    /// </param>
    /// <returns>
    /// A string that represents the custom error.
    /// </returns>
    public static string GetError(DependencyObject d)
    {
      return (string)d.GetValue(ErrorProperty);
    }
 
    /// <summary>
    /// Sets the custom error applied to the control.
    /// </summary>
    /// <param name="d">
    /// The control to set the current custom error for.
    /// </param>
    /// <param name="value">
    /// A string that represents the custom error. An empty or
    /// <c>null</c> string clears the error on the field.
    /// </param>
    public static void SetError(DependencyObject d, string value)
    {
      d.SetValue(ErrorProperty, value);
    }
 
    /// <summary>
    /// Called when the <see cref="P:ValidationHelper.Error"/>
    /// attached property changes value.
    /// </summary>
    /// <param name="d">
    /// The <see cref="DependencyObject"/> that is having its
    /// <see cref="P:ValidationHelper.Error"/> property changing
    /// values.
    /// </param>
    /// <param name="e">
    /// The <see cref="DependencyPropertyChangedEventArgs"/>
    /// instance containing the event data.
    /// </param>
    private static void OnErrorChanged(
      DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      BindingExpressionBase expr;
 
        expr = BindingOperations.GetBindingExpressionBase(
          d, BindingErrorTargetProperty);
 
      if ((expr == null) && (e.NewValue != null))
      {
          // create a new binding between two properties that
          // we're only going to use so that we have an avenue
          // of our own to attach binding errors
          Binding b = new Binding();
          b.Source = d;
          b.Path = new PropertyPath(BindingErrorSourceProperty);
          b.Mode = BindingMode.OneWayToSource;
 
          b.ValidationRules.Add(new InternalRule(d));
 
          expr = BindingOperations.SetBinding(
              d, BindingErrorTargetProperty, b);
      }
 
        if (expr != null)
      {
        expr.UpdateSource();
      }
      }
    }
 
    /// <summary>
    /// The internal implementation of <see cref="ValidationRule"/>
    /// that returns our real "error" whenever we want.
    /// </summary>
    private sealed class InternalRule : ValidationRule
    {
      private readonly DependencyObject _d;
 
      /// <summary>
      /// Initializes a new instance of the
      /// <see cref="InternalRule"/> class specific to a
      /// particular object. The
      /// <see cref="P:ValidationHelper.Error"/> property of the
      /// given object will be used to determine the error on the
      /// object.
      /// </summary>
      /// <param name="d">
      /// The <see cref="DependencyObject"/> to return errors
      /// for.
      /// </param>
      public InternalRule(DependencyObject d)
      {
        _d = d;
      }
 
      public override ValidationResult Validate(
          object value, CultureInfo cultureInfo)
      {
        // completely ignore /value/ and look for the error
        // on the DependencyObject that was given to us in
        // our constructor
        string error = GetError(_d);
 
        if (string.IsNullOrEmpty(error))
        {
          // an empty or null string means no error
          return ValidationResult.ValidResult;
        }
        else
        {
          // anything else means an error
          return new ValidationResult(false, error);
        }
      }
    }
 
    // two private dependency properties that we use internally to
    // set up our useless binding
 
    private static readonly DependencyProperty
      BindingErrorSourceProperty =
        DependencyProperty.RegisterAttached(
          "BindingErrorSource", typeof(object),
          typeof(ValidationHelper),
          new PropertyMetadata(null));
 
    private static readonly DependencyProperty
      BindingErrorTargetProperty =
        DependencyProperty.RegisterAttached(
          "BindingErrorTarget", typeof(object),
          typeof(ValidationHelper),
          new PropertyMetadata(null));
  }
}

Why it works

The System.Windows.Controls.Validation.Errors property is a collection for a reason—it’s a collection of all of the binding errors on the object.

For most controls, it’s not readily apparent that more than one binding on the same control could actually fail:

1
<TextBox Text="{Binding Path=IngredientName, ValidatesOnDataErrors=True}"/>

But if you had a complex control where more than one property was controlled directly by the user, it’s more obvious why you’d possibly need a collection instead of a single object:

1
2
3
4
5
<!-- this slider has two thumbs; the user drags both of
     them around to specify a range -->
<my:DoubleSlider
    MinValue="{Binding Path=Minimum, ValidatesOnDataErrors=True}"
    MaxValue="{Binding Path=Maximum, ValidatesOnDataErrors=True}"/>

If both of these properties had errors, WPF would collect both of them in Validation.Errors.

So the ValidationHelper code above is essentially emulating the following XAML snippet in C# (note that the code in green isn’t actually possible—that’s why we’re writing this code in C#):

<TextBox x:Name="MyTextBox"
Text="{Binding Path=IngredientName, ValidatesOnDataErrors=True}"
<vh:ValidationHelper.BindingErrorTarget>
<Binding Source="MyTextBox"
Path="(vh:ValidationHelper.BindingErrorSource)">
Mode="OneWayToSource"
<vh:ValidationHelper+InternalRule (MyTextBox)>
</Binding>
</vh:ValidationHelper.BindingErrorTarget>
</TextBox>

(It may help at this point to open another a window with my little sketch of how WPF data binding data flows around.)

Our hidden BindingErrorTarget property participates in validation just like a regular property. So when the value of ValidationHelper.Error is changed:

  1. Force the binding on ValidationHelper.BindingErrorTarget to be re-evaluated from the target back to the source (call BindingExpression.UpdateSource()).
  2. Our validation rule InternalRule gets called as part of the normal validation process; our rule will return ValidationHelper.Error instead of validating the incoming value.

It doesn’t actually matter what the values of the BindingErrorTarget and BindingErrorSource properties are; they only exist so that we can key into the binding system.

Why?

IDataErrorInfo and ValidatesOnDataErrors would seemingly make this technique redundant: why go through all this trouble to expose a binding site for errors on the viewmodel when you could just implement IDataErrorInfo?

  • IDataErrorInfo is only consulted when the source property changes value—if you have a data source whose errors can dynamically change independently of the source property, there isn’t a clean way from the viewmodel to force the view to pick up your changes in the error. (If your source implements INotifyPropertyChanged, you can raise PropertyChanged for the relevant property, but if you use DependencyObjects, there is no way to force the binding system to re-evaluate the property from the viewmodel—you’d need the BindingExpression, which then requires your viewmodel to have knowledge of the view).
  • It may be inconvenient or impossible from a design standpoint to have the object containing your error property to also implement IDataErrorInfo.
    If your error comes from a different object than your viewmodel, then your implementation of IDataErrorInfo would need to know where to fetch it.
  • If you ever had a situation where you had a ValidationRule that you wanted to bind to, you’ve probably discovered that it’s never going to happen—ValidationRule, not being a DependencyObject, doesn’t support binding—not in code, and certainly not in XAML. An attached behavior or subclass (you never need to subclass in WPF) is really your only recourse for situations like this.
  • You don’t want to rely on .NET 3.0 SP1 or .NET 3.5 SP1. Thankfully, Windows 7 comes out of the box with it, but Vista, and certainly XP, do not. This technique works with every version of WPF.

Remember that any DependencyProperty that is the target of a binding can hold validation errors; also remember that you can add attached properties to any object. That means you can add arbitrary validation errors to any DependencyObject through this trick. You can also drive this error generation off of whatever you want—I chose the simplest example and created an attached property specifically to hold an error that will be reported, unchanged, right back through the binding system. You could create a new attached behavior, have the TextBox.Text property and TextBox.TextChanged event drive the error; then set up an attached behavior that provides validation on the text of the TextBox without having to provide an instance of ValidationRule. —DKT


In the interests of not cluttering the picture, I left out a little magic trick:

  • We could attach a converter to the binding where IValueConverter.ConvertBack() returns Binding.DoNothing; this would stop data from ever flowing to the source.
  • Then we could actually drop the BindingErrorSource property and reuse an existing one (like Tag or something), knowing that our binding will never actually change the value or otherwise interfere with it.

And why use two properties when you could get away with just one?

Sep 24: Updated the OnErrorChanged to fix a bug that would cause the error state to never actually clear…whoops! –DKT

Data Flow in a WPF BindingExpression

I was writing up some posts on how ValidationRules work, and of course, I ended up confusing myself, because data binding can get confusing once you start throwing in IValueConverters, ValidationRules, target objects, source objects…yikes.

So I decided to sketch out a simple diagram for my own benefit. And after 15 minutes, I realized it wasn’t so simple:





Click the image for a larger version

Hope it helps make WPF binding less confusing… —DKT