一、什麼是依賴屬性
依賴屬性就是一種自己可以沒有值,并且可以通過綁定從其他資料源擷取值。依賴屬性可支援WPF中的樣式設定、資料綁定、繼承、動畫及預設值。
将所有的屬性都設定為依賴屬性并不總是正确的解決方案,具體取決于其應用場景。有時,使用私有字段實作屬性的典型方法便能滿足要求。MSDN中給出了下面幾種應用依賴屬性的場景:
1. 希望可在樣式中設定屬性。
2. 希望屬性支援資料綁定。
3. 希望可使用動态資源引用設定屬性。
4. 希望從元素樹中的父元素自動繼承屬性值。
5. 希望屬性可進行動畫處理。
6. 希望屬性系統在屬性系統、環境或使用者執行的操作或者讀取并使用樣式更改了屬性以前的值時報告。
7. 希望使用已建立的、WPF 程序也使用的中繼資料約定,例如報告更改屬性值時是否要求布局系統重新編寫元素的可視化對象。
二、依賴屬性的特點
1、屬性變更通知
無論什麼時候,隻要依賴屬性的值發生改變,wpf就會自動根據屬性的中繼資料觸發一系列的動作,這些動作可以重新呈現UI元素,也可以更新目前的布局,重新整理資料綁定等等,這種變更的通知最有趣的特點之一就是屬性觸發器,它可以在屬性值改變的時候,執行一系列自定義的動作,而不需要更改任何其他的代碼來實作。通過下面的示例來示範屬性變更通知
示例:當滑鼠移動到Button按鈕上面時,文字的前景色變為紅色,離開時變為預設顔色黑色,采用傳統方式和依賴屬性兩種方式實作:
(1)、使用傳統方式實作,在Button按鈕上定義MouseEnter和MouseLeave兩個事件,分别處理滑鼠移動到按鈕上面和離開,XAML界面代碼:

1 <Window x:Class="WpfDemo.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Grid面闆" Height="237" Width="525" WindowStartupLocation="CenterScreen">
5 <Grid >
6 <Button Height="30" Width="200" MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave" >滑鼠移動到上面,前景色變為紅色</Button>
7 </Grid>
8 </Window>

C#背景代碼實作:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Windows;
7 using System.Windows.Controls;
8 using System.Windows.Data;
9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15
16 namespace WpfDemo
17 {
18 /// <summary>
19 /// MainWindow.xaml 的互動邏輯
20 /// </summary>
21 public partial class MainWindow : Window
22 {
23 public MainWindow()
24 {
25 InitializeComponent();
26 }
27
28 /// <summary>
29 /// 滑鼠移動到按鈕上面
30 /// </summary>
31 /// <param name="sender"></param>
32 /// <param name="e"></param>
33 private void Button_MouseEnter(object sender, MouseEventArgs e)
34 {
35 Button btn = sender as Button;
36 if (btn != null)
37 {
38 btn.Foreground = Brushes.Red;
39 }
40 }
41
42 /// <summary>
43 /// 滑鼠離開按鈕
44 /// </summary>
45 /// <param name="sender"></param>
46 /// <param name="e"></param>
47 private void Button_MouseLeave(object sender, MouseEventArgs e)
48 {
49 Button btn = sender as Button;
50 if (btn != null)
51 {
52 btn.Foreground = Brushes.Black;
53 }
54 }
55 }
56 }

(2)使用依賴屬性實作,XAML界面代碼:

1 <Window x:Class="WpfDemo.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Grid面闆" Height="237" Width="525" WindowStartupLocation="CenterScreen">
5 <Grid >
6 <Button Height="30" Width="200">滑鼠移動到上面,前景色變為紅色
7 <Button.Style>
8 <Style TargetType="Button">
9 <Style.Triggers>
10 <Trigger Property="IsMouseOver" Value="true">
11 <Setter Property="Foreground" Value="Red"></Setter>
12 </Trigger>
13 </Style.Triggers>
14 </Style>
15 </Button.Style>
16 </Button>
17 </Grid>
18 </Window>

使用上面的兩種方式都可以實作Button按鈕的前景色改變,效果如下:
在判斷屬性IsMouseOver的值為false的時候,自動将Foreground的值改為之前的值,是以就不需要寫IsMouseOver的值為false的時候,将Foreground的值改為Black。
2、屬性值繼承
是指屬性值自頂向下沿着元素樹進行傳遞。

1 <Window x:Class="WpfDemo.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="依賴屬性" Height="237" Width="525" FontSize="18" WindowStartupLocation="CenterScreen">
5 <Grid >
6 <StackPanel>
7 <TextBlock>我使用的是繼承的fontsize</TextBlock>
8 <TextBlock FontSize="11">我使用的是自己的fontsize</TextBlock>
9 </StackPanel>
10 </Grid>
11 </Window>

