Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force Grid to shrink an auto sized row that contains ScrollViewer (when needed)?

How would one create this layout?:

How it should work

The idea is to keep the blue text always just below the green element BUT when there is not enough space, the green element should be placed inside a scrollable container (with vertical scrollbar visible).

I have tried StackPanel and Grid and both failed. The StackPanel does not shrink rows at all. The Grid does the same if the top RowDefinition's height is set to "auto" (the ScrollViewer inside does not show its scrollbar). If it's set to a star, the blue text is moved down to at least half of the space (depends of the bottom row's height setting). The bottom row's height can be either an "auto" or a star with VerticalAlignment of the blue text set to Top BUT the row's height must be kept at least as big as the text's. Of course the whole area (black box) cannot be stretched by its content - it's determined by the size of the window.

How? Please keep the responses to be based on XAML if possible.

like image 854
P.W. Avatar asked Aug 01 '14 10:08

P.W.


2 Answers

Inspired by Eirik's approach of constraining a greedy container by putting it inside other tight container, I've discovered a really simple way to achieve what I wanted. I only needed a container that shrinks its one child first, then (when the first one completely disappears) the second one. And there is such container: the DockPanel. Here goes:

<DockPanel LastChildFill="False">
    <DockPanel DockPanel.Dock="Top">
        <TextBlock DockPanel.Dock="Bottom" TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
        <ScrollViewer>
            <TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
        </ScrollViewer>
    </DockPanel>
</DockPanel>

As simple as that! :) I hope it helps someone.

like image 141
P.W. Avatar answered Sep 30 '22 06:09

P.W.


<Grid Name="outerGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>

    <Canvas>
        <Grid MaxWidth="{Binding ElementName=outerGrid, Path=ActualWidth}" MaxHeight="{Binding ElementName=outerGrid, Path=ActualHeight}">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <ScrollViewer Width="{Binding ElementName=outerGrid, Path=ActualWidth}">
                <TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
            </ScrollViewer>
            <TextBlock TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
        </Grid>
    </Canvas>
</Grid>

The auto height will grow to match the height of the content of that row.

The star height will let that row grow in height to fill the rest of the height of the grid, preventing the ScrollViewer to grow more than what's visible.

Edit: If you put the Grid inside another Grid like the XAML above you should get the behavior you want. The second row of the outer row acts as a "filler" to fill the rest of the space the outer Grid.

Edit 2: Try the edited XAML above. I've put the inner Grid inside a Canvas (to prevent clipping) and bound the MaxWidth and MaxHeight of the inner Grid to the ActualWidth and ActualHeight of the outer Grid to keep the inner Grid the same size as the outer Grid.

Edit 3: Added binding to the Width of the ScrollViewer to keep it the same width as the rest.

like image 33
Eirik Avatar answered Sep 30 '22 07:09

Eirik