9

I was looking at this question and discovered something very weird: it appears that the height of a row is incorrectly calculated in some cases involving Grid.RowSpan.

Here's an simple drawing of the Grid I'm testing with:

---------------
|   1   |     |
--------|  3  |
|   2   |     |
---------------
|      4      |
---------------

And here's some sample code for this Grid that demonstrates the problem:

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Background="Red">
        <Label Content="CELL 1 A"/>
        <Label Content="CELL 1 B"/>
        <Label Content="CELL 1 C"/>
    </StackPanel>

    <Grid Grid.Column="0" Grid.Row="2" Background="CornflowerBlue">
        <Label Content="CELL 2 D"/>
    </Grid>

    <StackPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Background="Yellow">
        <Label Content="CELL 3 A"/>
        <Label Content="CELL 3 B"/>
        <Label Content="CELL 3 C"/>
        <Label Content="CELL 3 D"/>
    </StackPanel>


    <Grid Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Background="Green">
        <Label Content="CELL 4"/>
    </Grid>
</Grid>

The end result is the height of the 3rd row (cell #2 and #3) has a lot of extra space in it:

enter image description here

If I adjust the Grid.RowSpan of the 1st and 3rd cells by +/- 1, and adjust the Grid.Row for the 2nd and 4th by +/- 1 to account for the extra row, I get this (correct) result:

enter image description here

I also get correct results if I remove enough elements from cell #3 so it can render in a single Row, like this:

enter image description here

And strangely enough, removing some the objects results in only some of the extra space being applied

enter image description here

I've been messing around with the number of elements in cells #1 and #3, and number of Rows, but I can't seem to figure out a conclusive pattern to explain this behavior.

What exactly is WPF doing behind the scenes when rendering this Grid to cause the extra space to appear when the Grid.RowSpan on cell #3?

Community
  • 1
  • 1
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • I was going to say because a grid measures differently but I tried it with all stackpanels and it still sizes that row wrong. – paparazzo May 01 '13 at 17:41
  • There are too many 'Auto' row heights. The Grid control does a pretty good job of dealing with row and column spanning, but when all the row heights are set to 'Auto' it's like solving an equation with too many unknowns. Constraining 1 or both of the top 2 rows to a fixed height helps out immensely. – Stewbob May 01 '13 at 19:27
  • @Stewbob I have also tested with leaving the last row at `Height="*"`, and it doesn't make a difference. The problem is with determining the Height of a cell that uses `RowSpan`, and is not related to how the `Grid` allocates the extra vertical space. I've updated my code sample to include a `*` sized row to help make that clear :) – Rachel May 01 '13 at 20:04
  • Not a "*", which is 'leftover' or 'ratio-ed' space, but an actual fixed value for 1 of the top two rows; the ones that are being spanned. If you set the height of the first row in your RowDefinitions to a fixed value, like '30' it sizes the spans correctly. – Stewbob May 01 '13 at 20:43
  • @Stewbob 6 auto on a simple layout is too many unknowns. – paparazzo May 01 '13 at 20:44

3 Answers3

1

I've run into this kind of condition before, as in my question here about extra space appearing in a ListView

Per a response I got from a Microsoft employee:

The bug involves a step in VSP’s Measure algorithm that remembers the largest size ever discovered and forces all future Measure calls to report a size at least as large. In your case, the VSP is initially measured before any triggers have fired, so it computes the size as if everything were visible. When the triggers fire and collapse the buttons, the measure algorithm computes the correct (small) size, but then forces the result to be large again.

The behavior of your grid appears similar to the behavior of my Virtualizing Stack Panel: Something is going on with the RowDefinition's Measure calls which force it to remember and always report a larger size, even though later on down the line a smaller one would be better.

In short, you probably found a bug in WPF, which, because there are myriad workarounds (match the total rows defined to the total needed, rearrange your grid, whatever else...) may never get attention. You can confirm or refute this only by opening a Microsoft Connect bug and waiting for their reply.

Community
  • 1
  • 1
Rob Perkins
  • 3,088
  • 1
  • 30
  • 51
  • 2
    That post you linked was very interesting, and I suspect you are right about it being a bug in the Measure calls. I hope you don't mind, but I'm going to edit your answer a bit to include the relative quote from your linked post. I was hoping someone would post an answer explaining exactly what happens behind the scenes to make this happen, backed up by reflector or MSDN code, but it's not looking like that is going to happen so I'm awarding you the bounty rep as I think your answer is the closest thing I'll get to a solid answer. – Rachel May 14 '13 at 16:09
  • Rachel I think you just demonstrated the value of a good editor. Random House, take note! – Rob Perkins May 14 '13 at 17:14
0

I don't have a full answer on why .NET does your 3rd row wrong.
But I contend what you are asking it to do is illogical as there is no reason to to have 0,0 span two rows.
When rows are shared they are not likely be equal length and WPF must add length to the shorter(s).
In you case since you have shared rows in the shared rows so WPF must apply some weighting and does not do that correctly.
If you don't span 0 0 then is shares the extra extra space with row 0 column 0 and row 1 column 0 equally which (to me) is the correct answer.

<Window x:Class="GridRowSizing.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <Style BasedOn="{StaticResource {x:Type Label}}" TargetType="Label">
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Margin" Value="3"/>
        </Style>
    </Window.Resources>
    <Grid ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Column="0" Grid.Row="0" Background="Red">
            <Label Content="CELL 1 A"/>
            <Label Content="CELL 1 B"/>
            <Label Content="CELL 1 C" BorderBrush="Black" BorderThickness="2"/>
        </StackPanel>
        <StackPanel  Grid.Column="0" Grid.Row="1" Background="CornflowerBlue">
            <Label Content="CELL 2 D" BorderBrush="Black" BorderThickness="2"/>
            <Label Content="CELL 2 E" BorderBrush="Black" BorderThickness="2"/>
        </StackPanel>
        <StackPanel Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Background="Yellow">
            <Label Content="CELL 3 A"/>
            <Label Content="CELL 3 B"/>
            <Label Content="CELL 3 C"/>
            <Label Content="CELL 3 D" BorderBrush="Black" BorderThickness="2"/>
            <Label Content="CELL 3 E" BorderBrush="Black" BorderThickness="2"/>
            <Label Content="CELL 3 F" BorderBrush="Black" BorderThickness="2"/>
        </StackPanel>
        <StackPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Background="Green">
            <Label Content="CELL 4"/>
        </StackPanel>
    </Grid>
</Window>
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • Yes, I know such code is illogical and that there are easy ways to avoid such behavior, such as removing the unneeded and undefined 2nd row (Grid.Row="1"), but I was curious what WPF does behind the scenes to decide render that way in the first place, and only with specific RowSpans. I suspect you're right that WPF is applying some weighting incorrectly when measuring the undefined row, as the number of elements affect how much extra space is drawn. I'm guessing I'll need to pull out reflector to get a definitive answer on this... – Rachel May 02 '13 at 16:30
  • 1
    I suspect it gets the weighting wrong (but there is no valid reason I can see to make it weight). Where you see what appears to be the correct weighting I suspect the delta is 0 and and weighting 0 wrong you don't see the wrong answer. – paparazzo May 02 '13 at 16:36
0

As Rob said, this is probably a bug in WPF measuring calls. So I don't know your answer. But to explore the inner workings of WPF apps, I use Snoop. It's an awesome tool, similar to the browser tools that show HTML elements, snoop shows you how your WPF form is laid out, nested elements, properties of elements, etc. It helps me a lot when trying to figure out layout issues. I thought I'd mention.

Tombala
  • 1,660
  • 9
  • 11