Windows Presentation Foundation (WPF) has been around for 16 years, or, for lack of a better reference, since .Net Framework 3.0. It has been greatly improved over the years, but there are still several areas that can suffer from poor performance. These are especially linked to bad coding practices, broken bindings, or complex layouts. Luckily, nothing that cannot be fixed with a thorough understanding of the WPF platform and a little planning.
This article can be useful to beginner developers who want to get things right, straight from the start. Because it is common sense that to prevent is much easier (and cheaper) than to treat.
But nonetheless, these tips may help if you have an existing application and you are not satisfied with its performance. This was my case years ago, when I was working as a developer for the aero-motive industry, and I was put in charge of bringing the application we were developing to a “Formula 1” performance level. That meant plunging into the code, no matter if it was written by me or by others, and doing whatever could be done to squeeze out every possible millisecond in application loading time.
I’ve always tried to be aware of my code’s performance, but this particular mission made me even more aware of what can be done in order to improve the performance of my WPF applications.
Here are the most common reasons that I’ve found to slow application performance and how to approach them:
Binding errors
Binding errors are responsible of slowing an application on runtime. If a binding error occurs, the application writes the error to the trace log. The more binding errors, the more entries in the log. This will result in bad performance.
This type of problem became obvious to me because it was written in the output, and until the app finished writing all the errors, it wouldn’t do anything. Later on, I learned that XAML code is very dependent on its particular syntax and if you don’t respect it 100%, it will throw errors after errors. And these errors are resource gobblers.
I currently work for a large application, with dozens XAML files. Imagine each of these files throwing 2-3 System Windows Data Errors. They pile up, and the application will take a lot more time to load. These are not errors that can cause the application to stop or to crash. But they eat up resources.
Fixing such an error may not take long. But the difference is visible when you fix all of them. In the end, each millisecond adds up and you can earn 2-3 seconds for the entire app.
What to look for:
System.Windows.Data Error: 40: BindingExpression path error: ‘NonExistingProperty’ property not found on ‘object’ ”Grid’ (Name=’pnlMain’)’. BindingExpression:Path=NonExistingProperty; DataItem=’Grid’ (Name=’pnlMain’); target element is ‘TextBlock’ (Name=”); target property is ‘Text’ (type ‘String’)
Tip:
Always check the output window and read the error. Use binding trace error syntax if it’s the case.
Pay attention to memory leaks
As you become more experienced with WPF, you are confronted with the “event” notion. An event is, for example, the action that happens when the user clicks a button.
There are several ways to define these actions. And many beginner WPF programmers register to an event, but they forget to un-register. In C++ there’s the pointers concept, and memory allocation. If you allocate memory, you must also de-allocate it. Microsoft came up with the “garbage collector” mechanism that verifies if the window containing the button can be destroyed. But if it is still active, it cannot be destroyed.
In C#, you can register to the event using +=, and unregister with -=
So if you don’t do memory management or event management, these problems will pile up.
Microsoft came up also on this front with a solution: a specific type of events that you don’t need to unregister from, the garbage collector will decide if this is necessary. But these event types are not so much used by beginners/developers who are not familiarized with using events.
Garbage collector is a .NET property that abides by the Microsoft philosophy known as “the lazy way”. This philosophy says that, as a developer, you shouldn’t waste time with technicalities, you are even discouraged to force this property. You should focus on code and creativity, and Microsoft tries to come up with solutions to help you manage memory, events, and so on. The condition is to use exactly the events that you need.
WeakEvent manager is the tool that manages this type of events that don’t require un-registration. This type of event will simply trigger Garbage Collector to destroy it when it finds it.
The main problem is that junior developers may do a web search on how to add an event and most likely find the += method.
Forgetting to unregister from events will cause for example some windows to open more than once. This again, leads to more memory allocated, and more time needed to load the application.
What to look for:
Subscribing to event handler – and forgetting to unsubscribe or using behaviors (pay attention to Attach/Detach events).
Incorrect WPF bindings (always bind to a DO or to an object that implements INotifyPropertyChanged – otherwise WPF will create a strong reference to the binding source.
Tip
Unsubscribe events. Use weak handler, of subscribe, when possible, with an anonymous function.
Keep the visual tree simple
At every code review that we have I can’t reinforce enough: there’s no point to have a grid in a grid in a grid. One grid is enough and does the work just as well.
Here’s an example:
In this case, you can drop the grid, because it serves the same function as the stack panel, which is to host the button. So what you are doing by using both is just to double the function.
What to look for:
Grid
StackPanel
Grid
Button
Tip:
Avoid adding unnecessary elements in the tree (e.g. a grid hosted on a stack panel hosted in another grid)
Use StaticResource vs. DynamicResource
When you define a color in XAML you can use either StaticResource or DynamicResource. The difference between the two is that static resources are evaluated only once, at load time, while dynamic resources are evaluated every time they are requested.
In the color example, if the color never changes, it’s recommended to use StaticResource. But if you have for example a tool tip, that is a dynamic element, then you can use DynamicResource. So it is best to write your code with the context in mind.
What to look for:
BorderBrush=”{DynamicResource GeneralBorderColor}”
Tip:
When possible, use StaticResource. StaticResources are evaluated only once by the element at load time. DynamicResources are evaluated each time when they are demanded by the element, and as a result the application will perform slower.
ElementName vs RelativeSource
I noticed that RelativeSource is more popular on Stack Overflow as a suggested solution to ElementName. That’s a bit counter-intuitive, because the RelativeSource syntax is longer than the one for Element Name.
If I search for «relativesource wpf» the first result will suggest binding path as element name and the name of the grid or the parent.
What to look for:
Binding=”{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Expander}}, Path=IsExpanded}”
Tip:
In general, the ElementName should be used when possible. RelativeSource will traverse the Visual tree until it finds the target.
TextBlock vs Label
Another place you could try to squeeze out some performance is where TextBlock can replace Label. Many times, when we need to describe an element, we tend to use Label, because our brain associates this word with a short, concise text. But from a coding point of view, Label has more functionality than TextBlock, and thus is more resource consuming on the WPF part.
What to look for:
<Border x:Name=”TextBlockBorder” Grid.Row=”0″ Grid.Column=”1″ BorderThickness=”0″>
<Label x:Name=”TextBlockApplyToAll” Content=”Apply to all” VerticalAlignment=”Center” HorizontalAlignment=”Center”/>
</Border>
Tip:
Use TextBlock when possible. TextBlock is derived from FrameworkElements, and on the other hand Label derivers from ContentControl, so it basically means that Label is heavier than TextBlock.
Why (the talk about) performance is important
First, I should specify that performance is measured in application loading time. And there’s no one big change that you can make, but hundreds and thousands of small changes that will each save you a few milliseconds and only when they add up, you will have a noticeable improvement, let’s say by 2 or 3 seconds.
It may not seem much. But this will grow exponentially for repetitive operations. In a factory where processes are multiplied by thousands, it would also result in significant energy consumption. In an application like Office Timeline, it will reduce the frustration on the end user, to wait for backend processes to complete, and save time that is spent on unnecessary operations.
These things are not easily recognizable. It was only when I received a specific performance related task that I acknowledged the problem and started to look for solutions.
That’s why I’m trying to share my findings with every chance I get. What I can say for sure is that people are usually receptive, and little by little, they start to apply these minor tweaks in how they plan and write the code. Doing it the right way from the start is much easier than fixing it later.
Where you could help
These are just a few tips to improve performance, the most important I could think of. But for sure, there are more. If you have idea about other performance improvement tweaks that can be made, please drop me a line bellow, and I will add to the list.
Also, if you are passionate about WPF, I’ll leave you with one of my favorite resources, 2000 Things WPF. Here you can find thousands short articles about how to solve specific problems in WPF.