Home > Software engineering >  ScrollViewer conflicts with Grid ColumnDefinition if content is too big
ScrollViewer conflicts with Grid ColumnDefinition if content is too big

Time:07-12

This is more of a general question to figure out if this is intentional behavior or a bug in .NET

<Grid Width="200" Height="200">
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
        <Grid Background="Green">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Rectangle Fill="Red" Margin="5" Grid.Column="0"/>
            <Rectangle Fill="Blue" Margin="5" Grid.Column="1"/>
            <Rectangle Fill="Violet" Opacity="0.7" Grid.Column="0" Grid.ColumnSpan="2" Width="40" HorizontalAlignment="Left"/>
        </Grid>
    </ScrollViewer>
</Grid>

Above XAML gives the following output:

enter image description here

What I discovered, is when on the Third Rectangle with the Violet Brush I set its Width to be larger than the Grid hosting the ScrollViewer, the Grid with the ColumnDefinitions resets its Column Behavior to be 1*:

enter image description here

The Designer still says that the first Column has a Width of 50px, but as you can see it's sharing the same total available space as the second Column, as if both were set to be 1*.

Is this intentional or actually a bug in .NET?

If it's the former, what would be the best approach to tackle this with only XAML to have the Columns behave the way they were defined.

CodePudding user response:

This behavior is related to the ScrollViewer. ScrollViewer allows an infinite width, which obviously does not work well when measuring the spanning column width, where at least one column is dynamic (* or Auto).

Grid will always try to stretch to occupy the max available space if there is no constraint. In your first scenario, the parent Grid enforces such a constraint: when the child Grid measures itself, it gets the available size from the parent Grid (in your case [200,200]).

I this case, the child Grid can measure itself properly: the effective available space for the second column is

column_2_width = available_size_width - column_1_width = 200 - 50

In your second scenario, the ScrollViewer, which will become visible once the content exceed the viewport, will lift the constraint enforced by the parent Grid and provides an infinite space:

column_2_width = available_size_width - column_1_width = positive_infinity - 50

positive infinity - x is not defined. This becomes problematic for the spanning column width and Grid will now modify the ColumnDefinition.Width value. Widthis just a desired value. It is not enforced or binding. To put a constraint on the width you can set the ColumnDefinition.MinWidth and ColumnDefinition.MaxWidth properties.

To ensure a specific fixed value (or final size), you should set all three properties Width, MinWidth and MaxWidth to the same desired value.
This forces the layout system (out layout logic) to finally apply the desired value.

This why FrameworkElement.ActualWidth (actual value) deviates from the set FrameworkElement.Width (suggested value) in some cases.

All this applies to the RowDefinition.Height (or FramworkElement.Height in general) as well.

To fix the issue, you can assign a ColumnDefinition.MaxWidth to allow the Grid to measure itself correctly.

<Grid Width="200" Height="200">
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
        <Grid Background="Green">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"
                                  MaxWidth="50" />
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Rectangle Fill="Red" Margin="5" Grid.Column="0"/>
            <Rectangle Fill="Blue" Margin="5" Grid.Column="1"/>
            <Rectangle Fill="Violet" Opacity="0.7" Grid.Column="0" Grid.ColumnSpan="2" Width="40" HorizontalAlignment="Left"/>
        </Grid>
    </ScrollViewer>
</Grid>

A fixed column width instead of a dynamic will of course also work.

  • Related