6
votes

I'm trying to figure out how to make my ContentControl to correctly scroll Horizontally (Vertically its fine at the moment). By correctly i mean i would like to see the content to stretch (expand infinitely) while having minimum sizes to which a scrollbar would appear in order for the content not to overflow behind the ContentControl's area, so here's a quick introduction:

The main window is structured in this way:

  • Grid (2 columns of .3* and .7*)
    • Border
      • Grid (7 rows, one set to * where ContentControl is)
        • ScrollViewer with StackPanel (purely for test) wrapping a ContentControl that has Auto Width

ContentControl's Template:

  • Grid (Width set to UserControl's ActualWidth, 6 rows with one set to Auto where ItemsControl go
    • ItemsControl that describes an ItemTemplate of a type DataTemplate which contains a Grid inside of which i have a DataGrid

The actual problem is that the ContentControl grows as you resize the window, but does not shrink with window resize.

Main View XAML (truncated for clarity):

<ScrollViewer Grid.Row="5" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
        <ContentControl Grid.Row="5" Background="Transparent"  Focusable="False" Margin="0,5,0,0"
                            Content="{Binding CurrentSection}" ContentTemplateSelector="{StaticResource templateSelector}/>
</ScrollViewer>

Tempate XAML (truncated for clarity):

<Grid>
...
    <ItemsControl Grid.Row="4" ItemsSource="{Binding Data.QualifyingDistributionsDividends}" x:Name="QualifyingItemsControl">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid x:Name="DTLayoutGrid">
                    ...
                    <Grid Grid.Row="1" x:Name="DataLayout" Width="{Binding ElementName=DTLayoutGrid, Path=ActualWidth}" HorizontalAlignment="Stretch">
                        ...
                        <DataGrid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="8" HorizontalScrollBarVisibility="Disabled"
                                  ItemsSource="{Binding Payments}" Style="{StaticResource DataGridStyle}" CellStyle="{StaticResource DataGridNormalCellStyle}">
                        </DataGrid>
                    </Grid>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

So what happens? Datagrid assumes width of the entire DataTemplate (well its underlying controls that are set to be DataTemplates size, then * column assumes all empty space. When you try to resize the entire window that holds this code it will grow correctly, expanding the * column but it seems shrinking is not "registered" and it keeps the size you expanded it to, applies a scrollbar over that and forgets about it.

What i've tried so far was to set widths for ItemsControl, its underlying parents like Grid etc, also setting size to ContentControl, StackPanel, ScrollViewer and parent Grid of that. I've also tried using scrollviewers directly on the Datagrid which produces an epileptic "1 million resizes a second" scenario. I've also played around with HorizontalAlignments Under certain situations i DID managed to get the horizontal scrollbar to appear correctly but unfortunately that makes my DataGrid's * column to assume Auto Width rather then Star so DataGrid starts having an empty area to the right (unacceptable unfortunately...)

I understand that in order for horizontal scrollbar to work the parent or child of the scrollviewer needs Width set, i guess i can't work out where exactly do i need to restrict it. DataGrids NEED to infinitely expand with the main window while having first column fill all the available space. Do let me now if you need more information on this and I will gladly answer.

2
Giving Width of the Grid to the ContentControl makes is react correctly but once minimum size is reached the Template overflows and no scrollbars appear, giving it a HorizontalAlignment="Stretch" does nothing, so does removing the ScrollViewer.CanContentScroll="False"kosdos
Dude... that's far too long... we don't need your life history, just the relevant part of your program.Sheridan
How is explaining an issue in detail so people understand it better a life story? Large amount of code had to be put in due to unknown from where the issue is coming from. I will trim down the snippets i guess...kosdos
Forgive my para-phrasing... I was just giving you good advice to help you get your question answered. Many users here would have seen how long your question was and simply moved onto the next one. +1 Well done for making it more concise though.Sheridan
No worries! I was thinking this is a no easy points one so decided to elaborate on the scenario.kosdos

2 Answers

5
votes

It seems to me that this is just another case of the dreaded StackPanel layout problem. This problem comes up again and again and I confess that I had the very same problem when I started learning WPF. The StackPanel does not take the available size of its parent into consideration whereas other Panels such as a DockPanel or a Grid (yes, that's actually a Panel too) do.

It's explained in the How to: Choose Between StackPanel and DockPanel page on MSDN:

Although you can use either DockPanel or StackPanel to stack child elements, the two controls do not always produce the same results. For example, the order that you place child elements can affect the size of child elements in a DockPanel but not in a StackPanel. This different behavior occurs because StackPanel measures in the direction of stacking at Double.PositiveInfinity; however, DockPanel measures only the available size.

The StackPanel should only really be used to align a number of items, such as Buttons or other controls in a straight line where available space is not a concern. So anyway, the solution should be simple... just remove the StackPanel from the ScrollViewer. It doesn't appear to serve any purpose there anyway.


UPDATE >>>

After looking again, it seems as though you're saying that the problem is inside the DataTemplate, right? You might be able to fix that by setting the ItemsControl.HorizontalContentAlignment property to Stretch. That would ensure that each item remains within the boundary of the ItemsControl.

I'd also remove your Binding on the Grid.Width as you don't need it... a child Grid will take up the full space of a parent Grid by default. If these ideas don't work, just simplify your problem. Seriously, if you follow the advise in the linked page from the Help Center that I gave you in the comments, then you'll either fix the problem, or be able to come back here and provide a complete, but concise example that we could test.

0
votes

I've found the behavior I was looking for by using a UniformGrid as the ItemsPanel, with its rows bound to the count of the ItemsSource model:

<ScrollViewer>
    <ItemsControl ItemsSource="{Binding MyCollection}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Rows="{Binding MyCollection.Count}" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                 ...                      
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

As @Sheridan pointed out above, it seems the StackPanel is causing trouble. Also, credit to https://stackoverflow.com/a/23375262/385273 for pointing out the UniformGrid option.