WPF Performance

 The WPF Framework looks at the graphics hardware that is installed on the computer that it is running on and puts it into one of 3 categories

Rendering Tier 0   < version 7 of DirectX

Rendering Tier 1   < version 9 of DirectX

Rendering Tier 2  >= version 9 of DirectX

As the UI will freeze during the rendering time, care should be taken to minimize the number of visual layers that are rendered

We can find out the rendering tier of the host computer's graphics hardware using the static Tier property of the RenderCapability class.

public App() 
{ 
  StateManager.Instance.RenderingTier =  
    (RenderingTier)(RenderCapability.Tier >> 16); 
} 
namespace CompanyName.ApplicationName.DataModels.Enums 
{ 
  public enum RenderingTier 
  { 
    Zero = 0, 
    One = 1, 
    Two = 2 
  } 
} 

Making more efficient resources

When we reference our resources, we can either use a StaticResource or a DynamicResourcea StaticResource will look up the value of the resource just once, which is comparative to a compile-time lookup. A DynamicResource will repeatedly look up the value of the resource each time it is requested, whether it has changed or not

Another simple way to improve the performance of our resources is to reuse them. Instead of declaring them inline in the place that they are used in the XAML, we should declare them in a suitable resource section and reference them

Freezing objects

In WPF, certain resource objects, such as animations, geometries, brushes, and pens, can be made Freezable. This provides special features that can help to improve the performance of our WPF applications. Freezable objects can either be frozen or unfrozen. In the unfrozen state, they behave like any other object, but when frozen, they become immutable and can no longer be modified.

The main benefit of freezing objects is that it can improve application performance, because frozen objects no longer require resources to be consumed on monitoring and issuing change notifications. Another benefit is that a frozen object is also safe to be shared across threads, unlike unfrozen objects.

in order to make the greatest performance improvements, we should get used to freezing all of our resources in all of the Resource sections

<DropShadowEffect x:Key="Shadow" BlurRadius="10" Direction="270"  
  ShadowDepth="7" Opacity="0.5" PresentationOptions:Freeze="True" /> 

 it is advisable not to freeze a Freezable object if it is expected to be animated

Enhancing Text Rendering

 Glyphs element in the fourth row and this represents the most efficient method of outputting text in a WPF application.

<UserControl x:Class="CompanyName.ApplicationName.Views.TextView" 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
  xmlns:Controls=
    "clr-namespace:CompanyName.ApplicationName.Views.Controls" 
  Height="250" Width="325"> 
  <Grid ShowGridLines="True"> 
    <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition /> 
      <RowDefinition /> 
      <RowDefinition /> 
    </Grid.RowDefinitions> 
    <Label Content="Quite Efficient" FontFamily="Times New Roman"  
      FontSize="50" FontWeight="Bold" FontStyle="Italic"  
      Foreground="Red" Margin="10,0,0,0" Padding="0" /> 
    <TextBlock Grid.Row="1" Text="More Efficient" 
      FontFamily="Times New Roman" FontSize="50" FontWeight="Bold"
      FontStyle="Italic" Foreground="Black" Margin="10,0,0,0" /> 
    <Controls:FormattedTextOutput Grid.Row="2" Text="More Efficient" /> 
    <Glyphs Grid.Row="3" UnicodeString="Most Efficient"  
      FontUri="C:\WINDOWS\Fonts\timesbi.TTF" FontRenderingEmSize="50"  
      Fill="Black" OriginX="10" OriginY="45" /> 
  </Grid> 
</UserControl> 

FormattedTextOutput class would need to  override void OnRender 

using System.Globalization; 
using System.Windows; 
using System.Windows.Media; 
 
namespace CompanyName.ApplicationName.Views.Controls 
{ 
  public class FormattedTextOutput : FrameworkElement 
  { 
    public static readonly DependencyProperty TextProperty =  
      DependencyProperty.Register(nameof(Text), typeof(string),
      typeof(FormattedTextOutput), new FrameworkPropertyMetadata( 
      string.Empty, FrameworkPropertyMetadataOptions.AffectsRender)); 
 
    public string Text 
    { 
      get { return (string)GetValue(TextProperty); } 
      set { SetValue(TextProperty, value); } 
    } 
 
