wpflistviewdatatemplate

Custom WPF ListView with Style (using DataTemplate) - how do I add headers?


I have the following code

DataTemplate for rows

<!-- Template for each item in ListView -->
    <DataTemplate x:Key="ItemTemplate">
        <Grid>                                
            <Grid.ColumnDefinitions>                    
                <ColumnDefinition Width="25"/>
                <ColumnDefinition Width="90"/>
                <ColumnDefinition Width="325"/>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type l:ItemsView}}, Path=ParentColumnWidth}"/>
                <ColumnDefinition Width="100"/>
                <ColumnDefinition Width="100"/>                    
            </Grid.ColumnDefinitions>
            <CheckBox Grid.Column="0" x:Name="Statement" IsChecked="{Binding Path=statement}" Foreground="{StaticResource CustomWhite}" VerticalAlignment="Center" Style="{StaticResource SelectionCheckBox}"/>
            <TextBlock Grid.Column="1" Text="{Binding Path=idate, StringFormat=d MMM yy}" FontSize="15" Foreground="{StaticResource CustomWhite}"/>
            <TextBlock Grid.Column="2" Text="{Binding Path=fullcomment}" FontSize="15" Foreground="{StaticResource CustomWhite}"/>
            <TextBlock Grid.Column="3" Text="{Binding Path=amount, StringFormat={}{0:N2}}" FontSize="15" Foreground="{Binding Converter={StaticResource GetColourConverterItemAmount}}" TextAlignment="Right" Padding="0,0,25,0"/>
            <TextBlock Grid.Column="4" Text="{Binding Path=acc}"  FontSize="15" Foreground="{StaticResource CustomWhite}"/>
            <TextBlock Grid.Column="5" Text="{Binding Path=source}" FontSize="15" Foreground="{StaticResource CustomWhite}"/>
            <TextBlock Grid.Column="6" Text="{Binding Path=transfer}" FontSize="15" Foreground="{StaticResource CustomWhite}"/>                
        </Grid>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding Path=statement}" Value="{x:Null}">
                <Setter TargetName="Statement" Property="IsEnabled" Value="False"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

Style for ListView - using above DataTemplate

<!-- ListView template -->
    <Style x:Key="HistoryListView" TargetType="{x:Type ListView}">
        <Setter Property="ItemTemplate" Value="{StaticResource ItemTemplate}"/>
        <Setter Property="Background" Value="{StaticResource CustomBackground}"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="Margin" Value="10,10,10,10"/>
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="Padding" Value="0,0,50,0"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>            
        <Style.Resources>
            <!-- Makes selection stay when focus lost (for context menu)-->
            <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{StaticResource CustomLightHighlightC}"/>
        </Style.Resources>
    </Style>        

    <Style x:Key="HistoryContainerStyle" TargetType="ListViewItem">
        <Setter Property="ContextMenu" Value="{StaticResource ItemMenu}"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    </Style>

And my ListView is defined as

<ListView x:Name="lstHistory" ItemsSource="{Binding Path=Items}" Style="{StaticResource HistoryListView}" MouseDoubleClick="lvShowItem" SelectionChanged="lstSelectionChanged" ItemContainerStyle="{StaticResource HistoryContainerStyle}"/>                

This produces a ListView that is exactly what I want - except I have no headers - and ideally I would like sortable headers. I would also like to 'add' the headers in the Style if possible so I can reuse it in other modules.

Any help appreciated Thanks Andy


