合理利用Freezable对象可以提升程序的性能,可冻结对象包括 画笔、笔、转换、几何图形和动画。

一、什么是可冻结的

Freezable 是具有两种状态的特殊对象类型:解冻和冻结。 解冻后,的 Freezable 行为与任何其他对象的行为类似。 冻结后,将无法 Freezable 再修改。

Freezable 提供 Changed 事件来通知观察者对对象的任何修改。 冻结 Freezable 会提高其性能,因为它不再需要在更改通知上消耗资源。 冻结 Freezable 还可在线程间共享,而 Freezable 不能冻结。

尽管 Freezable 类有许多应用程序,但 Freezable 中的大多数对象 Windows Presentation Foundation (WPF) 均与图形子系统相关。

Freezable 使用类可以更轻松地使用某些图形系统对象,并且可以帮助提高应用程序性能。 继承自的类型的示例 Freezable 包括 Brush Transform Geometry 类。 由于它们包含非托管资源,因此,系统必须监视这些对象进行修改,然后在对原始对象进行更改时更新其相应的非托管资源。 即使您不实际修改图形系统对象,系统仍必须将它的一些资源用于监视对象,以防您这样做更改。

例如,假设您创建了一个 SolidColorBrush 画笔,并使用它来绘制按钮的背景。

Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
if (myBrush.CanFreeze)
    // Makes the brush unmodifiable.
    myBrush.Freeze();

二、可冻结判断

若要使 Freezable 其成为不可修改的,请调用其 Freeze 方法。 冻结包含可冻结对象的对象时,这些对象也会被冻结。 例如,如果冻结 PathGeometry ,则它包含的图形和段也将被冻结。

如果满足以下任一条件,则 无法 冻结可冻结的:

它具有动画或数据绑定属性。

它包含动态资源设置的属性。 (有关动态资源的详细信息,请参阅 XAML 资源 。 )

它包含 Freezable 无法冻结的子对象。

如果这些条件为 false,并且你不打算修改,则 Freezable 应该冻结它以获得前面所述的性能优势。

调用可冻结的方法后 Freeze ,将无法再对其进行修改。 尝试修改冻结对象将导致 InvalidOperationException 引发。 下面的代码引发异常,因为我们尝试在冻结画笔之后修改画笔。

若要避免引发此异常,可以使用 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;
    // If the brush is not frozen,
    // it can be modified directly.
    myBrush.Color = Colors.Red;

三、标签语言修改

若要冻结 Freezable 在标记中声明的对象,请使用 PresentationOptions:Freeze 特性。 在下面的示例中,将 SolidColorBrush 声明为页资源并冻结。 然后,将使用它来设置按钮的背景。
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> xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="PresentationOptions"
若要使用 Freeze 属性,必须映射到表示选项命名空间: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options 。 PresentationOptions 建议用于映射此命名空间的前缀:
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
由于并非所有 XAML 读取器都能识别此特性,因此建议使用 mc:可忽略属性 将属性标记 Presentation:Freeze 为可忽略:
四、创建自己的可冻结对象

派生自的类具有 Freezable 以下功能。

特殊状态:只读 (冻结) 和可写状态。

线程安全:冻结 Freezable 可以在线程之间共享。

详细更改通知:与其他不同 DependencyObject ,可冻结对象会在子属性值更改时提供更改通知。

轻松克隆:可冻结的类已经实现了几种生成深层克隆的方法。

Freezable是的一种类型 DependencyObject ,因此使用了依赖属性系统。 类属性不一定是依赖属性,但使用依赖属性将减少您必须编写的代码量,因为 Freezable 该类是使用依赖属性设计的。 有关依赖属性系统的详细信息,请参阅 依赖属性概述

每个 Freezable 子类必须重写 CreateInstanceCore 方法。 如果你的类对所有数据使用依赖属性,则你已完成。

如果类包含非依赖属性数据成员,还必须重写以下方法:

CloneCore

CloneCurrentValueCore

GetAsFrozenCore

GetCurrentValueAsFrozenCore

FreezeCore

还必须遵守以下规则,以便访问和写入不是依赖属性的数据成员:

在读取非依赖属性数据成员的任何 API 的开头,调用 ReadPreamble 方法。

在写入非依赖属性数据成员的任何 API 的开头,请调用 WritePreamble 方法。 (WritePreamble 在 API 中调用后, ReadPreamble 如果还读取非依赖属性数据成员,则无需额外调用。 )

WritePostscript在退出写入非依赖属性数据成员的方法之前调用方法。

如果类包含非依赖属性数据成员 DependencyObject (这些对象是对象),则在 OnFreezablePropertyChanged 每次更改其值时,您还必须调用方法,即使您要将成员设置为 null 。