Grid/ScrollViewer- Freeze grid header row vertical scrolling, but not horizontal scrolling

soundslike picture soundslike · Nov 27, 2011 · Viewed 8.8k times · Source

I'm not using a DataGrid or a ListView and am using ScrollViewer and Grids to create cells that contain wrap panels of items for each cell column.

I'd like something with a similar behavior to the header columns of a datagrid or listview to keep it frozen while scrolling vertically through the items below it, but have it scroll horizontally.

I currently have the following as the root of my main window which works except for when there's a vertical scrollbar that is displayed the alignment of the items is off.

Notes:

  • The header columns are dynamically added in the code behind to the "mainGrid" below with a "1*" size for each column, and are "frozen" by having the horizontal scrollbar allowed but vertical scrollbar disabled.
  • Then an inner scrollviewer is added that disables horizontal scrolling, but allows vertical scrolling of the inner "row groupings".
  • The ItemTemplate for the ItemsControl creates a grid for each "row grouping" across the columns.

So when the "innerScrollViewer" displays it's vertical scrollbar the items aren't aligned with the outer column headers anymore and the items are shifted to the left due to the right vertical scrollbar. How do I get it to dynamically line things up with the outer column headers for the two scenarios of when a vertical scrollbar is displayed or not?

<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
        <Grid x:Name="mainGrid"
              Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}">
    <RowDefinitions>
        <RowDefinition Height="Auto" />     <!-- Contains Header columns dynamically added to be "frozen" -->
        <RowDefinition Height="*" />        <!-- "row groupings" -->
    </RowDefinitions>

            <ScrollViewer x:Name="innerScrollViewer"
                          HorizontalScrollBarVisibility="Disabled"
                          VerticalScrollBarVisibility="Auto"                          
                          Grid.Row="1"
                          Grid.ColumnSpan="{Binding Path=NumColumns}">

        <ItemsControl Name="mainWorkItemsItemsControl"
                  Width="{Binding ActualWidth, ElementName=mainGrid}"
                                  ItemsSource="{Binding MyGroups}"
                                  ItemTemplate="{StaticResource groupedTemplate}" />                    

            </ScrollViewer>
    </Grid>
</ScrollViewer>


<DataTemplate x:Key="groupedTemplate">

    <ItemsControl ItemsSource="{Binding GroupItems}">

                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <!-- Creates a grid with one row and the same number of columns as "mainGrid" above with a star sizing for each column  -->
                            <SimpleGrid Rows="Auto" 
                                        Columns="{p:PyBinding CommaDelimit($[.NumColumns]\, \'*\')}" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>

                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                <ItemsControl ItemsSource="{Binding Path=GroupedColumnItems}">                                    
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                        <MyControl />                                              
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>

                    <ItemsControl.ItemContainerStyle>
                        <Style>
                            <Setter Property="Grid.Column"
                                    Value="{Binding Path=Index}" />
                        </Style>
                    </ItemsControl.ItemContainerStyle>

    </ItemsControl>

</DataTemplate>

Answer

makwana.a picture makwana.a · Nov 27, 2011

You have three choices.

  1. Make vertical scroll bar visible by default for both the scroll viewers (for header and inner) so when inner scroll viewer requires vertical scroll, vertical scroll bar just get enabled without taking any more space.

  2. Bind width property of each columns in header with corresponding columns in grids in rows. so when width of a column of row grid changes it will also change width of corresponding column of header. this will also help you to give a facility to user to re size columns by dragging their borders. but it will require more codes.

  3. Make vertical bar of inner scroll viewer visible and set right margin on header which is equal to width of vertical scroll bar.