前言
如果看文字版本不沒耐心, 點擊下方查看視頻講解。
點擊觀看視頻
回顧
在講解依賴屬性之前, 首先我們熟悉一下WPF當中的綁定(Binding), 可能你曾用過WPF中綁定的語法。
下面演示了在Button按鈕上為Content屬性設置了一個綁定語法, 如下所示:
<Button Content="{Binding Content}"/>
當你在Content屬性按下F12轉到定義時,可以觀察到Button按鈕所繼承的類的定義,如下所示:
如圖上紅圈位置, 定義了一個靜態的只讀字段ContentProperty。
通過查看該字段的類型DependencyProperty,沒錯!它就是一個依賴屬性。
看到這里, 你應該有了一個概念, 在WPF當中, 所有支持綁定的屬性本質上它都是封裝后的依賴屬性。
那么也就是說, 只有依賴屬性才可以進行綁定。
如果你不能夠理解, 我們再舉個例子, 當你使用WPF當中PasswordBox控件的時候, 你會發現Password屬性不支持綁定, 當按下F12轉到定義,會發現該Password就是一個普通屬性:
這也就是為什么Password不支持綁定的真正原因。
屬性和依賴屬性
現在, 我們來解決另外一個概念問題, 可能看到上面, 你還是不太清楚屬性和依賴屬性它們的區別在哪里?
屬性
很常見, 在C#中的標准屬性,通常會由一個非靜態類型的私有字段支持, 假設當前有一個對象, 它擁有100個標准屬性,
並且背后都定義了一個4字節的字段, 如果我們初始化10000個這樣的對象, 那么這些字段將占用100×4×10000= 3.81M 內存。
但是實際上, 我們並非使用到所有的屬性, 這就意味着大多數內存會被浪費!
依賴屬性
如何解決屬性帶來的問題? 我們回到現實生活當中想象一種場景, 假設老王和你的女朋友去旅游, 他們准備東西的時候大都是把必要的帶上, 而不是說女朋友想喝水,要不然在行李箱里面放一箱水? 那么是不是意味着上廁所把紙帶上? 洗發水? 沐浴露? 天吶, 這真是一場糟糕的旅行。
我們都知道, 水、廁所紙、洗發水、沐浴露這些酒店里面都有阿, 為什么要我們自己帶? 所以我們懂了, 這些不必要帶的東西我們可以依賴外部提供給我們。是的, 我們把這種思想帶到編程當中。
所以, 這就是WPF當中的依賴屬性的理念, 也許你在其它的地方都聽過別人講解過依賴屬性, 並且他們都告訴你依賴屬性本身沒有值, 可以依賴綁定來源獲得值。
總結
下面是對比屬性/依賴屬性改的定義:
//依賴屬性
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(Test), new PropertyMetadata(0));
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
//屬性
private int name;
public int Name
{
get { return name; }
set { name = value; }
}
通過上面我們可以了解到, 普通的屬性通過定義了一個私有字段, 然后通過這個私有字段進行讀取或設置值。
而對於依賴屬性, 它同樣是使用屬性的訪問方式, 但是它獲取和設置值是通過依賴屬性而不再是私有字段。
依賴屬性定義
同樣, 你也許在許多的博客當中, 別人教你怎么定義依賴屬性, 它通過DependencyProperty類的靜態Register方法注冊, 如下所示:
public class Test : DependencyObject
{
public string Message
{
get { return (string)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("Message", typeof(string), typeof(Test), new PropertyMetadata(null));
}
上面的代碼當中, 我們定義了一個string類型的依賴屬性Message, 如果你們有看過WPF的源代碼, 你可能會了解, 在其背后, 生成了一個key/value存儲在Hashtable里面。
- 生成key的代碼片段
- 添加到Hashtable中
終於解開了疑惑,有了這個全局的Hashtable存在, 意味着,他就是用來存儲依賴屬性實例的地方。
注意:定義依賴屬性的地方, 你會發現它需要繼承於DependencyObject對象, 通過該對象當中的提供的GetValue/SetValue方法, 我們可以獲取/設置依賴屬性的值:
GetValue/SetValue哪里來?
看看WPF中的繼承關系, 噢,懂了。 基類就是DependencyObject。
噢! 我的🐎🦆, 這也就是為什么, 微軟提供了標准屬性的訪問方式!讓我們傻傻分不清其內部通過依賴屬性來設置或獲取值。
附加屬性
字母意思來理解, 附加屬性就是對於一個對象而言, 本來它不具備這個屬性, 但是由於附加給這個對象, 然后才有了這個屬性,這種我們稱之為附加屬性。
注:附加屬性也是依賴屬性, 只是它的注冊方式與表達方式略有不同。
如果仍然不理解, 我們可以找到WPF當中經常用到的例子, 如下所示, 當一個按鈕處在不同的容器當中, 它就具有了不同的附加屬性:
<Grid>
<!--在Grid當中,具備Row/Column附加屬性-->
<Button Grid.Row="0" Grid.Column="0" />
</Grid>
<DockPanel>
<!--在DockPanel中,具備Dock附加屬性-->
<Button DockPanel.Dock="Left" />
</DockPanel>
<Canvas>
<!--在Canvas中,具備Left/Top/Rifht/Bottom等附加屬性-->
<Button Canvas.Left="10"/>
</Canvas>
依賴屬性與附加屬性對比
聲明方式
創建依賴屬性
我們可以輸入propdp再按兩下tab鍵生成一個依賴屬性的模板,如下所示:
注: 聲明依賴屬性的所在位置的對象必須直接或簡介繼承於DependencyObject對象, 這樣它才具備GetValue/SetValue方法。
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
創建附加屬性
我們可以輸入propa再按兩下tab鍵生成一個附加屬性的模板,如下所示:
注:聲明附加屬性的對象無需繼承於DependencyObject, 因為這個時候DependencyObject對象作為方法參數傳遞。
public static int GetMyProperty(DependencyObject obj)
{
return (int)obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, int value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
使用場景
依賴屬性: 當您需要單獨創建控件時, 並且希望控件的某個部分能夠支持數據綁定時, 你則可以使用到依賴屬性。
附加屬性: 這種情況很多, 正因為WPF當中並不是所有的內容都支持數據綁定, 但是我們希望其支持數據綁定, 這樣我們就可以創建基於自己聲明的附加屬性,添加到元素上, 讓其元素的某個原本不支持數據綁定的屬性間接形成綁定關系。
例如:為PassWord定義附加屬性與PassWord進行關聯。例如DataGrid控件不支持SelectedItems, 但是我們想要實現選中多個條目進行數據綁定, 這個時候也可以聲明附加屬性的形式讓其支持數據綁定。