WPF 学习笔记 - 10. Binding (3)

来源:互联网 发布:ubuntu 多网卡配置 编辑:程序博客网 时间:2024/06/11 07:38

6. 数据模板

数据模板为展示数据提供了极大的灵活性,我们继续以前面的例子来看看它的能力。

<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}">

        <ListBox.ItemTemplate>
          <DataTemplate>
            <StackPanel Orientation="Horizontal">
              <TextBlock Text="{Binding Path=Name}" />
              <TextBlock>, </TextBlock>
              <TextBlock Text="{Binding Path=Age}" />
              <TextBlock>,</TextBlock>
              <TextBlock Text="{Binding Path=Sex}" />
            </StackPanel>
          </DataTemplate>
        </ListBox.ItemTemplate>

      </ListBox>
    </StackPanel>
  </Grid>
</Window>

uploads/200811/11_155607_1.png


很显然,利用 ListBox.ItemTemplate.DataTemplate 我们可以用更复杂和更丰富的手段显示数据源对象,不再仅仅是通过 Path 显示某单个属性。不过通常情况下,我们会像 HTML/CSS 那样将数据模板定义到资源中,以便在多个地方重复使用。
<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 x:Key="myData">
      <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}" 
        ItemTemplate="{StaticResource myData}">

      </ListBox>
    </StackPanel>
  </Grid>
</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 似乎很麻烦,现在只需做些简单的设置即可。
<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}">
      <Border x:Name="border1" BorderBrush="Red" BorderThickness="1" Padding="5" Margin="5">
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Name}" />
          <TextBlock>,</TextBlock>
          <TextBlock Text="{Binding Path=Age}" />
          <TextBlock>,</TextBlock>
          <TextBlock Text="{Binding Path=Sex}" />
        </StackPanel>
      </Border>
      
      <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=Sex}">
          <DataTrigger.Value>Male</DataTrigger.Value>
          <Setter TargetName="border1" Property="BorderBrush" Value="Blue" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>

  </Window.Resources>

  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}">
      </ListBox>
    </StackPanel>
  </Grid>
</Window>

首先,我们在数据模板中增加了一个 Border,默认颜色是红色。然后利用触发器,当 Personal.Sex == Male 时,将边框颜色设置为蓝色。

uploads/200811/11_155612_2.png


7. 值转换器

某些时候,我们需要对绑定的源值进行类型或者显示格式转换,那么可以采用值转换器达到这个目的。比如我们可以将上面的 Sex 转换成 "男"、"女" 来显示。

Window1.xaml.cs
public class SexConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return value.ToString() == "Male" ? "男" : "女";
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

转换器很简单,只需实现 IValueConverter 接口即可。

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>

    <my:SexConverter x:Key="sexConverter" />

    <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, Converter={StaticResource sexConverter}}" />
      </StackPanel>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}">
      </ListBox>
    </StackPanel>
  </Grid>
</Window>

首先在资源中创建一个转换器实例,然后在数据模板中使用 Binding.Converter 来指定转换器实例即可。看看最终效果。

uploads/200811/11_155617_3.png


Convert() 有个有趣的的返回结果 "Binding.DoNothing",它的意思是 "暂时取消该绑定"。
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
  //return value.ToString() == "Male" ? "男" : "女";
  return Binding.DoNothing;
}

注意,DoNothing 和 null 并不是一回事,null 是个有效返回值。

uploads/200811/11_155622_4.png


接下来,我们试着将 Sex 转换成 Brush 类型,以便显示不同的颜色。

Window1.xaml.cs
public class SexToBrushConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return value.ToString() == "Male" ? Brushes.Blue : Brushes.Red;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

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" Height="276" Width="360" WindowStartupLocation="CenterScreen">
  <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>

    <my:SexToBrushConverter x:Key="sexToBrushConverter" />

    <DataTemplate DataType="{x:Type my:Personal}">
      <Border x:Name="border1" 
          BorderBrush="{Binding Path=Sex, Converter={StaticResource sexToBrushConverter}}" 
          BorderThickness="1" Padding="5" Margin="5">

        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Name}" />
          <TextBlock>,</TextBlock>
          <TextBlock Text="{Binding Path=Age}" />
          <TextBlock>,</TextBlock>
          <TextBlock Text="{Binding Path=Sex}" />
        </StackPanel>

      </Border>
    </DataTemplate>
  </Window.Resources>
  <Grid>
    <StackPanel DataContext="{StaticResource personals}">
      <ListBox x:Name="listbox1" ItemsSource="{Binding}">
      </ListBox>
    </StackPanel>
  </Grid>
</Window>

注意 Border.BorderBrush 属性中的转换器用法,结果表明转换器达到了上面例子中数据模板触发器同样的效果。

uploads/200811/11_155612_2.png

原创粉丝点击