Montag, 7. Februar 2011

Set Styles of ListViewItem or TreeViewItem with ItemTemplate

I just had to modify the appearance of Items in a ListView and TreeView which are generated during runtime using ItemsControl.ItemsSource and ItemsControl.ItemTemplate like this:

  <ListView Name="listview">
    <ListView.ItemTemplate>
      <DataTemplate>
        <TextBlock Text="{Binding}"/>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>

  List<string> lst = new List<string>
  {
    "item1",
    "item2",
    "item3"
  };
  this.listview.ItemsSource = lst;

Goal is to change the look of the items which are selected (the look is okay if the ItemsControl is focused, but if not the gray color is a bit odd). When dealing with ItemsControls you really should take a look into the blog by Dr. WPF.

But let's take a look into the basic Style of a ListViewItem:

    <Style x:Key="ListViewItemStyle1" TargetType="{x:Type ListViewItem}">
      <Setter Property="Background" Value="Transparent"/>
      <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
      <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
      <Setter Property="Padding" Value="2,0,0,0"/>
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ListViewItem}">
            <Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
              <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsSelected" Value="true">
                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
              </Trigger>
              <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="IsSelected" Value="true"/>
                  <Condition Property="Selector.IsSelectionActive" Value="false"/>
                </MultiTrigger.Conditions>
                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
              </MultiTrigger>
              <Trigger Property="IsEnabled" Value="false">
                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
The marked lines set the apperaence when TreeViewItem.IsSelected is true.

Now the Style has to be assigned to each generated item. I'll do this by waiting for the ItemsControl.ItemContainerGenerator to finish generation of the container for each item (refer to Dr. WPF, he really get's the point):


Set the event, e.g. in Window.Initialized:

  private void Window_Initialized(object sender, EventArgs e)
  {
    this.listview.ItemContainerGenerator.StatusChanged
      += new EventHandler(ItemContainerGenerator_StatusChanged);
  }

And (after generation) take each container and assign the Style:

  void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
  {
    if (this.listview.ItemContainerGenerator.Status
      == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
      foreach (var item in this.listview.Items)
      {
        DependencyObject dependencyobject
          = this.listview.ItemContainerGenerator.ContainerFromItem(item);
        if (dependencyobject as ListViewItem != null
          && (dependencyobject as ListViewItem).Style == null)
          (dependencyobject as ListViewItem).Style
            = (Style)this.FindResource("ListViewItemStyle1");
      }
  }


That's it. If there's a better and/or more effective way please let me know :-)

Keine Kommentare:

Kommentar veröffentlichen