Freezable 物件概觀

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

什麼是可凍結的?

Freezable是一種特殊類型的物件,具有兩種狀態:凍結和凍結。 當凍結時, Freezable 看起來像任何其他物件一樣。 當凍結時, Freezable 無法再修改。

Freezable會提供 Changed 事件,以通知觀察者對物件所做的任何修改。 凍結 a Freezable 可以改善其效能,因為不再需要花費資源來變更通知。 凍結 Freezable 也可以線上程之間共用,但無法凍結 Freezable

Freezable雖然類別有許多應用程式,但是 Windows Presentation Foundation 中大部分 Freezable 的物件 (WPF) 與圖形子系統相關。

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 圖形子系統會使用您所提供的資訊來繪製一組圖元,以建立按鈕的外觀。 雖然您使用單色筆刷來描述按鈕的繪製方式,但您的純色筆刷實際上並不會進行繪製。 圖形系統會為按鈕和筆刷產生快速、低層級的物件,而這是實際顯示在畫面上的物件。

如果您要修改筆刷,則必須重新產生這些低層級的物件。 可凍結的類別可讓筆刷找出其對應的已產生、低層級的物件,並在變更時加以更新。 啟用這項功能時,筆刷稱為「未凍結」。

可凍結的 Freeze 方法可讓您停用此自我更新功能。 您可以使用這個方法,讓筆刷變成「凍結」或無法修改。

注意

並非每個可凍結的物件都可以凍結。 若要避免 InvalidOperationException 擲回,請檢查可凍結物件 CanFreeze 的屬性值,以判斷是否可以在嘗試凍結之前加以凍結。

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}
If myBrush.CanFreeze Then
    ' Makes the brush unmodifiable.
    myBrush.Freeze()
End If

當您不再需要修改可凍結時,凍結它可提供效能優勢。 如果您在此範例中凍結筆刷,圖形系統將不再需要監視它是否有變更。 圖形系統也可以進行其他優化,因為它知道筆刷不會變更。

注意

為了方便起見,可凍結的物件會保持未凍結,除非您明確凍結它們。

使用 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 無法修改,您可以呼叫其 Freeze 方法。 當您凍結包含可凍結物件的物件時,這些物件也會被凍結。 例如,如果您凍結 PathGeometry ,它所包含的資料和區段也會被凍結。

如果下列任一條件成立,則 無法 凍結凍結:

  • 它有動畫或資料系結屬性。

  • 它具有動態資源所設定的屬性。 (如需動態資源的詳細資訊,請參閱 XAML 資源 。 )

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

如果這些條件為 false,而您不想要修改 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/optionsPresentationOptions 是對應此命名空間的建議前置詞:

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

由於並非所有的 XAML 讀取器都會辨識此屬性,建議您使用 mc:可忽略屬性 將屬性標示 Presentation:Freeze 為可忽略:

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

如需詳細資訊,請參閱 mc:可忽略的屬性 頁面。

「解除凍結」是凍結的

一旦凍結,就永遠不能修改或解除凍結,不過,您可以使用 CloneCloneCurrentValue 方法來建立未凍結的 Freezable 複製。

在下列範例中,會使用筆刷設定按鈕的背景,然後將筆刷凍結。 未凍結的複製會使用 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 方法會產生無法凍結的深層複本。 如果可凍結的物件包含其他凍結的可凍結物件,則它們也會進行複製和修改。 例如,如果您複製凍結 PathGeometry 以使其成為可修改的,則它所包含的資料和區段也會複製並變成可修改。

建立您自己的凍結類別

衍生自 Freezable 的類別會取得下列功能。

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

  • 執行緒安全:凍結 Freezable 可以跨執行緒共用。

  • 詳細的變更通知:與其他 DependencyObject 的不同,可凍結的物件會在子屬性值變更時提供變更通知。

  • 簡易複製:可凍結的類別已執行數個產生深層複製的方法。

Freezable是的型 DependencyObject 別,因此會使用相依性屬性系統。 您的 Freezable 類別屬性不必是相依性屬性,但使用相依性屬性將會減少您必須撰寫的程式碼數量,因為類別是以相依性屬性為考慮而設計的。 如需相依性屬性系統的詳細資訊,請參閱相依性 屬性總覽

每個 Freezable 子類別都必須覆寫 CreateInstanceCore 方法。 如果您的類別使用其所有資料的相依性屬性,您就會完成。

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

您也必須觀察下列用來存取和寫入非相依性屬性之資料成員的規則:

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

  • 在任何撰寫非相依性屬性資料成員的 API 開頭,呼叫 WritePreamble 方法。 (在 API 中呼叫 WritePreamble 之後,如果您也讀取非相依性屬性資料成員,就不需要另外進行呼叫 ReadPreamble 。 )

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

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

注意

使用基底執行的呼叫來開始您覆寫的每個 Freezable 方法,是很重要的。

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

另請參閱