Freezable 物件概觀
更新:2007 年 11 月
本主題描述如何有效率地使用並建立 Freezable 物件,以提供可以協助改善應用程式效能的特定功能。Freezable 物件的範例包含筆刷、畫筆、轉換、幾何和動畫。
此主題包括下列章節:
什麼是 Freezable
Freezable 是具有下列兩種狀態的特殊物件型別:未凍結和凍結。未凍結時,Freezable 的行為就和其他任何物件一樣。而凍結時,則無法再修改 Freezable。
Freezable 提供 Changed 事件,以在發生任何物件修改時通知觀察器。凍結 Freezable 之後,因為不再需要花費資源在變更告知上,所以可以改善效能。凍結的 Freezable 物件也可以供多個執行緒共用,而未凍結的 Freezable 則不可以。
雖然 Freezable 類別具有許多應用程式,但是 Windows Presentation Foundation (WPF) 中的大部分 Freezable 物件都是與圖形子系統相關。
Freezable 類別可以簡化特定圖形系統物件的使用,而且可以協助改善應用程式效能。繼承自 Freezable 的型別範例包含 Brush、Transform 和 Geometry 類別。因為它們包含 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 的 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,但您可以使用 Clone 或 CloneCurrentValue 方法建立未凍結的複製品。
在下列範例中,使用筆刷設定按鈕的背景,隨後凍結該筆刷。然後使用 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。 |
Clone 和 CloneCurrentValue 方法都會產生 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 類別的範例,請參閱自訂動畫範例。