Introduction
WPF is one of the major changes to the desktop applications in recent times. Most of us are using it in our day to day life programming. Some use it for normal desktop or Windows based applications while others write programs that run in browsers as Sandboxed application. The major investments of Microsoft on making Silverlight to work out of browser enhanced the usage of WPF to a larger extent. But as for any other application, performance is the major issue for your application. It's not how well you structured your application, or how loosely coupled your UI is with other layers, it is often a requirement for any software on how it acts in stressed situations.
One of such performance hits that might appear for your WPF application is overutilization of CPU. In this post, I will cover some of the general performance hiccups with WPF which you might consider unnecessary for your application or maybe you want just to identify them for your application. Let's put few of the performance improvement tips for you in this post.
Try Not to be Smarter
WPF architecture is well written to handle performance of your application in a smart way. It draws only the portion of the screen which is visible through the window. Hence, if you have code which hides an element in WPF window, the Frames which WPF rendering engine draws will not take up those elements.
Hence, it is unnecessary to Remove a child element from a Container (even with having a strong reference of the control for future) to improve the application performance, rather it is ok if you just hide the control visually. For instance, let us suppose you have a TextBox
on the screen, now for certain situations if you want to hide the TextBox
from the screen, it will not be an issue to hide the Textbox
rather than actually removing the TextBox
from the Visual Tree.
Basically, WPF runs with two threads (not only) which you should know.
- UI Thread: Sometimes called as Dispatcher Thread is actually a Thread that runs all the managed code within it. This thread creates each element of WPF and every control has Thread affinity for the thread. Every control in WPF inheriting from
DispatcherObject
does holds the reference of this Thread in the form of DispatcherThread
property. Hence if you want to run any Non-UI thread to invoke a statement, like if any of your non-UI threads needs to update any WPF element, you should use code like this:
this.Dispatcher.BeginInvoke((Action)delegate()
{
}, DispatcherPriority.ApplicationIdle);
Hence, when you are running a task in a Thread, you can update your UI using the same Windows Messaging API call directly on the Dispatcher Thread. The Dispatcher call also allows you to specify priority.
- Rendering Thread: This thread goes on to find only the visual element and draws the whole window at a 60 frames per second as default rate directly to get the screen ready for you. Rendering thread popularly called as Composition Thread calls unmanaged Direct 3D objects to render output using Graphics acceleration. The threads uses Channel Protocol to communicate with the other thread.
Avoid Direct Traversal of Control Trees
If you are coming from normal Windows environment, this is one of the things that you should always remember when you start coding in WPF applications. WPF advances the architecture in such a way that you can make this more data driven. Use Binding extensively to ensure that your data is separated from the UI controls. Let's suppose you define a DataTemplate
for your ListBox
. Each DataTemplate
contains a number of Controls which repeat for each DataItem
in the List
. Now rather than going with listbox.Items.Add(new ListBoxItem())
with the data into it, you should bind the whole List
with data into the ListBox
. Hence, say if you have a ListBox
like this:
<ListBox ItemsSource="{Binding MyList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MyProperty}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should always use Binding extensively in WPF to ensure your Data is bound directly on the object. You should manually create ListBoxItem
in your code and do a loop to add Item
s.
On the other hand, it is also recommended to get updates directly into the object rather than finding each control from the Visual Tree. Say for instance, you want to find the control TextBlock
inside the selected ListBoxItem
, you might use:
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
This code finds each element from the Visual Tree. As you can see, it traverses the visual tree using recursion, this code is costly. Rather use MVVM pattern to ensure that your data been updated directly when the control is updated using Binding.
Deal with Excessive Usage of CPU
If you are an application developer, sometimes you might see your application taking a lot of CPU. There is a simple way to minimize this. Actually, WPF draws the screen at a continuous pace of 60 frames per second rate by default. So if you are using lots of graphics and images, your application will eventually take a lots of CPU utilization because of these framerates. Animations in your application worsen this much more. It is always a good idea to reduce the default rate something lesser than this value. You can use this code to change the default behaviour of your application to 10 frames per second.
Timeline.DesiredFrameRateProperty.OverrideMetadata(
typeof(Timeline),
new FrameworkPropertyMetadata { DefaultValue = 10 }
);
This will considerably reduce the utilization of CPU for your application.
Do Not Use BitmapEffect
Even though BitmapEffect
is depreciated in WPF 4.0, it had lots of performance issues. It takes a lot of memory to do simple effects hence try to avoid using them in your application. It is rather better to create your own effect using Pixel Shader effect or normal Effect.
You should also avoid use of RenderTargetBitmap
, TileBrush
, Layered Window, etc.
Try to Use Basic Controls
In WPF, most of the controls areis a composition of other controls. For example, a TextBox
contains 30 elements, a Label
contains 5 elements, while a TextBlock
contains only 1 element. So most often to write some text into WPF canvas, it is recommended to use a TextBlock
or even a Run only if your requirement is just to show text. In WPF 4.0, Run also supports binding.
Try to Avoid Auto Properties Often
Grid row height / width allows * or Auto properties. These allows the control to auto-calculate each of the Row and Column dimensions for each changes of controls in the cell. So in a grid, if you have 100 elements which sets width = Auto / * for each element, adding an element to the grid will calculate each of those 100 elements each time. It will recalculate the Real size of the control each time even when Visual tree is traversed.
So try to avoid using this extensively. Canvas on the other hand is the smallest ContentControl
but you need to customize yourself totally to use it as a Container
.
Use StaticResource over DynamicResource
StaticResource
is evaluated only once when the object is produced. Hence use of StaticResource
extensively in your application is recommended. DynamicResource
is evaluated each time you add the resource to a control, hence DynamicResource
puts an extra load over the UI thread. Also you should use ResourceDictionary
extensively to store reusable components in your application and later on refer to the same ResourceDictionary
each time.
Use VirtualizingStackPanel as Much as You Can
Another useful thing in WPF is the VirtualizingStackPanel
. Say you have a 100 thousand elements to load in an ItemsControl
. Each ItemsControl
can use VirtualizingStackPanel
to ensure that it renders only the portion of the screen which is displayed rather than going to create the whole 100 thousand element on first go. When the scrollbar is scrolled, the Panel
will go on creating each element on the Fly.
VirtualizingStackPanel
allows you to Recycle elements too.
<ItemsControl ItemsSource="{Binding MyList}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
</ItemsControl>
Here, the items will be created on the fly even if the Items will be Recycled.
Conclusion
Even though this is the most basic information provided with this post, I hope this comes in handy.
If you are new here, try reading my WPF Tutorial.
Thank you for reading.