Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 9

Freezable Objects Overview

 03/30/2017
 9 minutes to read

o

This topic describes how to effectively use and create Freezable objects, which


provide special features that can help improve application performance.
Examples of freezable objects include brushes, pens, transformations, geometries,
and animations.

What Is a Freezable?
A Freezable is a special type of object that has two states: unfrozen and frozen.
When unfrozen, a Freezable appears to behave like any other object. When
frozen, a Freezable can no longer be modified.

A Freezable provides a Changed event to notify observers of any modifications to


the object. Freezing a Freezable can improve its performance, because it no
longer needs to spend resources on change notifications. A frozen Freezable can
also be shared across threads, while an unfrozen Freezable cannot.

Although the Freezable class has many applications, most Freezable objects in


Windows Presentation Foundation (WPF) are related to the graphics sub-system.

The Freezable class makes it easier to use certain graphics system objects and can
help improve application performance. Examples of types that inherit
from Freezable include the Brush, Transform, and Geometry classes. Because they
contain unmanaged resources, the system must monitor these objects for
modifications, and then update their corresponding unmanaged resources when
there is a change to the original object. Even if you don't actually modify a
graphics system object, the system must still spend some of its resources
monitoring the object, in case you do change it.

For example, suppose you create a SolidColorBrush brush and use it to paint the
background of a button.

C#Copy
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;

When the button is rendered, the WPF graphics sub-system uses the information
you provided to paint a group of pixels to create the appearance of a button.
Although you used a solid color brush to describe how the button should be
painted, your solid color brush doesn't actually do the painting. The graphics
system generates fast, low-level objects for the button and the brush, and it is
those objects that actually appear on the screen.

If you were to modify the brush, those low-level objects would have to be
regenerated. The freezable class is what gives a brush the ability to find its
corresponding generated, low-level objects and to update them when it changes.
When this ability is enabled, the brush is said to be "unfrozen."

A freezable's Freeze method enables you to disable this self-updating ability. You


can use this method to make the brush become "frozen," or unmodifiable.

 Note

Not every Freezable object can be frozen. To avoid throwing


an InvalidOperationException, check the value of the Freezable
object's CanFreeze property to determine whether it can be frozen before
attempting to freeze it.

C#Copy
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}

When you no longer need to modify a freezable, freezing it provides


performance benefits. If you were to freeze the brush in this example, the
graphics system would no longer need to monitor it for changes. The graphics
system can also make other optimizations, because it knows the brush won't
change.

 Note
For convenience, freezable objects remain unfrozen unless you explicitly freeze
them.

Using Freezables
Using an unfrozen freezable is like using any other type of object. In the following
example, the color of a SolidColorBrush is changed from yellow to red after it's
used to paint the background of a button. The graphics system works behind the
scenes to automatically change the button from yellow to red the next time the
screen is refreshed.

C#Copy
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;

// Changes the button's background to red.


myBrush.Color = Colors.Red;

Freezing a Freezable

To make a Freezable unmodifiable, you call its Freeze method. When you freeze


an object that contains freezable objects, those objects are frozen as well. For
example, if you freeze a PathGeometry, the figures and segments it contains
would be frozen too.

A Freezable can't be frozen if any of the following are true:

 It has animated or data bound properties.


 It has properties set by a dynamic resource. (See the XAML Resources for
more information about dynamic resources.)
 It contains Freezable sub-objects that can't be frozen.

If these conditions are false, and you don't intend to modify the Freezable, then
you should freeze it to gain the performance benefits described earlier.

Once you call a freezable's Freeze method, it can no longer be modified.


Attempting to modify a frozen object causes an InvalidOperationException to be
thrown. The following code throws an exception, because we attempt to modify
the brush after it's been frozen.
C#Copy

Button myButton = new Button();


SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}

myButton.Background = myBrush;

try {

// Throws an InvalidOperationException, because the brush is frozen.


myBrush.Color = Colors.Red;
}catch(InvalidOperationException ex)
{
MessageBox.Show("Invalid operation: " + ex.ToString());
}

To avoid throwing this exception, you can use the IsFrozen method to determine


whether a Freezable is frozen.

C#Copy

Button myButton = new Button();


SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}

myButton.Background = myBrush;

if (myBrush.IsFrozen) // Evaluates to true.


{
// If the brush is frozen, create a clone and
// modify the clone.
SolidColorBrush myBrushClone = myBrush.Clone();
myBrushClone.Color = Colors.Red;
myButton.Background = myBrushClone;
}
else
{
// If the brush is not frozen,
// it can be modified directly.
myBrush.Color = Colors.Red;
}

