冻结对象概述
本主题介绍如何有效使用和创建 Freezable 对象,这些对象提供可帮助提高应用程序性能的特殊功能。 可冻结对象的示例包括画笔、笔、转换、几何图形和动画。
什么是 Freezable?
Freezable 是一种具有两种状态的特殊对象:未冻结和冻结。 取消冻结时,Freezable 的行为似乎与任何其他对象一样。 冻结后,不能再修改 Freezable。
Freezable 提供一个 Changed 事件,以通知观察者对对象所做的任何修改。 冻结 Freezable 可以提高其性能,因为它不再需要在更改通知上花费资源。 冻结的 Freezable 也可以跨线程共享,而未冻结的 Freezable 则不能。
尽管 Freezable 类有许多应用程序,但 Windows Presentation Foundation(WPF)中的大多数 Freezable 对象都与图形子系统相关。
Freezable 类使某些图形系统对象更易于使用,并有助于提高应用程序性能。 继承自 Freezable 的类型示例包括 Brush、Transform和 Geometry 类。 由于它们包含非托管资源,因此系统必须监视这些对象以进行修改,然后在对原始对象进行更改时更新相应的非托管资源。 即使实际上未修改图形系统对象,系统仍必须花费一些资源来监视该对象,以防更改它。
例如,假设你创建了一个 SolidColorBrush 画笔,并用它来绘制按钮的背景。
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
Dim myButton As New Button()
Dim myBrush As New SolidColorBrush(Colors.Yellow)
myButton.Background = myBrush
呈现按钮时,WPF 图形子系统使用提供的信息绘制一组像素来创建按钮的外观。 虽然你使用了纯色画刷来描述按钮应如何绘制,但你的纯色画刷实际上并没有实际进行绘制。 图形系统为按钮和画笔生成快速、低级别的对象,并且是实际出现在屏幕上的对象。
如果要修改画笔,则必须重新生成这些低级别对象。 Freezable 类使画笔能够找到生成的相应低级别对象并在更改时更新它们。 启用此功能后,画笔据说是“不受冻结的”。
使用 Freezable 的 Freeze 方法可以禁用此自更新功能。 可以使用此方法使画笔变为“冻结”或不可修改。
说明
并非每个可冻结对象都可以冻结。 为了避免引发 InvalidOperationException,您应在尝试冻结 Freezable 对象之前,检查其 CanFreeze 属性的值,以确定是否可以冻结。
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
当您不再需要修改一个可冻结的对象时,冻结它可以提供性能优势。 如果在此示例中将画笔冻结,图形系统将不再需要监视其更改。 图形系统还可以进行其他优化,因为它知道画笔不会更改。
说明
为方便起见,可冻结对象通常不会冻结,除非您显式地将它们冻结。
使用 Freezable
使用解冻的 Freezable 就像使用任何其他类型的对象一样。 在以下示例中,SolidColorBrush 的颜色在用于绘制按钮背景后从黄色更改为红色。 图形系统在后台工作,以便在下次刷新屏幕时自动将按钮从黄色更改为红色。
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
// Changes the button's background to red.
myBrush.Color = Colors.Red;
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
冻结 Freezable
若要使 Freezable 不可修改,请调用其 Freeze 方法。 冻结包含可冻结对象的对象时,这些对象也会被冻结。 例如,如果冻结 PathGeometry,它所包含的图形和段也会被冻结。
如果满足以下任一条件,则无法冻结 Freezable:
如果这些条件不成立,并且你不打算修改 Freezable,则应将其冻结,以获得前面所述的性能优势。
一旦调用可冻结对象的 Freeze 方法,它将无法再被修改。 尝试修改冻结对象会导致引发 InvalidOperationException。 以下代码会引发异常,因为我们试图在冻结画笔后对其进行修改。
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());
}
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
为了避免引发此异常,可以使用 IsFrozen 方法来确定 Freezable 是否已冻结。
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;
}
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
在前面的代码示例中,使用 Clone 方法对冻结对象进行了可修改的副本。 下一部分将更详细地讨论克隆。
说明
由于无法对冻结对象进行动画处理,因此当你尝试使用 Storyboard对其进行动画处理时,动画系统将自动创建被冻结的 Freezable 对象的可修改克隆。 若要消除克隆导致的性能开销,如果打算对对象进行动画处理,请保持对象不冻结。 有关使用情节提要进行动画处理的详细信息,请参阅 情节提要概述。
从标记冻结
若要冻结在标记中声明的 Freezable 对象,请使用 PresentationOptions:Freeze
属性。 在以下示例中,SolidColorBrush 被声明为页面资源并被冻结。 然后,它用于设置按钮的背景。
<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/presentation/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>
若要使用 Freeze
属性,必须映射到呈现选项命名空间:http://schemas.microsoft.com/winfx/2006/xaml/presentation/options
。 推荐使用 PresentationOptions
作为映射此命名空间的前缀。
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
由于并非所有 XAML 读取器都能识别此属性,因此建议使用 mc:Ignorable Attribute 将 PresentationOptions:Freeze
属性标记为可忽略:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
有关详细信息,请参阅 mc:Ignorable 属性页面。
“解冻”Freezable
冻结后,永远不能修改或取消冻结 Freezable;但是,可以使用 Clone 或 CloneCurrentValue 方法创建未冻结的克隆。
在以下示例中,按钮的背景使用画笔设置,然后该画笔被冻结。 使用 Clone 方法创建画笔的解冻副本。 克隆经过修改,用于将按钮的背景从黄色更改为红色。
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;
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
说明
无论使用哪种克隆方法,动画都不会复制到新的 Freezable。
Clone 和 CloneCurrentValue 方法可生成 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 类的示例,请参阅 自定义动画示例。