This article is part of a series of articles I have written covering WPF; improving its performance, its quirks and associated workarounds, as well as porting it to WUA Applications:
- Improving WPF Rendering Performance (Part 1)
- Improving WPF Rendering performance (Part 2)
- Improving WPF Rendering performance (Part 3)
- Troubleshooting Video Playback in WPF
- Writing HLSL Pixel Shaders for WPF
- Porting WPF applications to Windows Universal Apps
As of late I have been going through some of my most visited articles from my old blog and giving them a new lease of life. In this article I want to talk about some of the ways you can improve rendering performance of WPF applications, however some of the suggestions I talk about here can be ported across to Silverlight and WP7 apps.
One of the big issues I’ve had is how WPF handles ImageBrush, when loading an image it will load the entire image which if your rendering a 14mp digital photo can cause a huge slowdown if you’re using multiple instances of different brushes. There are a few tricks to start off with, firstly by altering the RenderOptions of the ImageBrush to increase its performance but sacrificing quality. Below is a sample of code that alters the RenderOptions of the brush _PictureBrush.
Reducing Image Footprint
If it is not possible to load a lower resolution version of an image, it may be possible to instead create and use a scaled down version instead which can help drastically cut memory usage.
public BitmapImage CreateThumbnail(Uri Source, int PreferredWidth)
BitmapImage bi = new BitmapImage();
bi.DecodePixelWidth = PreferredWidth;
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.UriSource = Source;
This code takes a Uri Source and creates a thumbnail of the PeferredWidth. It is usually good practice to limit the texture sizes to the size of the viewable screen, and create textures of multiple zoom levels externally if need arises.
Sometimes you may want to hide elements or render them as semi transparent. There are times when .opacity or .visibility can be faster.
When the .opacity property is used, the element is cached depending on the CacheMode set, therefore it is advisible to investigate that. When it is redrawn it can bypass most of the rendering phase and can be considerably faster.
When using .visibility, the visual tree of the elements on screen has to be updated. If visibility is set to true the element is completely redrawn. It is therefore only advisable to use .visibility = false over .opacity = 0 when there is a complex sub layout of the element.
Store as Content not as a Resource
Although little is done internally, storing images as content is usually advised unless you have a good reason not too, it also means file paths to images can be shorter and thus can be a tiny bit faster on loading as there is less hassle for the file handler.
Use JPGs instead of PNGs
I do understand when developers want to use PNGs instead of JPGS due to their increased quality, after all PNG is a lossless compression method, yet the decompressor for JPG is considerably faster than the PNG decompressor. This means JPGs will nearly always load faster.
If you are worried about the quality degradation, consider increasing the stored quality of the JPG when saving the original image.
PNGs should only be used when transparency is required as this is not supported by the JPG image format. This is the only time when I advise people to use PNG.
Another issue is around generating images as vector art in XAML. Although being vector based, it can scale perfectly, there is a performance hit. If scaling is not required, JPGs can still be faster.
Debugging your rendering
Although a WP7 option, you can see what you are rendering each frame by the following code:
Application.Current.Host.Settings.EnableRedrawRegions = true;
It displays a ghost version of what is being updated, performance comes by analysing this and only updating what is required onscreen.