7

I need to keep the HeaderTemplate of a ListView visible at all times, but I don't know what to set, or what part of the ListView's template to change to do that.

What I currently have causes the header of the ListView to scroll off the top when scrolling down through items.

How can I keep the header "row" of the ListView visible even when scrolling through the ListView's items??

Here's my XAML:

<ListView x:Name="permitResults"
          Grid.Row="1"
          AutomationProperties.AutomationId="PermitResults"
          AutomationProperties.Name="Permit Search Results"
          ItemsSource="{Binding Source={StaticResource ResultsSource}}" 
          ItemClick="permitResults_ItemClick"
          SelectionMode="None"
          TabIndex="1"
          Padding="0"
          Margin="0"
          BorderThickness="0"
          IsSwipeEnabled="True"
          IsItemClickEnabled="True"
          ScrollViewer.VerticalScrollBarVisibility="Auto" >
    <ListView.HeaderTemplate>
        <DataTemplate>
            <Grid Margin="0,0,0,0" Width="1366" Height="Auto" HorizontalAlignment="Left">
                <Grid.Resources>
                        <Style TargetType="TextBlock" BasedOn="{StaticResource SearchGridResultsHeaderTextBlock}">
                            <Setter Property="HorizontalAlignment" Value="Left"></Setter>
                        </Style>
                    </Grid.Resources>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="2*"/>
                        <ColumnDefinition Width="2*"/>
                        <ColumnDefinition Width="3*"/>
                        <ColumnDefinition Width="2*"/>
                        <ColumnDefinition Width="6*"/>
                        <ColumnDefinition Width="2*"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" Text="Permit #" MaxLines="2" TextWrapping="WrapWholeWords"/>
                    <TextBlock Grid.Column="1" Text="County" TextWrapping="WrapWholeWords" />
                    <TextBlock Grid.Column="2" Text="Business Name" TextWrapping="WrapWholeWords" />
                    <TextBlock Grid.Column="3" Text="Status" TextWrapping="WrapWholeWords" />
                    <TextBlock Grid.Column="4" Text="Type" TextWrapping="WrapWholeWords" />
                    <TextBlock Grid.Column="5" Text="FY" TextWrapping="WrapWholeWords" />
                </Grid>
        </DataTemplate>
    </ListView.HeaderTemplate>
    <ListView.ItemTemplate>
        <DataTemplate>
            <Grid Margin="-11,0,0,0" Width="1366" Height="Auto">
                <Grid.Resources>
                    <Style TargetType="TextBlock" BasedOn="{StaticResource SearchGridResultsTextBlock}">
                        <Setter Property="HorizontalAlignment" Value="Left"></Setter>
                    </Style>
                </Grid.Resources>
                <Grid.RowDefinitions>
                    <RowDefinition Height="44"></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*"/>
                    <ColumnDefinition Width="2*"/>
                    <ColumnDefinition Width="3*"/>
                    <ColumnDefinition Width="2*"/>
                    <ColumnDefinition Width="6*"/>
                    <ColumnDefinition Width="2*"/>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" Text="{Binding PermitNumber}" TextWrapping="WrapWholeWords" />
                <TextBlock Grid.Column="1" Text="{Binding County}" TextWrapping="NoWrap" />
                <TextBlock Grid.Column="2" Text="{Binding BusinessName}" TextWrapping="NoWrap" />
                <TextBlock Grid.Column="3" Text="{Binding Status}" TextWrapping="NoWrap" />
                <TextBlock Grid.Column="4" Text="{Binding PermitType}" TextWrapping="NoWrap" />
                <TextBlock Grid.Column="5" Text="{Binding EffFiscalYear}" TextWrapping="NoWrap" />
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
Andrii Krupka
  • 4,276
  • 3
  • 20
  • 41
BlackICE
  • 8,816
  • 3
  • 53
  • 91
  • Looks Like your Header is static. So why don't you pull the header out of ListView outside of Listview. And also. Why are you setting Width to 1366? What about smaller screens? Let me know if you think this is not a good idea, I will post an answer to do this in a different way. – AVK Jul 21 '16 at 20:15
  • @AVKNaidu I could do that, but I have about 10 of these in the app, so if there is a away I can apply a style to it to accomplish this that would be better. Width setting was just so I could see the whole thing in design mode and I forgot to remove. – BlackICE Jul 22 '16 at 13:22
  • Wrap your list view inside scroll viewer. Scroll viewer has a top header template. Load your header template there. And content will be just your list view. Make sure horizontal alignment is left and vertical alignment is top for listview. See if it helps. Or else let me know – AVK Jul 22 '16 at 13:27
  • ok, I'll try that and see what I get, thanks – BlackICE Jul 22 '16 at 15:05
  • try my answer) @BlackICE – Andrii Krupka Aug 11 '16 at 23:01

3 Answers3

11

