Freezable 物件概觀

本主題說明如何有效使用和建立 Freezable 物件,以提供特殊功能,協助提升應用程式效能。 Freezable 物件的範例包括筆刷、畫筆、轉換、幾何和動畫。

什麼是 Freezable?

Freezable 是一種特殊物件類型,共有兩種狀態︰未凍結和已凍結。 未凍結時,Freezable 的運作方式看似與其他物件無異。 凍結後,Freezable 就無法再修改。

Freezable 可產生 Changed 事件,通知觀察者對物件進行任何修改。 凍結 Freezable 可提升其效能,因為再也不需花費任何資源發佈變更通知。 凍結後,Freezable 亦能跨執行緒共用,但未凍結的 Freezable 則不行。

雖然 Freezable 類別的應用相當廣泛,但 Windows Presentation Foundation (WPF) 中的大多數 Freezable 物件都與圖形子系統有關。

Freezable 類別可讓某些圖形系統物件用起來更加容易,並有助於提升應用程式效能。 繼承自 Freezable 的類型範例包括 BrushTransformGeometry 類別。 由於上述物件包含非受控資源,因此系統必須監視這些物件是否被修改,然後在原始物件遭到變更時更新其對應的非受控資源。 即使您實際上並未修改圖形系統物件,系統仍必須花費部分資源來監視物件,以防您變更它。

例如,您建立了 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 方法可讓您停用這種自行更新功能。 您可以使用此方法「凍結」筆刷,或使筆刷無法修改。

注意

並非所有 Freezable 物件都可被凍結。 若要避免擲回 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 物件,否則 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 方法。 當您凍結的物件包含 Freezable 物件時,這些物件也會被凍結。 例如,當您凍結 PathGeometry 時,其包含的圖文框和區段也會被凍結。

當以下任一條件成立時,Freezable 物件就無法被凍結:

  • 該物件具備動畫或資料系結屬性。

  • 該物件的屬性由動態資源設定。 (如需關於動態資源的詳細資訊,請參閱 XAML 資源。)

  • 該物件包含無法凍結的 Freezable 子物件。

如果上述條件皆不成立,且您不打算再修改 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 方法建立已凍結物件的可修改複本。 下一節將進一步說明複製。

注意

已凍結的 Freezable 物件無法產生動畫效果,因此當您嘗試使用 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/optionsPresentationOptions 是對應此命名空間的建議前置詞:

xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"

由於並非所有 XAML 讀取器都能辨識此屬性,因此建議您使用 mc:Ignorable 屬性PresentationOptions:Freeze 屬性標記為可忽略:

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

如需詳細資訊,請參閱 mc:Ignorable 屬性頁面。

「解凍」Freezable 物件

凍結後,Freezable 就再也無法修改或解凍;不過,您可以使用 CloneCloneCurrentValue 方法替其建立未凍結的複製品。

在以下範例中,先以筆刷設定按鈕背景,再凍結該筆刷。 使用 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

CloneCloneCurrentValue 方法會產生 Freezable 物件的深層複本。 如果該 Freezable 物件包含其他已凍結的 Freezable 物件,這些物件也會被複製並轉變為可修改狀態。 例如,如果您複製已凍結的 PathGeometry 然後使其轉變為可修改,其包含的圖文框和區段也會被複製並變為可修改。

建立您自己的 Freezable 類別

衍生自 Freezable 的類別具備以下功能。

  • 特殊狀態:唯讀 (已凍結) 狀態和可寫入狀態。

  • 執行緒安全性:可跨執行緒共用已凍結的 Freezable

  • 詳細變更通知:不同於其他 DependencyObject,Freezable 物件會在子屬性值變更時發佈變更通知。

  • 易於複製:Freezable 類別已實作數個產生深層複製品的方法。

FreezableDependencyObject 的其中一種類型,因此同樣使用相依性屬性系統。 您的類別屬性不一定是相依性屬性,但使用相依性屬性可減少您要撰寫的程式碼數量,因為 Freezable 類別在設計時已將相依性屬性納入考量。 如需相依性屬性系統的詳細資訊,請參閱相依性屬性概觀

每個 Freezable 子類別都必須覆寫 CreateInstanceCore 方法。 當您類別中的所有資料都是相依性屬性,就代表您完成了。

如果您的類別中包含非相依性屬性資料成員,您還必須覆寫以下方法:

存取和寫入非相依性屬性資料成員時,您也必須遵守以下規則:

  • 在所有讀取非相依性屬性資料成員的 API 開頭,呼叫 ReadPreamble 方法。

  • 在所有寫入非相依性屬性資料成員的 API 開頭,呼叫 WritePreamble 方法。 (一旦您在 API 中呼叫 WritePreamble,當您要讀取非相依性屬性資料成員時,就不必再額外呼叫 ReadPreamble。)

  • 結束寫入至非相依性屬性資料成員的方法之前,請先呼叫 WritePostscript 方法。

如果您的類別包含屬於 DependencyObject 物件的非相依性屬性資料成員,每當要變更其中一員的值時,您都必須呼叫 OnFreezablePropertyChanged 方法,即使您將該成員設定為 null

注意

請務必先呼叫基礎實作,再開始覆寫每個 Freezable 方法。

如需自訂 Freezable 類別的範例,請參閱自訂動畫樣本

另請參閱