WPF 學習筆記 - 10. Binding (3)

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章