tags:

views:

484

answers:

3

Using the WPF Grid control is easy when your child elements are always contained within a single grid cell (i.e. there is no column/row spanning). A fixed width column stays the requested fixed width, an auto column will indeed size itself to be as wide as the widest cell in the column. Star columns share any remainder space according to their relative star values. All is simple in the world.

But once you have a cell that spans two or more columns the width of the columns becomes more complicated to calculate and indeed seems counter-intuitive. Here is a trivial example of what I mean. We can define two columns with the first as auto and the second as fixed at 30 pixels.

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>

Now we define a button that spans both of the columns and happens to measure at 80 pixels wide. (On my machine with my font and theme settings etc...)

<Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>

We need to allocate the 80 pixels width of the button across the two spanning columns. Given the second column is fixed at 30 I would expect 30 to be allocated against that column and the remaining 50 go to the first column. Because the first column is auto it seems the obvious thing to do.

But no. If you try this in practice you will find that the first column is 0 pixels and the second column has become the full 80. Even though that second column is defined as fixed and so should not be growing bigger, it as stretched to the full width of the button. Maybe I am missing something here but that does not seem very logical to me.

So to my actual question. Is there a full description of the logic used by the Grid control for doing this calculations so that I can fully understand how it works? I have searched MSDN and Google and find nothing that describes how spanning elements affect the column widths.

A: 

I do not know if there is a full description somewhere on the interwebs..

I do know, that Auto means it will ask the child controls how big they wish to be.. An asterisk (*) means it will take all the room it has left...

Edit: Instant update.. Width (ColumnDefinition) and Height (RowDefinition) are GridLength structs:

GridLength on MSDN

Arcturus
You have just given an answer that states you do not have an answer. My question points out that I already know what Auto,*,Fixed types are and how they work. I need to know how they work with cell spanning.
Phil Wright
+2  A: 

Honestly I didn't believe you when I read this, so I had to go off and test it myself, and indeed you are correct. I don't think that you'll have any luck finding rules though because further investigation led me to believe it is a bug in the WPF grid. I performed a number of tests to see if I could figue out the behaviour, but if you just want to know the final conclusions feel free to scroll past the following diatribe.

Sorry if the results aren't clear, they are how I wrote them as a worked. The first three numbers are the sizing values I put in for each column and rest is the resultant sizes.

Test 1:

<Grid Height="300" Width="300">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="..."/>
        <ColumnDefinition Width="..."/>
        <ColumnDefinition Width="..."/>
    </Grid.ColumnDefinitions>
    <Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>
</Grid>

Grid 300 X 300 hosting a contol 80 pixels wide. The grid has three columns and the control spans columns 0 and 1:

30  , auto, *    - column 0 - 80,  column 1 - 0,  column 2 - 220
auto, auto, *    - column 0 - 40,  column 1 - 40, column 2 - 220
auto, 30  , *    - column 0 - 0,   column 1 - 80, column 2 - 220
*   , 30  , *    - column 0 - 135, column 1 - 30, column 2 - 135
*   , 30  , 26*  - column 0 - 10,  column 1 - 30, column 2 - 260
*   , auto, *    - column 0 - 150, column 1 - 0,  column 2 - 150
*   , auto, 30*  - column 0 - 10,  column 1 - 0,  column 2 - 290
30  , 30  , *    - column 0 - 30,  column 1 - 30, column 2 - 240
*   , *   , 28*  - column 0 - 10,  column 1 - 10, column 2 - 280

So from this I can come up with a number of rules:

  1. If an element spans an auto sized cell and a non-auto sized cell the auto sized cell will be sized to 0
  2. If an element spans a fixed cell and an auto cell then the fixed cell's width will increase to the minimum that can contain the whole object
  3. If an element spans a * sized cell and an auto sized cell then the * sized cell will have its expected size
  4. If an element spans two auto sized cells they will expand so as to fit the object and be equal in width
  5. If an element spans cells that don't include an auto cell then the cells will have their expected size

Test 2:

<Grid ShowGridLines="True" Height="300" Width="300">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="..."/>
        <ColumnDefinition Width="..."/>
        <ColumnDefinition Width="..."/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>
    <Button Grid.Row="1" Grid.Column="1" Content="QWERTYUIOP"/>
</Grid>

Grid 300 X 300 with 3 columns hosting one control 80 pixels wide spaning columns 0 and 2 on row 0 and a second control 80 pixels wide only in column 1 of row 1

30  , auto, *    - column 0 - 30,  column 1 - 80, column 2 - 190
auto, auto, *    - column 0 - 0,   column 1 - 80, column 2 - 220
auto, 30  , *    - column 0 - 0,   column 1 - 80, column 2 - 220 (*)
*   , 30  , *    - column 0 - 135, column 1 - 30, column 2 - 135
*   , 30  , 26*  - column 0 - 10,  column 1 - 30, column 2 - 260
*   , auto, *    - column 0 - 110, column 1 - 80, column 2 - 110
*   , auto, 30*  - column 0 - 7,   column 1 - 80, column 2 - 213
30  , 30  , *    - column 0 - 30,  column 1 - 30, column 2 - 240
*   , *   , 28*  - column 0 - 10,  column 1 - 10, column 2 - 280

(*) This isn't exactly what happens. Column 1 is set to size 80 but only draws part of the non-spanning element. I was using buttons as my elements and the non-spanned button's chrome filled up the 80 pixel wide first column, but the text was trimmed to a size of 30 pixels. Basically it was completely screwed.

From this test I can add two more rules:

  1. If an auto column is used in a span and can get its size from somewhere it will behave as expected
  2. If an auto column is used in a span and can not get it's size from somewhere else then the mesuring system will break and may cause graphical corruption.

So, I guess we can combine these rules into one overarcing philosophy:

If an elements spans multiple columns and at least one, but not all, of those columns are auto sizing there must be another that doesn't span to provide a size for the auto sized columns. Otherwise the behaviour is at best unexpected, and at worst corrupting.

Feel free to raise a bug with Microsoft, but since this is now the defined behaviour of the grid control I imagine we're stuck with it for the lifetime of WPF.

Martin Harris
Although this is not the algorithm as such I was expecting it does confirm my own result of it being screwed up! Basically I recommend never have spanning cells when possible.
Phil Wright
A: 

Here how it is done:

If requestedSize (childelement.DesiredSize) fits into preferred size of the range (span).

      RequestedSize  is distributed according to the following logic:

         -  auto definitions won't  participate in distribution;  they will only get minsize if set.
        -   for all non-auto definitions,  requested size  is distributed equally between them; without exceeding preferred size of each.

if requestedSize bigger than preferred size, but fit into max size of the range. RequestedSize is distributed according to the following logic:

     -   for all non-auto definitions,  requested size is distributed equally between them; without exceeding  exceeding maxsize of each.

         - leftover size from above step is then equally distributed  between auto definitions.

If requestedSize bigger than max size of the range.

     RequestedSize  is distributed equally  between all definitions in following way:

      if  equi-size is less than maximum of maxSizes. 

               -   in this case size is distributed such a way that smaller definitions grow faster than  bigger ones.

      if  equi-size is greater or equal to maximum of max sizes.

             -   all definitions will receive equalSize as their min sizes.
Rath Shetty