WPF 学习笔记《7》——Binding《3》
6. 数据模板
数据模板为展示数据提供了极大的灵活性,我们继续以前面的例子来看看它的能力。
1 <Window x:Class="Learn.WPF.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:my="clr-namespace:Learn.WPF"
5 Title="Window1">
6 <Window.Resources>
7 <my:PersonalList x:Key="personals" >
8 <my:Personal Name="Tom" Age="10" Sex="Male" />
9 <my:Personal Name="Mary" Age="15" Sex="Female" />
10 <my:Personal Name="Jack" Age="12" Sex="Male" />
11 </my:PersonalList>
12 </Window.Resources>
13 <Grid>
14 <StackPanel DataContext="{StaticResource personals}">
15 <ListBox x:Name="listbox1" ItemsSource="{Binding}">
16
17 <ListBox.ItemTemplate>
18 <DataTemplate>
19 <StackPanel Orientation="Horizontal">
20 <TextBlock Text="{Binding Path=Name}" />
21 <TextBlock>, </TextBlock>
22 <TextBlock Text="{Binding Path=Age}" />
23 <TextBlock>,</TextBlock>
24 <TextBlock Text="{Binding Path=Sex}" />
25 </StackPanel>
26 </DataTemplate>
27 </ListBox.ItemTemplate>
28
29 </ListBox>
30 </StackPanel>
31 </Grid>
32 </Window>

很显然,利用 ListBox.ItemTemplate.DataTemplate 我们可以用更复杂和更丰富的手段显示数据源对象,不再仅仅是通过 Path 显示某单个属性。不过通常情况下,我们会像 HTML/CSS 那样将数据模板定义到资源中,以便在多个地方重复使用。
1 <Window x:Class="Learn.WPF.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:my="clr-namespace:Learn.WPF"
5 Title="Window1">
6 <Window.Resources>
7 <my:PersonalList x:Key="personals" >
8 <my:Personal Name="Tom" Age="10" Sex="Male" />
9 <my:Personal Name="Mary" Age="15" Sex="Female" />
10 <my:Personal Name="Jack" Age="12" Sex="Male" />
11 </my:PersonalList>
12
13 <DataTemplate x:Key="myData">
14 <StackPanel Orientation="Horizontal">
15 <TextBlock Text="{Binding Path=Name}" />
16 <TextBlock>,</TextBlock>
17 <TextBlock Text="{Binding Path=Age}" />
18 <TextBlock>,</TextBlock>
19 <TextBlock Text="{Binding Path=Sex}" />
20 </StackPanel>
21 </DataTemplate>
22
23 </Window.Resources>
24 <Grid>
25 <StackPanel DataContext="{StaticResource personals}">
26 <ListBox x:Name="listbox1" ItemsSource="{Binding}"
27 ItemTemplate="{StaticResource myData}">
28
29 </ListBox>
30 </StackPanel>
31 </Grid>
32 </Window>
当然,我们也可以使用 CSS 那样的选择器来指定模板的应用类型,而不是在 ListBox 上设置 ItemTemplate 属性。
<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>
<DataTemplate DataType="{x:Type my:Personal}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Age}" />
<TextBlock>,</TextBlock>
<TextBlock Text="{Binding Path=Sex}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel DataContext="{StaticResource personals}">
<ListBox x:Name="listbox1" ItemsSource="{Binding}">
</ListBox>
</StackPanel>
</Grid>
</Window>
通过 DataType="{x:Type my:Personal}" 我们就可以让所有使用 Personal 对象的地方自动使用这个模板设置。
利用数据模板,我们还可以做出复杂的效果来,比如根据 Personal.Sex 来显示一个不同颜色的边框。类似的做法在 WinForm 似乎很麻烦,现在只需做些简单的设置即可。
1 <Window x:Class="Learn.WPF.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:my="clr-namespace:Learn.WPF"
5 Title="Window1">
6 <Window.Resources>
7 <my:PersonalList x:Key="personals" >
8 <my:Personal Name="Tom" Age="10" Sex="Male" />
9 <my:Personal Name="Mary" Age="15" Sex="Female" />
10 <my:Personal Name="Jack" Age="12" Sex="Male" />
11 </my:PersonalList>
12
13 <DataTemplate DataType="{x:Type my:Personal}">
14 <Border x:Name="border1" BorderBrush="Red" BorderThickness="1" Padding="5" Margin="5">
15 <StackPanel Orientation="Horizontal">
16 <TextBlock Text="{Binding Path=Name}" />
17 <TextBlock>,</TextBlock>
18 <TextBlock Text="{Binding Path=Age}" />
19 <TextBlock>,</TextBlock>
20 <TextBlock Text="{Binding Path=Sex}" />
21 </StackPanel>
22 </Border>
23
24 <DataTemplate.Triggers>
25 <DataTrigger Binding="{Binding Path=Sex}">
26 <DataTrigger.Value>Male</DataTrigger.Value>
27 <Setter TargetName="border1" Property="BorderBrush" Value="Blue" />
28 </DataTrigger>
29 </DataTemplate.Triggers>
30 </DataTemplate>
31
32 </Window.Resources>
33
34 <Grid>
35 <StackPanel DataContext="{StaticResource personals}">
36 <ListBox x:Name="listbox1" ItemsSource="{Binding}">
37 </ListBox>
38 </StackPanel>
39 </Grid>
40 </Window>
首先,我们在数据模板中增加了一个 Border,默认颜色是红色。然后利用触发器,当 Personal.Sex == Male 时,将边框颜色设置为蓝色。

7. 值转换器
某些时候,我们需要对绑定的源值进行类型或者显示格式转换,那么可以采用值转换器达到这个目的。比如我们可以将上面的 Sex 转换成 "男"、"女" 来显示。
Window1.xaml.cs
1 public class SexConverter : IValueConverter
2 {
3 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
4 {
5 return value.ToString() == "Male" ? "男" : "女";
6 }
7
8 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
9 {
10 throw new NotImplementedException();
11 }
12 }
转换器很简单,只需实现 IValueConverter 接口即可。
Window1.xaml
1 <Window x:Class="Learn.WPF.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:my="clr-namespace:Learn.WPF"
5 Title="Window1">
6 <Window.Resources>
7 <my:PersonalList x:Key="personals" >
8 <my:Personal Name="Tom" Age="10" Sex="Male" />
9 <my:Personal Name="Mary" Age="15" Sex="Female" />
10 <my:Personal Name="Jack" Age="12" Sex="Male" />
11 </my:PersonalList>
12
13 <my:SexConverter x:Key="sexConverter" />
14
15 <DataTemplate DataType="{x:Type my:Personal}">
16 <StackPanel Orientation="Horizontal">
17 <TextBlock Text="{Binding Path=Name}" />
18 <TextBlock>,</TextBlock>
19 <TextBlock Text="{Binding Path=Age}" />
20 <TextBlock>,</TextBlock>
21 <TextBlock Text="{Binding Path=Sex, Converter={StaticResource sexConverter}}" />
22 </StackPanel>
23 </DataTemplate>
24 </Window.Resources>
25 <Grid>
26 <StackPanel DataContext="{StaticResource personals}">
27 <ListBox x:Name="listbox1" ItemsSource="{Binding}">
28 </ListBox>
29 </StackPanel>
30 </Grid>
31 </Window>
首先在资源中创建一个转换器实例,然后在数据模板中使用 Binding.Converter 来指定转换器实例即可。看看最终效果。

Convert() 有个有趣的的返回结果 "Binding.DoNothing",它的意思是 "暂时取消该绑定"。
{
//return value.ToString() == "Male" ? "男" : "女";
return Binding.DoNothing;
}
注意,DoNothing 和 null 并不是一回事,null 是个有效返回值。

接下来,我们试着将 Sex 转换成 Brush 类型,以便显示不同的颜色。
Window1.xaml.cs
1 public class SexToBrushConverter : IValueConverter
2 {
3 public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
4 {
5 return value.ToString() == "Male" ? Brushes.Blue : Brushes.Red;
6 }
7
8 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
9 {
10 throw new NotImplementedException();
11 }
12 }
Window1.xaml
1 <Window x:Class="Learn.WPF.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:my="clr-namespace:Learn.WPF"
5 Title="Window1" Height="276" Width="360" WindowStartupLocation="CenterScreen">
6 <Window.Resources>
7 <my:PersonalList x:Key="personals" >
8 <my:Personal Name="Tom" Age="10" Sex="Male" />
9 <my:Personal Name="Mary" Age="15" Sex="Female" />
10 <my:Personal Name="Jack" Age="12" Sex="Male" />
11 </my:PersonalList>
12
13 <my:SexToBrushConverter x:Key="sexToBrushConverter" />
14
15 <DataTemplate DataType="{x:Type my:Personal}">
16 <Border x:Name="border1"
17 BorderBrush="{Binding Path=Sex, Converter={StaticResource sexToBrushConverter}}"
18 BorderThickness="1" Padding="5" Margin="5">
19
20 <StackPanel Orientation="Horizontal">
21 <TextBlock Text="{Binding Path=Name}" />
22 <TextBlock>,</TextBlock>
23 <TextBlock Text="{Binding Path=Age}" />
24 <TextBlock>,</TextBlock>
25 <TextBlock Text="{Binding Path=Sex}" />
26 </StackPanel>
27
28 </Border>
29 </DataTemplate>
30 </Window.Resources>
31 <Grid>
32 <StackPanel DataContext="{StaticResource personals}">
33 <ListBox x:Name="listbox1" ItemsSource="{Binding}">
34 </ListBox>
35 </StackPanel>
36 </Grid>
37 </Window>
注意 Border.BorderBrush 属性中的转换器用法,结果表明转换器达到了上面例子中数据模板触发器同样的效果。


浙公网安备 33010602011771号