Sunday, March 13, 2011

Deal with Performance in WPF applications

Hi friends,

WPF is one of the major changes to the desktop applications in recent times. Most of us is using it in your day to day life programming. Some use 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 in a larger extent. But as for any other application, performance is the major issue for your application. Its not how well you structured your application, or how loose couple your UI 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 over utilization 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 may be you want just to identify them for your application. Lets 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 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 situation 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.

1. 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 thread needs to update any WPF element, you should use code like this :

this.Dispatcher.BeginInvoke((Action)delegate()
{
    //Write your Code here
}, 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 does also allows you to specify priority.

2.  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.  Lets suppose you define a DataTemplate for your ListBox. Each DataTemplate contains a number of Controls which repeats 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 Items.

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 might be taking a lots of CPU. There is 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

BitmapEffect even though is depreciated in WPF 4.0 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 use Basic Controls 

In WPF, most of the control is 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 recomended 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 element each time. It will recalculate the Real size of the control each time even when Visual tree is traversed.

So try 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 for 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 the Items will be Recycled.


Conclusion

Even though these are the most basic information provided with this post, but I hope this comes handy. 

If you are new here, try reading my WPF Tutorial.
Thank you for reading.

2 comments:

  1. "WPF draws the screen at a continuous pace of 60 frames per second rate by default"

    This is when using animations or always?

    ReplyDelete
  2. @Eduardo Molteni

    It generally happens for animation. Animation continuously changes content of the screen and needs refresh. So DesiredFramerate is used to determine when to update the content.

    If you have somewhat static text displayed, the framerate will not be so high.

    ReplyDelete

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.

Author's new book

Abhishek authored one of the best selling book of .NET. It covers ASP.NET, WPF, Windows 8, Threading, Memory Management, Internals, Visual Studio, HTML5, JQuery and many more...
Grab it now !!!