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:
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*:
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. Width
is 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.