Freezable 对象概述
本主题说明如何有效地使用和创建 Freezable 对象,这些对象提供可帮助改进应用程序性能的特殊功能。 Freezable 对象的示例包括画笔、钢笔、变换、几何图形和动画。
本主题包含以下各节:
什么是 Freezable?
Freezable 是一种特殊的对象类型,具有两个状态:解冻和冻结。 当处于解冻状态时,Freezable 的行为与任何其他对象的行为一样。 Freezable 一旦冻结,便无法修改。
Freezable 提供了一个 Changed 事件以将对对象所做的任何修改通知给观察程序。 冻结 Freezable 可以改进其性能,因为它不再需要因更改通知而消耗资源。 冻结的 Freezable 也可以在线程之间共享,而解冻的 Freezable 则不能。
虽然 Freezable 类具有许多应用程序,但 Windows Presentation Foundation (WPF) 中的大多数 Freezable 对象都与图形子系统相关。
Freezable 类使您在使用某些图形系统对象时更加轻松,并且有助于改进应用程序性能。 从 Freezable 继承的类型示例包括 Brush、Transform 和 Geometry 类。 由于它们包含非托管资源,因此系统必须监视这些对象的修改情况,然后在对原始对象进行了更改的情况下,更新其相应的非托管资源。 即使实际上您并没有修改图形系统对象,但系统也必须花费一些资源来监视该对象,以防您对其进行更改。
例如,假设您要创建一个 SolidColorBrush 画笔,并使用它来绘制按钮的背景。
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
myButton.Background = myBrush
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
呈现按钮时,WPF 图形子系统使用您提供的信息来绘制一组像素,以创建按钮的外观。 虽然您使用了纯色画笔来描写应如何绘制按钮,但您的纯色画笔并未真正绘制。 图形系统为该按钮和画笔生成快速、低级别的对象,而屏幕上实际显示的正是这些对象。
如果要修改画笔,则必须重新生成这些低级别对象。 通过 Freezable 类,画笔可以找到相应的已生成低级别对象,并在画笔更改时更新这些对象。 当启用该功能时,画笔便被认为是“解冻的”。
使用 Freezable 对象的 Freeze 方法可以禁用该自行更新功能。 可以使用该方法使画笔变为“冻结”或不可修改。
注意 |
---|
并不是每个 Freezable 对象都可以冻结。若要避免引发 InvalidOperationException,请在尝试冻结 Freezable 对象之前,查看其 CanFreeze 属性的值,以确定它是否可以被冻结。 |
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}
当不再需要修改某个 Freezable 时,冻结它可以改进性能。 如果您在该示例中冻结画笔,则图形系统将不再需要监视它的更改情况。 图形系统还可以进行其他优化,因为它知道画笔不会更改。
注意 |
---|
出于方便,除非您显式冻结 Freezable 对象,否则它们保持解冻状态。 |
使用 Freezable
使用解冻的 Freezable 就像使用任何其他类型的对象一样。 在下面的示例中,使用 SolidColorBrush 来绘制按钮的背景之后,其颜色从黄色更改为红色。 图形系统在幕后工作,在下次刷新屏幕时将按钮从黄色自动更改为红色。
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
myButton.Background = myBrush
' Changes the button's background to red.
myBrush.Color = Colors.Red
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
// Changes the button's background to red.
myBrush.Color = Colors.Red;
冻结 Freezable
若要使 Freezable 不可修改,可调用其 Freeze 方法。 冻结某个包含 Freezable 对象的对象时,也会冻结这些被包含的对象。 例如,如果您冻结某个 PathGeometry,则它包含的图形和线段也会被冻结。
如果下列任一情况属实,则无法冻结 Freezable:
如果不存在这些情况,并且您不希望修改 Freezable,则应当像前面介绍的那样冻结该对象以改进性能。
一旦调用某个 Freezable 的 Freeze 方法,便不能再修改该对象。 尝试修改冻结的对象会导致引发 InvalidOperationException。 由于尝试修改已冻结的画笔,下面的代码将引发一个异常。
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
myButton.Background = myBrush
Try
' Throws an InvalidOperationException, because the brush is frozen.
myBrush.Color = Colors.Red
Catch ex As InvalidOperationException
MessageBox.Show("Invalid operation: " & ex.ToString())
End Try
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());
}
为了避免引发此异常,可以使用 IsFrozen 方法来确定 Freezable 是否处于冻结状态。
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
myButton.Background = myBrush
If myBrush.IsFrozen Then ' Evaluates to true.
' If the brush is frozen, create a clone and
' modify the clone.
Dim myBrushClone As SolidColorBrush = 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
End If
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;
}
在前面的代码示例中,使用 Clone 方法对一个冻结的对象创建了可修改副本。 下一节将更详细地讨论克隆操作。
注意 由于无法对冻结的 Freezable 进行动画处理,因此当您尝试使用 Storyboard 对冻结的 Freezable 对象进行动画处理时,动画系统将自动创建这些对象的可修改复本。 为了消除由于克隆而导致的性能系统开销,当您希望对某个对象进行动画处理时,请使其保持解冻状态。 有关使用演示图板进行动画处理的更多信息,请参见演示图板概述。
从标记冻结
若要冻结在标记中声明的 Freezable 对象,请使用 PresentationOptions:Freeze 特性。 在下面的示例中,将一个 SolidColorBrush 声明为页资源,并冻结它。 随后将它用于设置按钮的背景。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:mc="https://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>
若要使用 Freeze 特性,必须映射到表示选项命名空间:https://schemas.microsoft.com/winfx/2006/xaml/presentation/options。 PresentationOptions 是用于映射此命名空间时的建议前缀:
xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
由于并非所有 XAML 读取器都能识别该特性,因此建议使用 mc:Ignorable 特性 将 Presentation:Freeze 特性标记为可忽略:
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
有关更多信息,请参见 mc:Ignorable 特性页。
“解冻”Freezable
Freezable 一旦冻结,便不能再修改或解冻;不过,您可以使用 Clone 或 CloneCurrentValue 方法创建一个解冻的复本。
在下面的示例中,使用一个画笔设置按钮的背景,然后冻结该画笔。 使用 Clone 方法创建该画笔的解冻的副本。 然后,修改该复本并使用它将按钮的背景从黄色更改为红色。
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
' Freezing a Freezable before it provides
' performance improvements if you don't
' intend on modifying it.
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
myButton.Background = myBrush
' If you need to modify a frozen brush,
' the Clone method can be used to
' create a modifiable copy.
Dim myBrushClone As SolidColorBrush = 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
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;
注意 |
---|
无论使用哪一种克隆方法,都不会将动画复制到新的 Freezable。 |
Clone 和 CloneCurrentValue 方法可生成 Freezable 的深层副本。 如果该 Freezable 包含其他冻结的 Freezable 对象,则还会克隆这些对象使它们可以修改。 例如,如果克隆某个冻结的 PathGeometry 以便使其可以修改,则还会复制它包含的图形和线段,使它们可以修改。
创建自己的 Freezable 类
从 Freezable 派生的类可以获取以下功能。
特殊的状态:只读(冻结)状态和可写状态。
线程安全:冻结的 Freezable 可以在线程之间共享。
详细的更改通知:与其他 DependencyObject 不同,Freezable 对象会在子属性值更改时提供更改通知。
轻松克隆:Freezable 类已经实现了多种生成深层复本的方法。
Freezable 是一种 DependencyObject,因而使用依赖项属性系统。 您的类属性不必是依赖项属性,但使用依赖项属性可以减少必须编写的代码量,因为设计 Freezable 类时考虑了依赖项属性。 有关依赖项属性系统的更多信息,请参见依赖项属性概述。
每个 Freezable 子类都必须重写 CreateInstanceCore 方法。 如果您的类对于其所有数据都使用依赖项属性,则您的工作已完成。
如果您的类包含非依赖项属性数据成员,则还必须重写以下方法:
在访问和写入不属于依赖项属性的数据成员时,还必须遵守以下规则:
在读取非依赖项属性数据成员的任何 API 的开头,调用 ReadPreamble 方法。
在写入非依赖项属性数据成员的任何 API 的开头,调用 WritePreamble 方法。 (在 API 中调用 WritePreamble 之后,如果还应读取非依赖项属性数据成员,则不需要另外调用 ReadPreamble)。
在退出写入非依赖项属性数据成员的方法之前,调用 WritePostscript 方法。
如果类包含的非依赖项属性数据成员为 DependencyObject 对象,则每次更改这些成员的值时,还必须调用 OnFreezablePropertyChanged 方法,即使您将这些成员设置为 null 也是如此。
注意 |
---|
通过调用基实现来开始重写的每个 Freezable 方法,这一点非常重要。 |
有关自定义 Freezable 类的示例,请参见 Custom Animation Sample(自定义动画示例)。