1. 程式人生 > >Binding(三)——使用DataContext作為Binding的源

Binding(三)——使用DataContext作為Binding的源

前面的例子都是將CLR型別的物件指定為Binding的Source,使用了兩種方法——把物件賦值個Binding.Source或把物件的Name賦值給Binding.ElementName。

DataContext屬性被定義在FrameworkElement類中,這個類是WPF控制元件的基類,意味著所有WPF空間都具備這個屬性。WPF的UI佈局是樹形結構,每個節點都是空間,由此可以推出另一個結論——UI元素樹的每個結點都有DataContext。

這一點非常重要,因為當一個Binding只知道自己的Path而不知道自己的Source時,它會沿著UI元素樹一路向樹的根部找過去,每路過一個結點就要看看這個結點的DataContext是否具有Path所指定的屬性。如果有,就把這個物件作為自己的Source;如果沒有,那就繼續找,如果到了樹的根部還沒有找到,那這個Binding就沒有Source,因而也不會得到資料。看如下例子。

建立一個Student類,具有Id、Name、Age三個屬性:

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

然後在XAML建立程式的UI。
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="Binding Source" Height="135" Width="300">
    <StackPanel Background="LightBlue">
        <StackPanel.DataContext>
            <local:Student Id="6" Name="Tim" Age="29"/>
        </StackPanel.DataContext>
        <Grid>
            <StackPanel>
                <TextBox Text="{Binding Path=Id}" Margin="5"/>
                <TextBox Text="{Binding Path=Name}" Margin="5"/>
                <TextBox Text="{Binding Path=Age}" Margin="5"/>
            </StackPanel>
        </Grid>
    </StackPanel>
</Window>

UI佈局可用如下的UI樹了表示:


這樣,這3個TextBox的Binding就會自動向UI元素樹的上層去尋找可用的DataContext物件。最終,他們在最外層的StackPanel身上找到了可用的DataContext物件。

這樣,當某個DataContext是一個簡單型別物件的時候,我們可以完全能看到一個“既沒有Path有沒有Source”的Binding。

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="Binding Source" Height="135" Width="300">
    <StackPanel Background="LightBlue">
        <StackPanel.DataContext>
            <sys:String>Hello Tom!</sys:String>
        </StackPanel.DataContext>
        <TextBlock Text="{Binding .}" Margin="5"/>
        <Grid>
            <Grid.DataContext>
                <sys:String>Hello Jerry!</sys:String>
            </Grid.DataContext>
            <StackPanel>
                <TextBlock Text="{Binding .}" Margin="5"/>
            </StackPanel>
        </Grid>
    </StackPanel>
</Window>

執行效果如下:


你可能會想,Binding是怎樣自動向UI元素樹的上層尋找DataContext對並把它作為Source的呢?DataContext是一種依賴屬性,依賴屬性有一個很重要的特點就是當你沒有為控制元件的某個依賴屬性顯示賦值時,控制元件會把自己容器的屬性值“借過來”當作自己的屬性值。因此,“Binding沿著UI元素樹向上找”只是WPF給我們的一個錯覺,實際上是屬性沿著UI元素樹向下傳遞了。

在實際工作中DataContext的用法是十分靈活的。比如:

  1. 當UI的多個控制元件都是用Binding關注同一個物件時,不妨採用DataContext。
  2. 當作為Source的物件不能被直接訪問的時候——比如B窗體內的控制元件想把A窗體內的空間當做自己的Binding源時,但A窗體內的控制元件是private訪問級別,這時候就可以把這個控制元件作為窗體A的DataContext(這個屬性是public訪問級別)從而暴露資料。
  3. DataContext本身是一個依賴屬性,可以使用Binding把它關聯到一個數據源上。