In the preceding code example, a modifiable copy was made of a frozen object
using the Clone method. The next section discusses cloning in more detail.

 Note

Because a frozen freezable cannot be animated, the animation system will


automatically create modifiable clones of frozen Freezable objects when you try
to animate them with a Storyboard. To eliminate the performance overhead
caused by cloning, leave an object unfrozen if you intend to animate it. For more
information about animating with storyboards, see the Storyboards Overview.

Freezing from Markup

To freeze a Freezable object declared in markup, you use


the PresentationOptions:Freeze attribute. In the following example,
a SolidColorBrush is declared as a page resource and frozen. It is then used to set
the background of a button.

XAMLCopy
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

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

<Page.Resources>

<!-- This resource is frozen. -->


<SolidColorBrush
x:Key="MyBrush"
PresentationOptions:Freeze="True"
Color="Red" />
</Page.Resources>

<StackPanel>

<Button Content="A Button"


Background="{StaticResource MyBrush}">
</Button>

</StackPanel>
</Page>

To use the Freeze attribute, you must map to the presentation options


namespace: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options . 
PresentationOptions is the recommended prefix for mapping this namespace:

XAMLCopy
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presen
tation/options"

Because not all XAML readers recognize this attribute, it's recommended that you
use the mc:Ignorable Attribute to mark the Presentation:Freeze attribute as
ignorable:

XAMLCopy
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"

For more information, see the mc:Ignorable Attribute page.

"Unfreezing" a Freezable

Once frozen, a Freezable can never be modified or unfrozen; however, you can


create an unfrozen clone using the Clone or CloneCurrentValue method.

In the following example, the button's background is set with a brush and that
brush is then frozen. An unfrozen copy is made of the brush using
the Clone method. The clone is modified and used to change the button's
background from yellow to red.

C#Copy
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

// Freezing a Freezable before it provides


// performance improvements if you don't
// intend on modifying it.
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}

myButton.Background = myBrush;

// If you need to modify a frozen brush,


// the Clone method can be used to
// create a modifiable copy.
SolidColorBrush myBrushClone = myBrush.Clone();

// Changing myBrushClone does not change


// the color of myButton, because its
// background is still set by myBrush.
myBrushClone.Color = Colors.Red;

// Replacing myBrush with myBrushClone


// makes the button change to red.
myButton.Background = myBrushClone;
 Note

Regardless of which clone method you use, animations are never copied to the
new Freezable.

The Clone and CloneCurrentValue methods produce deep copies of the


freezable. If the freezable contains other frozen freezable objects, they are also
cloned and made modifiable. For example, if you clone a frozen PathGeometry to
make it modifiable, the figures and segments it contains are also copied and
made modifiable.

Creating Your Own Freezable Class


A class that derives from Freezable gains the following features.

 Special states: a read-only (frozen) and a writable state.


 Thread safety: a frozen Freezable can be shared across threads.
 Detailed change notification: Unlike other DependencyObjects, Freezable
objects provide change notifications when sub-property values change.
 Easy cloning: the Freezable class has already implemented several
methods that produce deep clones.

A Freezable is a type of DependencyObject, and therefore uses the dependency


property system. Your class properties don't have to be dependency properties,
but using dependency properties will reduce the amount of code you have to
write, because the Freezable class was designed with dependency properties in
mind. For more information about the dependency property system, see
the Dependency Properties Overview.

Every Freezable subclass must override the CreateInstanceCore method. If your


class uses dependency properties for all its data, you're finished.

If your class contains non-dependency property data members, you must also
override the following methods:

 CloneCore
 CloneCurrentValueCore
 GetAsFrozenCore
 GetCurrentValueAsFrozenCore
 FreezeCore

You must also observe the following rules for accessing and writing to data
members that are not dependency properties:

 At the beginning of any API that reads non-dependency property data


members, call the ReadPreamble method.
 At the beginning of any API that writes non-dependency property data
members, call the WritePreamble method. (Once you've
called WritePreamble in an API, you don't need to make an additional call
to ReadPreamble if you also read non-dependency property data members.)
 Call the WritePostscript method before exiting methods that write to non-
dependency property data members.

If your class contains non-dependency-property data members that


are DependencyObject objects, you must also call
the OnFreezablePropertyChanged method each time you change one of their
values, even if you're setting the member to null.

You might also like