Apply this style to your ListView and you will have static header

    <Style TargetType="ListView" x:Key="FixedHeaderListViewStyle">
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="TabNavigation" Value="Once" />
        <Setter Property="IsSwipeEnabled" Value="True" />
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
        <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled" />
        <Setter Property="ScrollViewer.IsHorizontalRailEnabled" Value="False" />
        <Setter Property="ScrollViewer.VerticalScrollMode" Value="Enabled" />
        <Setter Property="ScrollViewer.IsVerticalRailEnabled" Value="True" />
        <Setter Property="ScrollViewer.ZoomMode" Value="Disabled" />
        <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
        <Setter Property="ScrollViewer.BringIntoViewOnFocusChange" Value="True" />
        <Setter Property="UseSystemFocusVisuals" Value="True" />
        <Setter Property="ItemContainerTransitions">
            <Setter.Value>
                <TransitionCollection>
                    <AddDeleteThemeTransition />
                    <ContentThemeTransition />
                    <ReorderThemeTransition />
                    <EntranceThemeTransition IsStaggeringEnabled="False" />
                </TransitionCollection>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <ItemsStackPanel Orientation="Vertical" />
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListView">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" 
                            Background="{TemplateBinding Background}" 
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>

                            <ContentControl Content="{TemplateBinding Header}"
                                                ContentTemplate="{TemplateBinding HeaderTemplate}"
                                                ContentTransitions="{TemplateBinding HeaderTransitions}"/>

                            <ScrollViewer x:Name="ScrollViewer"
                                          Grid.Row="1"
                                        TabNavigation="{TemplateBinding TabNavigation}"
                                        HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
                                        HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                                        IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
                                        VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
                                        VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
                                        IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
                                        IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
                                        IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
                                        ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
                                        IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
                                        BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
                                        AutomationProperties.AccessibilityView="Raw">
                                <ItemsPresenter 
                                                Footer="{TemplateBinding Footer}"
                                                FooterTemplate="{TemplateBinding FooterTemplate}"
                                                FooterTransitions="{TemplateBinding FooterTransitions}"
                                                Padding="{TemplateBinding Padding}" />
                            </ScrollViewer>

                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
Andrii Krupka
  • 4,276
  • 3
  • 20
  • 41
  • Thanks, I'll give it a try, and post the one I have as welll if it's different – BlackICE Aug 13 '16 at 11:44
  • Tried this. Thanks @Andrii. Works a treat and does exactly what it says on the tin. However, for the situation I'm in, I need horizontal scrolling as well. Unfortunately, this solution does not provide horizontal scrolling of the headers along with the items (whilst tracking them). But still, a great answer if you don't require horizontal scrolling :) – Geoff James Mar 06 '17 at 12:23
  • 1
    Helped me - just extracted the template portion of the setter and the listview functioned and looked like the standard but with the static header. – Lindsay Aug 30 '17 at 11:13
  • Wow bunch of code to acomplish small task. Thanks for the help! – Borzh Jan 09 '18 at 15:32
  • @Borzh - On first glance, I agree with you, but after thinking about it a little more, I don't see another option. The designer might have chosen to have an attribute collapse one visual tree and make another one visible if you have 'FixedHeader' == true, or vica versa, but that would be hella ugly and might not perform as well. – Quark Soup Jun 06 '19 at 19:59
  • Ok. I have found the best way to achieve this with Horizontal scroll bars and virtualization without the need to restrict the height of the listview, is to group the query on Id and use CollectionViewSource with a group header template. – 27k1 Jul 27 '19 at 09:26
2

Based on the first answer (which works perfectly if horizontal scrolling is not required), here is a work around to achieve horizol scroll + sticky header (The FixedHeaderListViewStyle is same as above):

<ScrollViewer
   HorizontalScrollBarVisibility="Auto"
   HorizontalScrollMode="Auto"
   VerticalScrollBarVisibility="Disabled"
   VerticalScrollMode="Disabled">
   <ListView Style="{StaticResource FixedHeaderListViewStyle}">
      <!-- your list here -->
   </ListView>
</ScrollViewer>
Wendee Hsu
  • 98
  • 6
  • Problem here is that you have disabled virtualization with ScrollViewer – 27k1 Jul 19 '19 at 14:37
  • @peterincumbria what do you mean? I disabled vertical scroll in `scrollViewer` is because `ListView` already enables vertical scroll. – Wendee Hsu Jul 20 '19 at 02:40
  • Hi, You could be correct, please check this out. I've had a few problems with virtualization, if you have time to test, please let me know the result - could be interesting. https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/listview-and-gridview-data-optimization – 27k1 Jul 20 '19 at 09:02
2

The style provided by Andrii is definitely the answer, but you don't need to include all that other stuff. When overriding an intrinsic style, all you need to do is provide setters for the stuff you're actually overriding. The minimum code you need is:

<Style x:Key="FixedHeaderListViewStyle"
       TargetType="ListView">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListView">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <ContentControl Content="{TemplateBinding Header}"
                                        ContentTemplate="{TemplateBinding HeaderTemplate}"
                                        ContentTransitions="{TemplateBinding HeaderTransitions}"/>
                        <ScrollViewer AutomationProperties.AccessibilityView="Raw"
                                      BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
                                      Grid.Row="1"
                                      HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                                      HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
                                      IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
                                      IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
                                      IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
                                      IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
                                      IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
                                      x:Name="ScrollViewer"
                                      TabNavigation="{TemplateBinding TabNavigation}"
                                      VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
                                      VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
                                      ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
                            <ItemsPresenter Footer="{TemplateBinding Footer}"
                                            FooterTemplate="{TemplateBinding FooterTemplate}"
                                            FooterTransitions="{TemplateBinding FooterTransitions}"
                                            Padding="{TemplateBinding Padding}"/>
                        </ScrollViewer>

                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Quark Soup
  • 4,272
  • 3
  • 42
  • 74
  • this looks like a good alternative, much less code, but I've not been working with WPF for a while, so I can't say if it works for sure, hopefully someone will try it! – BlackICE Jun 18 '21 at 15:06