簡述WPF中的Binding


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的一些其他不太常用的功能,以及轉換器和校驗器的用法。

 

(完)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM