WPF 学习笔记《7》——Binding《2》

4. 绑定到集合


在实际开发中,我们通常是将一个集合数据对象 (比如数据表) 绑定到一个 DataGrid 或者 ListBox 列表控件上,这时候我们就需要使用到集合绑定方式。WPF 特意为我们实现了一个 System.Collections.ObjectModel.ObservableCollection<T> 泛型集合,省却了我们写具备变更通知功能集合代码的时间。


Window1.xaml

<Window x:Class="Learn.WPF.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
Title
="Window1">
<Grid>
<StackPanel>
<ListBox x:Name="listbox1"></ListBox>
</StackPanel>
</Grid>
</Window>


Window1.xaml.cs

public class Personal
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
}

public class PersonalList : ObservableCollection<Personal>
{
}

public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();

var list
= new PersonalList
{
new Personal { Name = "Tom", Age = 10, Sex = "Male" },
new Personal { Name = "Mary", Age = 15, Sex = "Female" },
new Personal { Name = "Jack", Age = 12, Sex = "Male" },
};

var binding
= new Binding{ Source = list };
this.listbox1.SetBinding(ListBox.ItemsSourceProperty, binding);
this.listbox1.DisplayMemberPath = "Name";
}
}


注意使用 DisplayMemberPath 属性指定 ListBoxItem 的内容,否则会调用 ToString() 来显示结果。

当然,我们也可以直接绑定逻辑资源中的列表数据。

Window1.xaml

<Window x:Class="Learn.WPF.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my
="clr-namespace:Learn.WPF"
Title
="Window1">
<Window.Resources>
<my:PersonalList x:Key="personals" >
<my:Personal Name="Tom" Age="10" Sex="Male" />
<my:Personal Name="Mary" Age="15" Sex="Female" />
<my:Personal Name="Jack" Age="12" Sex="Male" />
</my:PersonalList>
</Window.Resources>
<Grid>
<StackPanel>
<ListBox x:Name="listbox2"
ItemsSource
="{Binding Source={StaticResource personals}}"
DisplayMemberPath
="Name">
</ListBox>
</StackPanel>
</Grid>
</Window>


为了使用 Personal 和 PersonalList,我们引入了一个 CLR Namespace。

在主从结构 (Master-Detail) 显示中,我们通常要实现 "选择项跟踪" 功能,也就是说当我们选中主表的某个记录时,其他细节控件要同步刷新该记录的细节内容。

<Window x:Class="Learn.WPF.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my
="clr-namespace:Learn.WPF"
Title
="Window1">
<Window.Resources>
<my:PersonalList x:Key="personals" >
<my:Personal Name="Tom" Age="10" Sex="Male" />
<my:Personal Name="Mary" Age="15" Sex="Female" />
<my:Personal Name="Jack" Age="12" Sex="Male" />
</my:PersonalList>
</Window.Resources>
<Grid>
<StackPanel>
<ListBox x:Name="listbox1"
ItemsSource
="{Binding Source={StaticResource personals}}"
DisplayMemberPath
="Name"
IsSynchronizedWithCurrentItem
="True">
</ListBox>

<Label x:Name="lblName" Content="{Binding Source={StaticResource personals}, Path=Name}" />
<Label x:Name="lblAge" Content="{Binding Source={StaticResource personals}, Path=Age}"/>
<Label x:Name="lblSex" Content="{Binding Source={StaticResource personals}, Path=Sex}"/>
</StackPanel>
</Grid>
</Window>


我们将 ListBox.IsSynchronizedWithCurrentItem 置为 True,这样当我们改变 ListBox 选择项时,下面三个标签会自动同步变更为被选中记录的信息。



当然,我们还可以使用多个 ListBox,就像真正的主从表显示那样相互影响。

