Freezable是WPF中一個特殊的基類,用於建立可以凍結(Freeze)的可變物件。凍結一個物件意味著將其狀態設定為只讀,從而提高效能並允許在多執行緒環境中共享物件。
Freezable的應用
我們定義畫刷資源的時候常常會這樣寫:
<SolidColorBrush x:Key="RedBrush" Color="Red" o:Freeze="True"/>
程式碼中的o:Freeze="True"
其實就是使用Freezable
的 Freeze
方法凍結畫刷,使之不可修改,系統不必監視該畫刷物件,從而減少資源消耗。
o:Freeze="True"
乍一看像附加屬性,其實並不是的。Freeze
屬性是http://schemas.microsoft.com/winfx/2006/xaml/presentation/options
XML名稱空間中定義的唯一屬性或其他程式設計元素。Freeze
屬性專門存在於此特殊名稱空間中,以便在根元素宣告中可以使用。處理Freeze
屬性的功能專門內建於處理已編譯應用程式的 XAML的XAML處理器中。
那是不是WPF中的所有資源都可以(需要)使用Freeze
方法凍結來提高效能呢?
Freezable類通常用於WPF中的資源和動畫,例如建立可重用的畫刷、幾何圖形和動畫。從Freezable
繼承的型別包括Brush
、Transform
和Geometry
類。由於它們包含非託管資源,因此係統必須監視這些物件發生的修改,然後在原始物件發生更改時更新對應的非託管資源。即使實際上並未修改圖形系統物件,系統仍必須消耗一些資源來監視該物件,以防更改它。
例如,假設建立一個SolidColorBrush畫筆並用它來繪製按鈕的背景。
<Window.Resources>
<SolidColorBrush x:Key="RedBrush" Color="Red"/>
</Window.Resources>
<Button Background="{StaticResource RedBrush}"/>
呈現按鈕時,WPF圖形子系統使用你提供的資訊來繪製一組畫素,以建立按鈕的外觀。儘管使用純色畫筆來描述按鈕的繪製方式,但純色畫筆實際上並沒有進行繪製。圖形系統為按鈕和畫筆生成快速、低階別的物件,實際顯示在螢幕上的就是這些物件。
如果要修改畫筆,則必須重新生成這些低階別物件。Freezable
類使畫筆能夠找到生成的相應低階別物件並在更改時更新它們。
注意事項
並非每個Freezable
物件都可以凍結。為避免引發InvalidOperationException
,請在嘗試凍結Freezable
物件之前檢查該物件的CanFreeze屬性值,以確定是否可以將其凍結。如果滿足以下任一條件,則無法凍結Freezable:
- 它具有動畫屬性或資料繫結屬性。
- 它具有由動態資源設定的屬性。
- 它包含無法凍結的Freezable子物件。
Freezable
物件呼叫Freeze
方法凍結後,就無法解凍。修改凍結物件屬性時會引發InvalidOperationException
。但是,可以使用Clone
或CloneCurrentValue
方法建立(深複製)解凍的副本。如果Freezable
包含其他已凍結的 Freezable
物件,它們也會被克隆並變為可修改。
無論使用哪種克隆方法,動畫都不會複製到新的 Freezable。
由於無法對凍結的Freezable
進行動畫處理,因此使用Storyboard
對其進行動畫處理時,動畫系統會自動建立凍結的Freezable
物件的可修改克隆。為了消除克隆導致的效能開銷,如果需要對物件進行動畫處理,請讓其保持解凍狀態。
附加屬性實現XAML中Freeze
上文中提到o:Freeze="True"
並不是透過附加屬性實現,而是內建於XAML處理器中實現。我們自己也可以透過附加屬性的方式實現,程式碼如下:
public class PresentationOptionsAttach
{
public static bool GetFreeze(Freezable freezable)
{
return (bool)freezable.GetValue(FreezeProperty);
}
public static void SetFreeze(Freezable freezable, bool value)
{
freezable.SetValue(FreezeProperty, value);
}
public static readonly DependencyProperty FreezeProperty =
DependencyProperty.RegisterAttached("Freeze", typeof(bool), typeof(PresentationOptionsAttach), new PropertyMetadata(false, OnFreezeChanged));
private static void OnFreezeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(d)) return;
if ((bool)e.NewValue)
{
Freezable freezable = d as Freezable;
if (freezable.CanFreeze)
freezable.Freeze();
}
}
}
小結
Freezable
是一個我們既熟悉又陌生的類,熟悉是因為我們經常使用,陌生是因為很少關注其最佳化效能的機制以及需要注意的地方。本文簡單介紹了Freezable
最佳化效能的機制以及注意事項,並提供了透過附加屬性的方式在XAML中凍結資源(純屬探索,實際意義不大)。