1. 概述 依賴屬性(DependencyProperty)是UWP的核心概念,它是有DependencyObject提供的一種特殊的屬性。由於UWP的幾乎所有UI元素都是集成於DependencyObject的FramewordElement,並且這些UI元素的幾乎所有屬性及它們出現在XAML中的 ...
1. 概述
依賴屬性(DependencyProperty)是UWP的核心概念,它是有DependencyObject提供的一種特殊的屬性。由於UWP的幾乎所有UI元素都是集成於DependencyObject的FramewordElement,並且這些UI元素的幾乎所有屬性及它們出現在XAML中的幾乎所有屬性都是依賴屬性,所以可以說依賴屬性是專門為UI設計的屬性系統。
依賴屬性的定義:
/// <summary>
/// 獲取或設置Title的值
/// </summary>
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// 標識 Title 依賴屬性。
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MyPage), new PropertyMetadata(string.Empty));
如上述代碼所示,和CLR屬性不同依賴屬性需要定義TitleProperty ,然後在屬性包裝器(Getter和Setter)中通過GetValue和SetValue函數操作屬性值。
2. 使用的場景
UWP的依賴屬性經過大幅簡化(相對於WPF),更關註它的核心功能:使用綁定,通過多個輸入計算屬性值,屬性值變化通知,節約記憶體使用。
2.1 綁定
通過屬性包裝器,依賴屬性可以像CLR屬性那樣使用,也可以在XAML中通過綁定來使用,這是CLR屬性不能提供的功能。
在XAML中使用:
<TextBlock x:Name="TextElement"
Text="{Binding Title}" />
在代碼中使用:
var binding = new Windows.UI.Xaml.Data.Binding();
binding.Path = new PropertyPath("Title");
TextElement.SetBinding(TextBlock.TextProperty, binding);
2.2 通過多個輸入計算屬性值
在UWP中依賴屬性通過多個輸入源計算屬性的值,從而使開發人員就避免了不必要的屬性設置值或者處理屬性值變更通知。以下列表從優先順序由高到低列出了依賴屬性的使用順序。
- 動畫值 正在運行的動畫,或具有 HoldEnd 行為的動畫。若要進行動畫處理,動畫的目標屬性必須是依賴項屬性。
本地值 在代碼中直接為對象實例設置的屬性值,或者在 XAML 中設置的屬性值。
本地值可以通過調用ClearValue函數清除,從而使屬性值還原成預設值(以我的經驗來說,很少會用到)。
- 模板屬性 如果在某個模板(來自 ControlTemplate 或 DataTemplate)中創建一個元素,該元素就會擁有這些模板屬性。
- 樣式設置器 Style中的Setter。
- 繼承值 元素可以從其在對象樹中的父級繼承依賴項屬性的值。譬如開發人員不必為每個TextBlock設置FontSize,只需要為父容器設置FontSize即可套用到父容器中的所有TextBlock上。
預設值 不是指數據類型的預設值,是指PropertyMetadata中指定的預設值。在上面Title的例子中,預設值是string.Empty。
2.3 屬性值變化通知
使用依賴屬性,不必再實現INotifyPropertyChanged即可在屬性改變時通知UI更新。也可以在PropertyMeta中使用PropertyChangedCallback或DependencyObject.RegisterPropertyChangedCallback監視依賴屬性的屬性值改變。
2.4 節約記憶體使用
前面提到,UWP可以不必為所有值都設置值,UI元素的依賴屬性可以從樣式、繼承值、預設值等計算出實際值,並不需要分配記憶體;如果設置了本地值,這個本地值將存儲在HashTable中,之後從這個HashTable中讀取。這是一種以時間換空間的做法。
假設一個Control自身及所繼承的FrameworkElement等父類型中所有的屬性加起來大概50個,它的控制項模板中大概有3個FrameworkElement,所有屬性都是double類型並且所有都不必要設置值,一個Control就可以節省50 * 3 * 8=1200位元組的記憶體空間。我做過的系統最多同時在UI上放了10W個Control,那麼就總共節省了大概100M記憶體。
這麼極端的情況也才節省這點記憶體,作用好像也沒那麼大(難道我算錯了?)。關於節約記憶體這點稍微瞭解下就好,有助於瞭解依賴屬性的原理,並且面試的時候有可能有幫助。
3 依賴屬性和CLR屬性之間的選擇
使用依賴屬性的情況
- 基本上所有繼承DependencyObject的類中的屬性都應該是依賴屬性;
- 需要使用Binding、Style或動畫設置值的屬性;
- 需要監視屬性值變化通知;
- 記憶體真的真的不夠用;
使用CLR屬性的情況
- 集合屬性。在UWP中常見的集合屬性,只有ItemsControl的ItemsSource等少數幾個是依賴屬性,其它大部分都是CLR屬性,譬如Hub的Sections;
- CPU性能敏感的場合。依賴屬性是用時間換空間的概念,假如需要頻繁讀寫而又不需要綁定,可以考慮使用CLR屬性;
4. 依賴屬性和線程
所有依賴屬性都只能在UI線程上使用,否則會拋異常(“應用程式調用一個已為另一線程整理的介面。”)。不過如果使用Async模式的話通常會迴避了線程的問題。