Solution

  • Found the answer.

    Define ListView and associated elements:

        <Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="ScrollViewer">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ScrollViewer">
                        <Grid Background="{TemplateBinding Background}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <DockPanel Margin="{TemplateBinding Padding}">
                                <ScrollViewer DockPanel.Dock="Top" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" Focusable="False">
                                    <GridViewHeaderRowPresenter Margin="2,0,2,0" Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContainerStyle="{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderTemplate="{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderTemplateSelector="{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}" AllowsColumnReorder="{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderContextMenu="{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}" ColumnHeaderToolTip="{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </ScrollViewer>
                                <ScrollContentPresenter Name="PART_ScrollContentPresenter" KeyboardNavigation.DirectionalNavigation="Local" CanContentScroll="{TemplateBinding CanContentScroll}"/>
                            </DockPanel>
                            <ScrollBar Name="PART_HorizontalScrollBar" Orientation="Horizontal" Grid.Row="1" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Value="{TemplateBinding HorizontalOffset}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
                            <ScrollBar Name="PART_VerticalScrollBar" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Value="{TemplateBinding VerticalOffset}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
        <Style x:Key="GridViewColumnHeaderGripper" TargetType="Thumb">
            <Setter Property="Width" Value="18"/>
            <Setter Property="Background" Value="{StaticResource CustomBackground}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Thumb}">
                        <Border Padding="{TemplateBinding Padding}" Background="Transparent">
                            <Rectangle HorizontalAlignment="Center" Width="1" Fill="{TemplateBinding Background}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    
        <Style x:Key="{x:Type GridViewColumnHeader}" TargetType="GridViewColumnHeader">
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Foreground" Value="{StaticResource CustomWhite}"/>
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="GridViewColumnHeader">
                        <Grid>
                            <Border Name="HeaderBorder" BorderThickness="0,0,0,0" BorderBrush="{StaticResource CustomWhite}" Background="{StaticResource CustomBackground}" Padding="0,0,0,4">
                                <ContentPresenter Name="HeaderContent" Margin="4,0,0,0" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                            </Border>
                            <!-- <Thumb x:Name="PART_HeaderGripper" HorizontalAlignment="Right" Margin="0,0,-9,0" Style="{StaticResource GridViewColumnHeaderGripper}"/> -->
                        </Grid>                        
                    </ControlTemplate>
                </Setter.Value>
            </Setter>            
        </Style>
    
        <!-- ListView template -->
        <Style x:Key="HistoryListView" TargetType="{x:Type ListView}">            
            <Setter Property="View" Value="{StaticResource ItemView}"/>
            <Setter Property="Background" Value="{StaticResource CustomBackground}"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="Margin" Value="10,10,10,10"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="Padding" Value="0,0,50,0"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
            <Setter Property="FontSize" Value="15"/>
            
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListView">
                        <Border Name="Border" BorderThickness="0">
                            <ScrollViewer Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
                                <ItemsPresenter/>
                            </ScrollViewer>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsGrouping" Value="True">
                                <Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
                            </Trigger>                            
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
    
            <Style.Resources>
                <!-- Makes selection stay when focus lost (for context menu)-->
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="{StaticResource CustomLightHighlightC}"/>
            </Style.Resources>
        </Style>               
    
        <Style x:Key="HistoryContainerStyle" TargetType="{x:Type ListViewItem}">
            <Setter Property="ContextMenu" Value="{StaticResource ItemMenu}"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListViewItem}">
                        <Border BorderBrush="Transparent" BorderThickness="0" Background="{TemplateBinding Background}">
                            <GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="0" Content="{TemplateBinding Content}"/>
                        </Border>                        
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Background" Value="{StaticResource CustomLightHighlight}"/>                                
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>    
    

    And define template as a GridView:

            <!-- Template for each item in ListView -->
        <GridView x:Key="ItemView">
            <GridViewColumn Header="" Width="25">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox x:Name="Statement" IsChecked="{Binding Path=statement}" Foreground="{StaticResource CustomWhite}" VerticalAlignment="Center" Style="{StaticResource SelectionCheckBox}"/>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding Path=statement}" Value="{x:Null}">
                                <Setter TargetName="Statement" Property="IsEnabled" Value="False"/>
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Date" Width="90">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=idate, StringFormat=d MMM yy}" Foreground="{StaticResource CustomWhite}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Comment" Width="325">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=fullcomment}" Foreground="{StaticResource CustomWhite}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Amount" Width="100">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=amount, StringFormat={}{0:N2}}" Foreground="{Binding Converter={StaticResource GetColourConverterItemAmount}}" TextAlignment="Right" Padding="0,0,25,0"/>
                    </DataTemplate>                    
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Account" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type l:ItemsView}}, Path=ParentColumnWidth}">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=acc}" Foreground="{StaticResource CustomWhite}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Source" Width="100">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=source}" Foreground="{StaticResource CustomWhite}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Transfer" Width="100">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=transfer}" Foreground="{StaticResource CustomWhite}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    

    Still working on sorting. Will update.