WPF學習筆記:Binding基礎

我們知道WPF最重要的一個特性是數據驅動UI,Binding就是實現這個特性的橋樑,這個類把數據和界面控件關聯起來。而且它還支持雙向通信。當數據改變時,界面顯示會自動改變;當界面內容改變時,後臺的數據也會自動改變。當然這個雙向通信是可以設置的。

1、Binding基礎

binding源和目標。剛纔說binding是一座橋樑,那源和目標就是橋樑的兩端。一般來說,源就是數據,目標就是UI,但也可以反過來,也可以兩端都是UI控件。下面看一個最簡單的例子:

public class Student:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public int ID { get; set; }

        private string name;
        public string Name
        {
            get 
            {
                return name;
            }
            set
            {
                name = value;
                if(PropertyChanged!=null)
                {
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));
                }
            }
        }

        public int Age { get; set; }
    }

 我們定義了一個學生類,繼承接口INotifyPropertyChanged,這個接口只定義了一個屬性值改變的事件。

下面是界面代碼:

<Window x:Class="BindingResearch.Window4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingResearch"
        mc:Ignorable="d"
        Title="Window4" Height="450" Width="800">
    <StackPanel>
        <TextBox x:Name="txtName" Margin="10"/>
        <Button x:Name="btn1" Height="50" Content="OK" Click="btn1_Click"/>
    </StackPanel>
</Window>

下面我們在界面的構造函數中設置binding:

Student student = new Student();
        public Window4()
        {
            InitializeComponent();

            Binding binding = new Binding("Name") { Source = student };
            txtName.SetBinding(TextBox.TextProperty, binding);
        }

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            student.Name += "Name";
        }

 Binding binding = new Binding("Name") { Source = student };這條語句的含義就是:把binding的源設置爲student對象實例,Name爲binding的訪問路徑,意思就是說這個binding關注的是student對象的Name屬性。

txtName.SetBinding(TextBox.TextProperty, binding);這條語句把binding對象和界面控件關聯起來,意思是把txtName文本框的TextProperty和binding對象關聯起來,因爲binding對象已經關聯了student對象了Name屬性,那麼這兩條語句的意思就是:txtName文本框關聯了student對象的Name屬性值。

那文本框怎麼知道Name的屬性值變了呢,答案就是Name屬性的set代碼塊中。PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Name"));這條語句觸發了Name屬性的值改變事件,因爲binding對象關聯了Name屬性值,所以當binding對象接受到Name屬性值改變後,就把Name屬性值賦值給了txtName文本框。

當我們點擊按鈕後,文本框就會顯示當前Name屬性的值。

2、Binding的路徑(Path)

path即對象的屬性,關於path有很多種用法,下面我們逐一講解:

1、索引器。看下面的例子:

<Window x:Class="BindingResearch.Window4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingResearch"
        mc:Ignorable="d"
        Title="Window4" Height="450" Width="800">
    <StackPanel>
        <TextBox x:Name="txtName" Margin="10" />
        <TextBox x:Name="txtLength" Text="{Binding Path=Text.[1], ElementName=txtName,Mode=OneWay}" Margin="10" />
    </StackPanel>
</Window>

我們把txtLength文本框的文本通過binding設置爲txtName文本框文本的第二個字符,運行效果如下:

等效的c#代碼:

Binding binding1 = new Binding("Text.[1]") { Source = txtName,Mode= BindingMode.OneWay };
txtLength.SetBinding(TextBox.TextProperty, binding1);

2、使用集合或者DataView作爲Binding源時,如果我們想把它的默認元素當作path使用,則語法如下:

List<string> stringList = new List<string>() { "Tim", "Tom", "Blog" };
            txt1.SetBinding(TextBox.TextProperty, new Binding("/") { Source = stringList });
            txt2.SetBinding(TextBox.TextProperty, new Binding("/Length") { Source = stringList, Mode = BindingMode.OneWay });
            txt3.SetBinding(TextBox.TextProperty, new Binding("/[1]") { Source = stringList, Mode = BindingMode.OneWay });

/斜槓即代表默認元素,集合的默認元素即第一個元素。

3、如果集合是嵌套的,我們想把子級集合中的元素作爲path,則語法如下:

