Freezable 物件概觀

更新:2007 年 11 月

本主題描述如何有效率地使用並建立 Freezable 物件,以提供可以協助改善應用程式效能的特定功能。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;  

當呈現按鈕時,WPF 圖形子系統會使用您所提供的資訊來繪製像素群組,以建立按鈕的外觀。雖然使用純色筆刷來描述應該繪製按鈕的方式,但是純色筆刷並未實際進行繪製。圖形系統會針對按鈕和筆刷產生快速且低階的物件,而這些就是實際顯示在螢幕上的物件。

如果要修改筆刷,則必須重新產生那些低階物件。Freezable 類別讓筆刷可以找到與它對應的已產生低階物件,並在筆刷變更時更新這些物件。啟用這個功能時,筆刷就稱為「未凍結」。

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

注意事項:

並非所有 Freezable 物件都可以進行凍結。為了避免擲回 InvalidOperationException,請在嘗試凍結之前,先檢查 Freezable 物件的 CanFreeze 屬性值,判斷是否可以進行凍結。

if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}

當您不再需要修改 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;

凍結 Freezable

若要讓 Freezable 變為不可修改,請呼叫它的 Freeze 方法。當您凍結的物件內含 Freezable 物件時,那些物件也會一併凍結。例如,如果凍結 PathGeometry,則也會一併凍結它所含的圖形和區段。

如果符合下列任一項,就「不可以」凍結 Freezable 物件:

  • 具有顯示為動畫或資料繫結屬性。

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

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

如果不符合上述條件,而且不想要修改 Freezable,則應該考慮將之凍結,如前所述來提升效能。

呼叫 Freezable 的 Freeze 方法之後,就無法再修改 Freezable。如果嘗試修改已凍結的物件,則會擲回 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());
}

為了避免擲回這個例外狀況,可以使用 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;
}


在上述程式碼範例中,使用 Clone 方法建立了凍結物件的可修改複本。下一節會詳細討論複製 (Clone)。

注意:因為已凍結的 Freezable 無法建立動畫,所以在您嘗試使用 Storyboard 建立已凍結 Freezable 物件的動畫時,動畫系統會自動建立這些凍結物件的可修改複製品 (Clone)。如果想要建立物件的動畫,則請將物件保留為未凍結狀態,以去除複製所造成的效能負荷。如需使用腳本建立動畫的詳細資訊,請參閱腳本概觀

透過標記進行凍結

若要凍結以標記宣告的 Freezable 物件,請使用 PresentationOptions:Freeze 屬性。在下列範例中,SolidColorBrush 宣告為頁面資源,而且已凍結。接著會使用它來設定按鈕的背景。

<Page 
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
  xmlns:mc="https://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 屬性,則必須對應至呈現選項命名空間:https://schemas.microsoft.com/winfx/2006/xaml/presentation/options。PresentationOptions 是對應這個命名空間的建議前置字元:

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

因為並非所有 XAML 讀取器 (Reader) 都可以辨識這個屬性,所以建議您使用 mc:Ignorable 屬性將 Presentation:Freeze 屬性標示為可忽略:

  xmlns:mc="https://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;
注意事項:

不論使用哪種複製方法,都絕不會將動畫複製至新的 Freezable

CloneCloneCurrentValue 方法都會產生 Freezable 的深層複本 (Deep Copy)。如果 Freezable 包含其他凍結的 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 類別的範例,請參閱自訂動畫範例

請參閱

工作

自訂動畫範例

概念

相依性屬性概觀

自訂相依性屬性

參考

Freezable