<Window x:Class="Learn.WPF.Window1"
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my
="clr-namespace:Learn.WPF"
Title
="Window1">
<Window.Resources>
<my:PersonalList x:Key="personals" >
<my:Personal Name="Tom" Age="10" Sex="Male" />
<my:Personal Name="Mary" Age="15" Sex="Female" />
<my:Personal Name="Jack" Age="12" Sex="Male" />
</my:PersonalList>
</Window.Resources>
<Grid>
<StackPanel>
<ListBox x:Name="listbox1"
ItemsSource
="{Binding Source={StaticResource personals}}"
DisplayMemberPath
="Name"
IsSynchronizedWithCurrentItem
="True">
</ListBox>

<ListBox x:Name="listbox2"
ItemsSource
="{Binding Source={StaticResource personals}}"
DisplayMemberPath
="Age"
IsSynchronizedWithCurrentItem
="True">
</ListBox>
</StackPanel>
</Grid>
</Window>

 


有一点需要说明,IsSynchronizedWithCurrentItem 不支持多项选择同步。

5. DataContext 共享源

在上面的例子中,我们需要将同一资源绑定到多个 UI 元素上,很显然到处写 "{Binding Source={StaticResource personals}}" 是件很繁琐且不利于修改的做法。WPF 提供了一个称之为 "数据上下文 (DataContext)" 的东西让我们可以在多个元素上共享一个源对象,只需将其放到父元素 DataContext 属性即可。

<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:my="clr-namespace:Learn.WPF"
  Title="Window1">
  <Window.Resources>
    <my:PersonalList x:Key="personals" >
      <my:Personal Name="Tom" Age="10" Sex="Male" />
      <my:Personal Name="Mary" Age="15" Sex="Female" />
      <my:Personal Name="Jack" Age="12" Sex="Male" />
    </my:PersonalList>
  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" 
        ItemsSource="{Binding}" 
        DisplayMemberPath="Name" 
        IsSynchronizedWithCurrentItem="True">
      </ListBox>
      
      <Label x:Name="lblName" Content="{Binding Path=Name}" />
      <Label x:Name="lblAge" Content="{Binding Path=Age}"/>
      <Label x:Name="lblSex" Content="{Binding Path=Sex}"/>
    </StackPanel>
  </Grid>
</Window>


当我们不给 Binding 扩展标志指定 Source 属性时,它会自动寻找上级父元素的数据上下文。

当然,我们也可以在代码中做同样的事情。

Window1.xaml

<Window x:Class="Learn.WPF.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:my="clr-namespace:Learn.WPF"
  Title="Window1">
  <Grid>
    <StackPanel x:Name="stackPanel1">
      <ListBox x:Name="listbox1" 
        ItemsSource="{Binding}" 
        DisplayMemberPath="Name" 
        IsSynchronizedWithCurrentItem="True">
      </ListBox>
      
      <Label x:Name="lblName" Content="{Binding Path=Name}" />
      <Label x:Name="lblAge" Content="{Binding Path=Age}"/>
      <Label x:Name="lblSex" Content="{Binding Path=Sex}"/>
    </StackPanel>
  </Grid>
</Window>


Window1.xaml.cs

 1 public partial class Window1 : Window
2 {
3 public Window1()
4 {
5 InitializeComponent();
6
7 var list = new PersonalList {
8 new Personal{Name="Tom", Age=10, Sex="Male"},
9 new Personal{Name="Mary", Age=15, Sex="Female"},
10 new Personal{Name="Jack", Age=12, Sex="Male"},
11 };
12
13 this.stackPanel1.DataContext = list;
14 }
15 }


从上面的例子中,我们可以看出使用 DataContext 使得数据和 UI 分离更加灵活,因为 DataContext 可以跨越多级父元素。比如我们可以直接将数据源设置为 Window.DataContext。

public partial class Window1 : Window
{
  public Window1()
  {
    ... ...
    this.DataContext = list;
  }
}
posted @ 2011-08-22 16:26  徐文峰  阅读(346)  评论(0)    收藏  举报