Data Binding機制可以說是WPF實現MVVM的關鍵,與之配套的Dependency Property和DataTemplate共同完成了數據到UI的通路,讓邏輯層與UI分離。
本文主要基於個人的理解,針對Data Binding簡要概括其主要用法、特性及注意事項等。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
舉個最簡單的Data Binding的栗子:
(UI代碼片段)
<StackPanel> <TextBox x:Name="textBox1" Margin="3" BorderBrush="Black"/> </StackPanel>
(定義用戶類)
public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
(后台代碼片段)
textBox1.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = new Student { Name = "Jack" } } );
運行程序,TextBox中顯示了Jack字符串。
這樣就實現了一個Student的對象和一個TextBox的binding,更加准確的說法是Student對象的Name屬性和TextBox的Text屬性進行了Binding。
觀察SetBinding方法的兩個參數:第一個參數是一個依賴屬性,第二個參數是一個Binding類型的對象,它指定了數據源和需要進行關聯的屬性。
但是所謂綁定,應該對更改也有響應才對。那么下面對代碼稍加更改加以驗證:
(UI代碼片段)
<StackPanel> <TextBox x:Name="textBox1" Margin="5" BorderBrush="Black"/> <Button Content="Show" Margin="5" Click="Button_Click"/> <Button Content="Change" Margin="5" Click="Button_Click_1"/> </StackPanel>
(后台代碼)
private Student stu; public MainWindow() { InitializeComponent(); textBox1.SetBinding(TextBox.TextProperty, new Binding("Name") { Source = stu = new Student { Name = "Jack" } } );
} private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show(stu.Name); } private void Button_Click_1(object sender, RoutedEventArgs e) { stu.Name += " Changed!"; }
運行代碼:

(textBox1中顯示stu的Name屬性值)

(在修改文本框中的值時,對象中的屬性也隨之更改)

(但在對象屬性更改時,textBox1中的文本並沒有變化)
如果希望屬性變化時UI元素也能響應,那就需要在屬性的set索引器中激發一個PropertyChanged事件,它被定義在INotifyPropertyChanged接口中。
修改Student類:
public class Student : INotifyPropertyChanged { public int Id { get; set; } private string name; public event PropertyChangedEventHandler PropertyChanged; public string Name { get { return name; } set { name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name")); } } public int Age { get; set; } }
測試:

(在stu.Name發生改變后,UI也響應了這一變化)
至此,基本實現了最簡單的數據與UI綁定。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
從上面的栗子,基本可以構建出從數據源到Binding對象到UI元素的Binding模型。
說得更具體些,是實現了INotifyPropertyChanged的普通對象,通過Binding對象,到依賴屬性的關聯。
其中普通對象是Binding的"source",依賴對象的依賴屬性是"target"。這樣的關聯邏輯比較符合我們正常的程序設計的思維,但也並不絕對。
還是用栗子說明:
(UI代碼片段)
<StackPanel> <TextBlock FontWeight="Bold" Margin="5">Student ID:</TextBlock> <TextBox x:Name="txtStudentID" Margin="5" BorderBrush="Black" Text="{Binding SelectedItem.Id, ElementName=lbxStudentList}"/> <TextBlock Text="Student List:" FontWeight="SemiBold" Margin="5"/> <ListBox x:Name="lbxStudentList" Margin="5" Height="150"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Id}"/> <TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Age}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button Content="Change" Margin="5" Click="Button_Click"/> </StackPanel>
(后台代碼片段)
(為ListBox設置Binding源)
stus = new ObservableCollection<Student> { new Student { Id = 0, Name = "Tim", Age = 19 }, new Student { Id = 1, Name = "Tom", Age = 22 }, new Student { Id = 2, Name = "Kyle", Age = 21 }, new Student { Id = 3, Name = "Tony", Age = 19 }, new Student { Id = 4, Name = "Nike", Age = 18 }, new Student { Id = 5, Name = "Nancy", Age = 22 } }; this.lbxStudentList.ItemsSource = stus;
(添加按鈕事件,改變stus集合)
private void Button_Click(object sender, RoutedEventArgs e) { stus.Add(new Student { Id = 6, Name = "Milk", Age = 19 }); }
測試:

(txtStudentID中實時顯示lbxStudentList中選中項的Id值)

(單擊Change按鈕,lbxStudentList實時反映數據的變化)
這里就涉及到了UI元素到UI元素(也就是依賴屬性到依賴屬性)的關聯。
注意到:當依賴屬性作為Binding的source時並不需要實現INotifyPorpertyChanged接口就可以讓Binding對象監測到其發生變化。
初次之外,還注意到:
1、為ItemsControl設置數據源時,只要給ItemSource屬性賦值就好;
2、當使用集合類作為數據源時,一般會使用ObservableCollection<T>,他已經實現了INotifyPorpertyChanged和INotifyCollectionChanged接口;
3、Student類型中的Id字段是int類型,但TextProperty是string類型,這實際上經過了一個類型轉換。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
在實際開發中,數據通常以集合為主,所以ItemsControl經常被使用,這里要單獨說一下。
上一個栗子中我們直接給ItemSource屬性賦值,這好像不太符合之前所述的Binding規則,好像這樣寫才對:
this.lbxStudentList.SetBinding(ListBox.ItemsSourceProperty, new Binding() { Source = stus });
實際上,這樣也是可以的。
這里順帶提一下,上述的Binding是沒有Path的,因為source本身就是數據。
甚至還可以把source也省略。做法就是把source直接丟給目標屬性的對象的DataContext屬性:
this.lbxStudentList.DataContext = stus; this.lbxStudentList.SetBinding(ListBox.ItemsSourceProperty, new Binding());
有的時候,我們會把DataTable直接作為ItemsControl的數據源。直接把DataTable的DefaultView屬性賦給ItemsSource即可。
而且不需要實現INotifyCollectionChanged和INotifyPropertyChanged接口就可以讓關聯目標直接反映數據變化。
this.lbxStudentList.ItemsSource = (new DataTable()).DefaultView;
或:
this.lbxStudentList.DataContext = new DataTable(); this.lbxStudentList.SetBinding(ListBox.ItemsSourceProperty, new Binding());
# 以上整理了一些Binding的常規用法,后續可能會補上Binding的一些其他不太常用的功能,以及轉換器和校驗器的用法。
(完)
