Freezable 物件概觀

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

什麼是冰凍?

Freezable是具有兩種狀態的特殊物件類型:解除凍結和凍結。 解除凍結時, Freezable 似乎就像任何其他物件一樣。 凍結時, Freezable 無法再修改 。

Freezable提供 Changed 事件,以通知觀察者對 物件所做的任何修改。 凍結 Freezable 可以改善其效能,因為它不再需要將資源花在變更通知上。 凍結 Freezable 也可以跨執行緒共用,但無法凍結 Freezable

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

類別 Freezable 可讓您更輕鬆地使用特定圖形系統物件,並可協助改善應用程式效能。 繼承自 Freezable 的類型範例包括 BrushTransformGeometry 類別。 因為它們包含 Unmanaged 資源,所以系統必須監視這些物件以進行修改,然後在原始物件有變更時更新其對應的 Unmanaged 資源。 即使您實際上未修改圖形系統物件,系統仍必須花費部分資源來監視物件,以防您變更它。

例如,假設您建立 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

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

注意

為了方便起見,除非您明確凍結可凍結物件,否則可凍結的物件會維持未凍結狀態。

使用 Freezables

使用未凍結的凍結就像使用任何其他類型的物件一樣。 在下列範例中,在用來繪製按鈕背景之後,的色彩 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 ,它所包含的圖形和區段也會凍結。

如果下列任一項成立,則無法凍結 Freezable

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

  • 它有動態資源所設定的屬性。 (請參閱 XAML 資源 ,以取得動態資源的詳細資訊。

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

如果這些條件為 false,而且您不打算修改 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/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 Attribute 頁面。

「Unfreezing」 a Freezable

凍結之後, Freezable 永遠無法修改或解除凍結;不過,您可以使用 或 CloneCurrentValue 方法建立未凍結的複製 Clone 品。

在下列範例中,按鈕的背景是以筆刷設定,然後凍結該筆刷。 未凍結的複本是使用 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 包含其他凍結的可凍結物件,它們也會複製並加以修改。 例如,如果您複製凍結 PathGeometry 使其可修改,也會複製它所包含的圖形和區段,並加以修改。

建立您自己的 Freezable 類別

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

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

  • 執行緒安全性:凍結 Freezable 可以線上程之間共用。

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

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

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

每個 Freezable 子類別都必須覆寫 CreateInstanceCore 方法。 如果您的類別針對所有資料使用相依性屬性,則表示您已完成。

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

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

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

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

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

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

注意

請務必使用對基底實作的呼叫來開始覆寫的每個 Freezable 方法。

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

另請參閱