City c1 = new City() { Name = "黃岡" };
            City c2 = new City() { Name = "武漢" };

            Province p1 = new Province()
            {
                Name = "湖北",
                CityList = new List<City>() { c1, c2 }
            };

            City c3 = new City() { Name = "廣州" };
            City c4 = new City() { Name = "深圳" };
            Province p2 = new Province()
            {
                Name = "廣東",
                CityList = new List<City>() { c3, c4 }
            };

            Country country1 = new Country()
            {
                Name = "中國",
                ProvinceList = new List<Province>() { p1, p2 }
            };

            List<Country> countries = new List<Country>();
            countries.Add(country1);

            txt1.SetBinding(TextBox.TextProperty, new Binding("/Name") { Source= countries });
            txt2.SetBinding(TextBox.TextProperty, new Binding("/ProvinceList.[1].Name") { Source = countries });
            txt3.SetBinding(TextBox.TextProperty, new Binding("/ProvinceList/Name") { Source = countries });
            txt4.SetBinding(TextBox.TextProperty, new Binding("/ProvinceList/CityList/Name") { Source = countries });

 運行效果如下:

 4、沒有指定path的binding。如果path的值爲"."或者直接沒有指定path,則表明binding的源本身就是數據,比如:string,int類型的數據。看下面的例子:

<Window x:Class="BindingResearch.Window7"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingResearch"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="Window7" Height="450" Width="800">
    <Window.Resources>
        <sys:String x:Key="myString">菩提本無樹,明鏡亦非臺。本來無一物,何處惹塵埃。</sys:String>
    </Window.Resources>
    <StackPanel>
        <TextBlock  x:Name="lable1" Margin="10" Text="{Binding Path=., Source={StaticResource ResourceKey=myString}}"/>
    </StackPanel>
</Window>

 <TextBlock  x:Name="lable1" Margin="10" Text="{Binding Path=., Source={StaticResource ResourceKey=myString}}"/>

我們看到path=.,或者直接把path=.去掉都可以。

這條語句等效的c#代碼:

string s = "菩提本無樹,明鏡亦非臺。本來無一物,何處惹塵埃。";
lable1.SetBinding(TextBlock.TextProperty, new Binding(".") { Source = s });

 這裏的"."也可以省略

 3、沒有Source的Binding

DataContext屬性被定義在FrameworkElement類裏,這個類是WPF控件的基類,這意味着所有的WPF控件都具備這個屬性。我們知道WPF的UI佈局是樹形結構的,這棵樹的每個節點都是控件,由此我們推出一個結論:在UI元素樹每個節點都有DataContext。這一點很重要,因爲當一個Binding只知道自己的path而不知道自己的source時,它就會沿着UI元素樹一路向樹的根部找過去,每路過一個節點都要看看這個節點的DataContext是否具備path指定的屬性。如果有,那就把這個對象作爲自己的source;如果沒有,那就繼續找下去;如果到了樹的根部還沒有找到,那這個binding就沒有source,因此也不會得到數據。

 我們看一個例子:

先定義一個Person類:

class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

 在xaml裏面定義DataContext和Binding:

<Window x:Class="BindingResearch.Window8"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingResearch"
        mc:Ignorable="d"
        Title="Window8" Height="450" Width="800">
    <Window.DataContext>
        <local:Person ID="1002" Name="李四" Age="40"></local:Person>
    </Window.DataContext>
    <StackPanel>
        <TextBox x:Name="txt1" Text="{Binding Path=ID}" Margin="10"/>
        <TextBox x:Name="txt2" Text="{Binding Path=Name}" Margin="10"/>
        <TextBox x:Name="txt3" Text="{Binding Path=Age}" Margin="10"/>
    </StackPanel>
</Window>

 我們把DataContext定義在Window標籤內,給TextBox設置沒有Source,只有Path的Binding。運行效果如下:

 4、即沒有指定Source也沒有指定Path的Binding

 學習完前面的知識,理解這個其實很容易,我們看一個例子就明白了:

<Window x:Class="BindingResearch.Window9"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BindingResearch"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="Window9" Height="450" Width="800">
    <Window.DataContext>
        <sys:String>Hello DataContext!</sys:String>
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Mode=OneWay}" Margin="10"/>
    </StackPanel>
</Window>

 

 

 

 

發佈了38 篇原創文章 · 獲贊 8 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章