WPF中的列表式控件派生自ItemsControl类,自然也就继承了ItemsSource这个属性。ItemsSource属性可以接受一个IEnumerable接口派生类的实例作为自己的值。
每个ItemsControl的派生类都具有自己对应的条目容器。例如ListBox的条目容器是ListBoxItem,ComboBox的条目容器时ComboBoxItem。ItemsSource里存放的是一条一条数据,要想把数据显示出来需要为它们穿上“外衣”,条目容器就起到了数据外衣的作用。怎样让条目容器与它对应的数据条目关联起来呢?当然是依靠Binding!
UI代码如下:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding Source" Height="240" Width="300">
<StackPanel x:Name="stackPanel" Background="LightBlue">
<TextBlock Text="StudentID:" FontWeight="Bold" Margin="5"/>
<TextBox x:Name="textBoxId" Margin="5"/>
<TextBlock Text="Student List:" FontWeight="Bold" Margin="5"/>
<ListBox x:Name="listBoxStudents" Height="110" Margin="5"/>
</StackPanel>
</Window>
我们要实现的效果是把一个List<Student>集合的实例作为ListBox的ItemsSource,让ListBox显示Student的Name并使用TextBox显示ListBox当前选中条目的Id。逻辑层代码:
public MainWindow()
{
InitializeComponent();
List<Student> stuList = new List<Student>()
{
new Student(){Id=0,Name="Tim",Age=29},
new Student(){Id=1,Name="Tom",Age=28},
new Student(){Id=2,Name="Kyle",Age=27},
new Student(){Id=3,Name="Tony",Age=26},
new Student(){Id=4,Name="Vina",Age=25},
new Student(){Id=5,Name="Mike",Age=24},
};
this.listBoxStudents.ItemsSource = stuList;
Binding binding = new Binding("SelectedItem.Id") { Source = this.listBoxStudents };
this.textBoxId.SetBinding(TextBox.TextProperty, binding);
}
当DisplayMemberPath属性被设置后,ListBox在获得ItemsSource的时候就会创建等量的ListBoxItem并以DisplayMemberPath属性值为Path创建Binding,Binding的目标是ListBoxItem的内容插件。
事实上在为ListBox设置Binding时,隐式生成一个DataTemplate类型(就仿佛给数据穿上了一件衣服一样)。至于什么是DataTemplate讲会放到Template相关章节去讨论。
最后,我们看一个为数据显示设置DataTemplate的例子。先删除C#中的this.listBoxStudents.DisplayMemberPath = "Name";再在XAML中添加几行代码:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Binding Source" Height="240" Width="300">
<StackPanel x:Name="stackPanel" Background="LightBlue">
<TextBlock Text="StudentID:" FontWeight="Bold" Margin="5"/>
<TextBox x:Name="textBoxId" Margin="5"/>
<TextBlock Text="Student List:" FontWeight="Bold" Margin="5"/>
<ListBox x:Name="listBoxStudents" Height="120" Margin="5">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=Id}" Width="30"/>
<TextBox Text="{Binding Path=Name}" Width="60"/>
<TextBox Text="{Binding Path=Age}" Width="30"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
最后效果:
最后特别提醒一点:在使用集合类型作为列表控件的ItemsSource时一般会考虑使用ObservableCollection<T>代替List<T>,因为OvservableCollection<T>类实现了INotifyCollectionChanged和INotifyPropertyChanged接口,能把集合的变化立刻通知显示它的列表控件,改变会立刻显现出来。