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; however, 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 when monitoring and issuing change notifications. Another benefit is that a frozen object is also safe to be shared across threads, unlike unfrozen objects.

Many UI-related objects extend the Freezable class to provide this functionality and most Freezable objects relate to the graphics sub-system, as rendering visuals is one of the areas where performance improvements are most needed.

Classes such as the Brush, Geometry, and Transform classes contain unmanaged resources and the system must monitor them for changes. By freezing these objects and making them immutable, the system is able to free up its monitoring resources and better utilize them elsewhere. Furthermore, even the memory footprint of a frozen object is considerably less than its unfrozen counterpart.

Therefore, in order to make the greatest performance improvements, we should get used to freezing all of our resources in all of theĀ Resource sections, as long as we have no plans to modify them. As most resources typically remain unmodified, we are usually able to freeze the vast majority of them and gain significant and noticeable improvements in performance by doing so.

In Chapter 8, Creating Visually Appealing User Interfaces, we learned how to freeze a Freezable object in code by calling its Freeze method. Let's now look at how we can freeze our resources in XAML. First, we need to add a XAML namespace prefix to the presentation options namespace to access its Freeze attribute:

xmlns:PresentationOptions=
  "http://schemas.microsoft.com/winfx/2006/xaml/presentation/options 
"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="PresentationOptions"

Note that we also include another XAML namespace prefix to be able to access the Ignorable attribute, and we set our PresentationOptions prefix as its value. This is because the Freeze attribute is primarily only recognized by the WPF XAML processor, and, in order to maintain compatibility with other XAML readers, we need to specify that the attribute can be ignored.

We'll find a full example in the Drawing conclusions section coming up soon, but for now, using a resource from an earlier example, let's examine how to freeze a Freezable object in XAML:

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

Some Freezable objects, such as the animation and geometry objects, can contain other Freezable objects. When a Freezable object is frozen, its child objects are also frozen. However, there are a few cases where a Freezable object cannot be frozen.

One case happens if it has any properties that might change in value, due to animations, data binding, or DynamicResource references. The other case occurs when the Freezable object has any child objects that cannot be frozen.

If we are freezing resource type objects in the code behind of a custom control, for example, then we can call the CanFreeze property of the Freezable class to check whether each Freezable object can be frozen before attempting to freeze them:

EllipseGeometry ellipseGeometry =  
  new EllipseGeometry(new Rect(0, 0, 500, 250)); 
if (ellipseGeometry.CanFreeze) ellipseGeometry.Freeze(); 
Path.Data = ellipseGeometry; 

Once a Freezable object is frozen, it cannot be modified, and attempting to do so will cause an InvalidOperationException to be thrown. Note that a Freezable object cannot be unfrozen; so, to avoid this situation, we can check the value of the IsFrozen property before attempting to modify the object. If it is frozen, we can make a copy of it using its Clone method and modify that instead:

if (ellipseGeometry.IsFrozen) 
{ 
  EllipseGeometry ellipseGeometryClone = ellipseGeometry.Clone(); 
  ellipseGeometryClone.RadiusX = 400; 
ellipseGeometryClone.Freeze();
Path.Data = ellipseGeometryClone;
}
else ellipseGeometry.RadiusX = 400;

If a Freezable object is cloned, any Freezable children that it might have will also be copied to enable modification. When a frozen object is animated, the animation system will make cloned copies of it in this way so that it can modify them. But, as this adds an overhead to performance, it is advisable not to freeze a Freezable object if you expect to be animated.