    protected override void OnRender(DrawingContext drawingContext) 
    { 
      DpiScale dpiScale = VisualTreeHelper.GetDpi(this); 
FormattedText formattedText = new FormattedText(Text,
CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight,
new Typeface("Times New Roman"), 50, Brushes.Red,
dpiScale.PixelsPerDip);
formattedText.SetFontStyle(FontStyles.Italic); formattedText.SetFontWeight(FontWeights.Bold); drawingContext.DrawText(formattedText, new Point(10, 0)); } } }

Data binding

When we only require one-way bindings, we can save some computing resources by setting the Mode property of the Binding class to the appropriate member of the BindingMode enumeration.

When property is for display purposes only, we should set its Mode property to OneWay

Another good practice to get into is to set the FallbackValue property of the Binding class on each binding that we declare. Doing this will stop the WPF Framework from performing a lookup of the default value of the target Dependency Property when there are data binding errors and will prevent trace statements from being generated and output;

Registering Dependency Properties

We need to be careful when setting the metadata for our Dependency Properties. Incorrectly specifying the framework metadata while registering our Dependency Properties can lower performance by forcing the layout system to unnecessarily perform additional layout passes.

In particular, we need to be careful when specifying any of the AffectsMeasureAffectsArrangeAffectsParentMeasureAffectsParentArrange, or AffectsRender members of the FrameworkPropertyMetadataOptions enumeration, and ensure that they are actually required before using them.

Likewise, if we specify the Inherits member of the FrameworkPropertyMetadataOptions enumeration when registering our Dependency Property, we are effectively increasing the length of time that invalidation will take on the property. As such, we should ensure that this particular metadata member is only used when it is really necessary.

One last metadata option that can improve the performance of the application is the SubPropertiesDoNotAffectRender member. If the type of our Dependency Property is a reference type, we can specify this enumeration member to stop the layout system from checking for changes to all sub-properties of the object, which it would otherwise do by default.

Binding to collections

When dealing with collections that will be updated in a WPF application, we tend to prefer using the generic ObservableCollection<T> class. The reason for this is because this class implements the INotifyCollectionChanged interface, which notifies listeners of changes to the collection, such as adding, removing, or clearing items.

What we may not realize is the incredible performance improvement that we get from using this class to hold our data collections. When comparing this with the generic List<T> class, for example, we note that it does not automatically raise any collection changed event. In order to enable the View to display the updated collection, we need to reset it as the ItemsSource property value of the relevant collection control.

However, each time that the ItemsSource property is set, the data bound collection control will clear its current list of items and completely regenerate them again, which can be a time-consuming process. So, to add a single item to an ObservableCollection<T> takes approximately 20 milliseconds to render, but to reset the ItemsSource property value could take over 1.5 seconds.

However, if our collection is immutable and we will not be altering it in any way, we do not need to use the generic ObservableCollection<T> class, as we have no need for its change handlers. Rather than wasting resources on unused change handlers, we can use a different type of collection class.

While there is not a preferred type of collection to use when data binding immutable collections to UI controls, we should try to avoid using the IEnumerable class as the collection container. This type cannot be used directly by the ItemsControl class, and, when it is used, the WPF Framework will generate a generic IList<T> collection to wrap the IEnumerable instance and this can also negatively affect performance.

Shrinking data objects

Rather than building one giant View to edit the whole product, we could then provide a number of smaller Views, perhaps even accessible from different tabs within the same View. In this way, we would be able to just load the ProductDescription objects for the user to select from and then load the individual sections of the product in each sub View.

Controls

Each time a UI element is rendered, the layout system must complete two passes, a measure pass and an arrange pass, which is collectively known as a layout pass. If the element has children and/or grandchildren, they will all need to complete the layout pass too. This process is intensive and the fewer passes that can be made, the quicker our Views will render.

This can occur when adding or removing items to or from a panel, applying transforms on the elements, or by calling the UIElement.UpdateLayout method, which forces a new layout pass.

Virtualizing collections

When we display large numbers of items in our collection controls, it can negatively affect the application's performance. This is because the layout system will create a layout container, such as a ComboBoxItem in the case of a ComboBox, for example, for every item in the data bound collection. As only a small subset of the complete number of items is displayed at any one time, we can take advantage of virtualization to improve the situation.

