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

2

4

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

Selfreliant answered 26/12, 2013 at 11:39 Comment(2)
As per your requirement i think DataGrid is much better option to use here. You can set IsReadOnly to True if you don't want editing feature.Quenchless
I don't think (but I may be wrong) I can customise the UI of a DataGrid in the way I can a ListViewSelfreliant
S
7

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.

Selfreliant answered 29/12, 2013 at 8:32 Comment(2)
You got a upvote because i have been looking for a example for skinning the ListView for a long time and actually the most people use ListViewItem skin only!Songstress
Thank you for this. We are creating a new dark theme for our product, and I kept seeing a couple white lines appear in ListViews that use GridView when both ScrollBars were visible. For some reason, there's an extra DockPanel with a couple Rectangles in it specifically on the ListView.GridView.ScrollViewer.Gravitative
S
1

Use the GroupStyle property of ListView as follows.

<ListView.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding GroupHeader}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
</ListView.GroupStyle>

For more details check the following link.

http://www.wpf-tutorial.com/listview-control/listview-grouping/

Survive answered 26/12, 2013 at 11:58 Comment(1)
Hi. Thanks. I t doesn't seem to work, I don't have headers and ListView loading has become much slower. I looked at the link - I don't need row grouping just column headers. Am I doing it wrong?Selfreliant

© 2022 - 2024 — McMap. All rights reserved.