Стили представления

83

Для того чтобы представление TileView работало так, как надо, WPF должна быть способна отыскивать подлежащие применению стили. Гарантировать, что стили будут доступны автоматически, можно, создав словарь ресурсов с именем generic.xaml. Этот словарь ресурсов следует разместить в подпапке проекта под названием Themes.

WPF использует файл generic.xaml для извлечения ассоциируемых с классом стилей по умолчанию. В данном примере в файле generic.xaml содержатся определения стилей, ассоциируемых с классом TileView. Чтобы установить ассоциацию между стилями и TileView, нужно присвоить каждому из этих стилей правильный ключ в словаре ресурсов generic.xaml. Вместо обычного строкового ключа WPF ожидает объект ComponentResourceKey, который должен соответствовать информации, возвращаемой свойствами DefaultStyleKey и ItemContainerDefaultStyleKey класса TileView.

Ниже показана базовая структура словаря ресурсов generic.xaml с правильными ключами:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DataBinding">
       
       <Style x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:TileView},ResourceId=TileViewItem}"
         TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
    <Setter Property="Padding" Value="3"/>
    <!--    <Setter Property="Margin" Value="5"/>-->
    <Setter Property="HorizontalContentAlignment" Value="Center"></Setter>
    <Setter Property="ContentTemplate" Value="{Binding Path=View.ItemTemplate,
            RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>

    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ListBoxItem}">
          <Border
           Name="Border" BorderThickness="1" CornerRadius="3" >
            <ContentPresenter />
          </Border>
          <ControlTemplate.Triggers>            
            <Trigger Property="IsSelected" Value="True">
              <Setter TargetName="Border" Property="BorderBrush"
               Value="{Binding Path=View.SelectedBorderBrush,
            RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>
              <Setter TargetName="Border" Property="Background"
              Value="{Binding Path=View.SelectedBackground,
            RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"></Setter>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>

  </Style>
    
</ResourceDictionary>

Нетрудно заметить, что ключ каждого стиля устанавливается так, чтобы он соответствовал информации, предоставляемой классом TileView. Вдобавок в стилях также устанавливаются свойства TargetType (для указания, какой элемент должен изменять данный стиль) и BasedOn (для наследования базовых параметров от более фундаментальных стилей, используемых с ListBox и ListBoxItem). Это экономит часть усилий и позволяет сфокусироваться на расширении этих стилей с помощью специальных параметров.

Поскольку эти два стиля ассоциируются с TileView, именно они и будут применяться для конфигурирования ListView при присваивании свойству View объекта TileView. В случае использования другого объекта представления эти стили будут игнорироваться. Это и есть тот трюк, который заставляет представление ListView работать должным образом, т.е. плавно перестраиваться при каждом изменении значения свойства View.

Стиль TileView, применяющийся к ListView, вносит три перечисленных ниже изменения:

Следует иметь в виду, что для обеспечения максимальной гибкости данный стиль TileView проектируется так, чтобы использовался шаблон данных, предоставляемый разработчиком. Для применения этого шаблона стиль TileView должен сначала извлекать объект TileView (с помощью свойства ListView.View), а затем уже сам шаблон из свойства TileView.ItemTemplate. Этот шаг выполняeтся с помощью выражения привязки, которое производит поиск по дереву элементов (с использованием режима FindAncestor для RelativeSource) до тех пор, пока не обнаружит исходный элемент ListView.

Использование элемента управления ListView

Создав класс представления и вспомогательные стили, можно приступать к их использованию в элементе управления ListView. Чтобы применить специальное представление, нужно просто установить для свойства ListView.View в качестве значения экземпляр объекта представления, как показано ниже:

<ListView Name="lstProducts">
   <ListView.View>
      <TileView ... >
   </ListView.View>
</ListView>

Однако в данном примере демонстрируется элемент управления ListView, способный переключаться между тремя представлениями. Поэтому и экземпляров объектов представления должно быть три. Проще всего с этим справиться можно так: определить каждый объект представления отдельно в коллекции Windows.Resources, а затем загружать нужное представление при выполнении пользователем выбора в элементе управления ComboBox.

Первое представление является довольно простым — оно подразумевает отображение данных в виде множества столбцов с использованием уже знакомого класса GridView, о котором рассказывалось ранее.

Два других представления TileView более интересны. Оба они применяют шаблон для определения того, как должна выглядеть мозаика. Представление ImageView использует элемент StackPanel, который предусматривает размещение рисунка товара над названием. Представление ImageDetailView использует состоящую из двух столбцов сетку. Слева размещается небольшая версия рисунка, а справа — более детальная информация о товаре. Второй столбец помещается в группу общего размера для того, чтобы все элементы имели одинаковую ширину (соответствующую ширине самого большого текстового значения).

Ниже представлена необходимая разметка:

<Window.Resources>

    <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
    
      <local:TileView x:Key="ImageView">
        <local:TileView.ItemTemplate>
          <DataTemplate>
            <StackPanel Width="150" VerticalAlignment="Top">
              <Image Source="{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}"></Image>
              <TextBlock TextWrapping="Wrap" HorizontalAlignment="Center"  Text="{Binding Path=ModelName}" ></TextBlock>
            </StackPanel>
          </DataTemplate>
        </local:TileView.ItemTemplate>
      </local:TileView>

    <local:TileView x:Key="ImageDetailView" SelectedBackground="LightSteelBlue">
      <local:TileView.ItemTemplate>
        <DataTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto"></ColumnDefinition>
              <ColumnDefinition Width="Auto" SharedSizeGroup="Col2"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            
            <Image Margin="5"  Width="100" Source="{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}"></Image>
            <StackPanel Grid.Column="1" VerticalAlignment="Center">
              <TextBlock FontWeight="Bold" Text="{Binding Path=ModelName}"></TextBlock>
              <TextBlock Text="{Binding Path=ModelNumber}"></TextBlock>
                            <TextBlock Text="{Binding Path=UnitCost, StringFormat={}{0:C}}"></TextBlock>
            </StackPanel>
          </Grid>
        </DataTemplate>
      </local:TileView.ItemTemplate>
    </local:TileView>
    
      <GridView x:Key="GridView">
        <GridView.Columns>
          <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=ModelName}" />
          <GridViewColumn Header="Model" DisplayMemberBinding="{Binding Path=ModelNumber}" />
                <GridViewColumn Header="Price" DisplayMemberBinding="{Binding Path=UnitCost, StringFormat={}{0:C}}" />
        </GridView.Columns>
      </GridView>

    
  </Window.Resources>
  
    <Grid Margin="5">
      <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition Height="Auto"></RowDefinition>
      </Grid.RowDefinitions>
      
      <ListView Name="lstProducts" View="{StaticResource GridView}">
      </ListView>
      <Grid Grid.Row="1" Margin="5">
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto"></ColumnDefinition>
          <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Margin="5" VerticalAlignment="Center">Choose your view:</TextBlock>
        <ComboBox Grid.Column="1" Margin="5" Width="150" HorizontalAlignment="Left" 
                  Name="lstView" SelectionChanged="lstView_SelectionChanged">
          <ComboBoxItem>GridView</ComboBoxItem>
          <ComboBoxItem>ImageView</ComboBoxItem>
          <ComboBoxItem>ImageDetailView</ComboBoxItem>
        </ComboBox>
      </Grid>
    </Grid>
Модель представления элемента ListView
Пройди тесты
Лучший чат для C# программистов