<ComboBox ItemsSource="{Binding Items}"> 
  <ComboBox.ItemsPanel> 
    <ItemsPanelTemplate> 
      <VirtualizingStackPanel IsVirtualizing="True" /> 
    </ItemsPanelTemplate> 
  </ComboBox.ItemsPanel> 
</ComboBox> 

Apart from setting the IsVirtualizing property to False, there are a few other reasons why UI virtualization may not work. One case is when item containers have manually been added to an ItemsControl object or one of its derived controls. Another case is when the item containers are of different types.

The final reason why virtualization may not work is not so obvious and relates to the CanContentScroll property of the ScrollViewer class. This is an interesting property that specifies whether the ScrollViewer in a collection control will scroll its items in logical units or physical units. The default value is False, which smoothly scrolls in terms of physical units.

Physical units relate to the device-independent pixels that WPF works with, while logical units relate to the widths or heights of the collection items, depending on the orientation of the control. As the default value of the CanContentScroll property is False, this will need to be set to True to enable virtualization, so that scrolling is performed item by item and not pixel by pixel.

When virtualization is employed in a collection control that extends the ItemsControl class and the user scrolls, new item containers are created for the newly visible items and the containers for the items that are no longer visible are disposed of.

One further optimization that WPF provides us with is deferred scrolling. Normally, scrolling in a collection control continuously updates the UI. However, if our data items or their item containers have several layers of visuals that define them and scrolling is slow, we can opt to defer the UI update until scrolling has finished.

In order to enable deferred scrolling on a collection control, we need to set the ScrollViewer.IsDeferredScrollingEnabled Attached Property to True. Although we don't generally use ScrollViewer elements in XAML directly, we can also attach this property to collection controls that host a ScrollViewer element in their control templates:

<ListBox ItemsSource="{Binding Items}"
ScrollViewer.IsDeferredScrollingEnabled="True" />

Handling events

One of the most common causes of memory leaks appearing in an application is the failure to remove event handlers once they are no longer needed. When we attach an event handler to an object's event in the usual way, we are effectively passing that object a reference to the handler and creating a hard reference to it.

An alternative method that we can use to avoid this situation. In the .NET Framework, there is a WeakReference class and it can be used to remove the hard references caused by attaching event handlers to events using the traditional method.

he basic idea is that the class that raises the event should maintain a collection of WeakReference instances and add to it each time another class attaches an event handler to the event. Let's now create a new WeakReferenceActionCommand class, from our ActionCommand class from earlier, to use this WeakReference class:

using System; 
using System.Collections.Generic; 
using System.Windows.Input; 
 
namespace CompanyName.ApplicationName.ViewModels.Commands 
{ 
  public class WeakReferenceActionCommand : ICommand 
  { 
    private readonly Action<object> action; 
    private readonly Predicate<object> canExecute; 
    private List<WeakReference> eventHandlers = new List<WeakReference>(); 
 
    public WeakReferenceActionCommand(Action<object> action) : 
this(action, null) { } public WeakReferenceActionCommand(Action<object> action, Predicate<object> canExecute) { if (action == null) throw new ArgumentNullException("The action
input parameter of the WeakReferenceActionCommand constructor
cannot be null.");
this.action = action; this.canExecute = canExecute; } public event EventHandler CanExecuteChanged { add { eventHandlers.Add(new WeakReference(value)); CommandManager.RequerySuggested += value; } remove { if (eventHandlers == null) return; for (int i = eventHandlers.Count - 1; i >= 0; i--) { WeakReference weakReference = eventHandlers[i]; EventHandler handler = weakReference.Target as EventHandler; if (handler == null || handler == value) { eventHandlers.RemoveAt(i); } } CommandManager.RequerySuggested -= value; } } public void RaiseCanExecuteChanged() { eventHandlers.ForEach( r => (r.Target as EventHandler)?.Invoke(this, new EventArgs())); } public bool CanExecute(object parameter) { return canExecute == null ? true : canExecute(parameter); } public void Execute(object parameter) { action(parameter); } } }