界面運作效果:
3、節省記憶體空間
依賴屬性和CLR屬性在記憶體的使用上是截然不同的,每個CLR屬性都包含一個非static的字段,是以當我們執行個體化一個類型的時候,就會建立該類型所擁有的所有CLR屬性,也就是說一個對象所占用的記憶體在調用new操作進行執行個體化的時候就已經決定了、而wpf允許對象在建立的時候并不包含用于存儲資料的空間,隻保留在需要用到資料的時候能夠獲得該預設值,即用其他對象資料或者實時配置設定空間的能力。
三、如何自定義依賴屬性
1、聲明依賴屬性變量。依賴屬性的聲明都是通過public static來公開一個靜态變量,變量的類型必須是DependencyProperty
2、在屬性系統中進行注冊。使用DependencyProperty.Register方法來注冊依賴屬性,或者是使用DependencyProperty.RegisterReadOnly方法來注冊
3、使用.NET屬性包裝依賴屬性
在類上實作屬性時,隻要該類派生自 DependencyObject,便可以選擇使用 DependencyProperty 辨別符來标示屬性,進而将其設定為依賴屬性。其文法如下:

1 public static DependencyProperty TextProperty;
2 TextProperty =
3 DependencyProperty.Register("Text", //屬性名稱
4 typeof(string), //屬性類型
5 typeof(TestDependencyPropertyWindow), //該屬性所有者,即将該屬性注冊到那個類上
6 new PropertyMetadata("")); //屬性預設值
7
8 public string Text
9 {
10 get { return (string)GetValue(TextProperty); }
11 set { SetValue(TextProperty, value); }
12 }

示例:自定義一個依賴屬性,界面包括一個TextBox和TextBlock,TextBlock上面字型的前景色随TextBox裡面輸入的顔色而改變,如果TextBox裡面輸入的值可以轉換成顔色,TextBlock字型的前景色會顯示輸入的顔色值,如果不能轉換,顯示預設的前景色。
1、在目前項目裡面添加一個WPF版的使用者控件,命名為“MyDependencyProperty”,在MyDependencyProperty.xaml.cs檔案裡面自定義一個依賴屬性:

1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6 using System.Windows;
7 using System.Windows.Controls;
8 using System.Windows.Data;
9 using System.Windows.Documents;
10 using System.Windows.Input;
11 using System.Windows.Media;
12 using System.Windows.Media.Imaging;
13 using System.Windows.Navigation;
14 using System.Windows.Shapes;
15
16 namespace WpfDemo
17 {
18 /// <summary>
19 /// MyDependencyProperty.xaml 的互動邏輯
20 /// </summary>
21 public partial class MyDependencyProperty : UserControl
22 {
23 public MyDependencyProperty()
24 {
25 InitializeComponent();
26 }
27
28 //1、聲明依賴屬性變量
29 public static readonly DependencyProperty MyColorProperty;
30
31 //2、在屬性系統中進行注冊
32 static MyDependencyProperty()
33 {
34 MyColorProperty = DependencyProperty.Register("MyColor", typeof(string), typeof(MyDependencyProperty),
35 new PropertyMetadata("Red", (s, e) =>
36 {
37 var mdp = s as MyDependencyProperty;
38 if (mdp != null)
39 {
40 try
41 {
42 var color = (Color)ColorConverter.ConvertFromString(e.NewValue.ToString());
43 mdp.Foreground = new SolidColorBrush(color);
44 }
45 catch
46 {
47 mdp.Foreground = new SolidColorBrush(Colors.Black);
48 }
49 }
50
51 }));
52 }
53
54 //3、使用.NET屬性包裝依賴屬性:屬性名稱與注冊時候的名稱必須一緻,
55 //即屬性名MyColor對應注冊時的MyColor
56 public string MyColor
57 {
58 get
59 {
60 return (string)GetValue(MyColorProperty);
61 }
62 set
63 {
64 SetValue(MyColorProperty, value);
65 }
66 }
67 }
68 }

快速定義依賴屬性的快捷方式:
輸入propdp,連續按兩下Tab健,自動生成定義依賴屬性的文法。和輸入cw連續按兩下Tab健,自動生成Console.Write()一樣。

1 public int MyProperty
2 {
3 get { return (int)GetValue(MyPropertyProperty); }
4 set { SetValue(MyPropertyProperty, value); }
5 }
6
7 // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
8 public static readonly DependencyProperty MyPropertyProperty =
9 DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

2、在MyDependencyProperty.xaml裡面添加一個TextBlock

1 <UserControl x:Class="WpfDemo.MyDependencyProperty"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6 mc:Ignorable="d"
7 d:DesignHeight="300" d:DesignWidth="300">
8 <Grid>
9 <TextBlock>我是自定義的依賴屬性</TextBlock>
10 </Grid>
11 </UserControl>

3、在MainWindow.xaml裡面引用新建立的使用者控件,并添加一個TextBox,用于輸入顔色值,并将自定義的依賴屬性MyColor綁定到TextBox

1 <Window x:Class="WpfDemo.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:p="clr-namespace:WpfDemo"
5 Title="依賴屬性" Height="237" Width="525" WindowStartupLocation="CenterScreen">
6 <Grid >
7 <StackPanel>
8 <TextBox Name="tbColor"></TextBox>
9 <p:MyDependencyProperty MyColor="{Binding Path=Text,ElementName=tbColor}" ></p:MyDependencyProperty>
10 </StackPanel>
11 </Grid>
12 </Window>

在設計界面顯示的效果:
4、程式運作效果:
在TextBox裡面輸入正确的顔色值,前景色會顯示為目前輸入的顔色:
在TextBox裡面輸入錯誤的顔色值,前景色會顯示為預設顔色: