tags:

views:

63

answers:

1

In this example the first column gets 100 and the next 2 columns get 50 each, which is the expected behaviour.

<Grid Width="200" Height="200">
    <Grid.ColumnDefinitions>
        <ColumnDefinition MinWidth="100" />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Border Background="Red" Grid.Column="0" />
    <Border Background="Yellow" Grid.Column="1" />
    <Border Background="Blue" Grid.Column="2" />
</Grid>

alt text

If I move the MinWidth to the middle column ...

<Grid Width="200" Height="200">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition MinWidth="100" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Border Background="Red" Grid.Column="0" />
    <Border Background="Yellow" Grid.Column="1" />
    <Border Background="Blue" Grid.Column="2" />
</Grid>

... then the first column gets 33.3 and the last column 66.6 which seems weird. Not sure why this should change the grid's behaviour. I would expect columns 0 and 2 to get 50 each.

alt text

Update: I understand why this happens, but was wondering if anyone thinks it is a bug (especially since the behaviour in Silverlight is different)

+1  A: 

I'm assuming you're looking for a reason for the behavior and not some kind of workaround for this situation. Note: There are differences in how this is rendered in .NET 3.0/3.5 and 4.0. I'm not exactly clear why; my answer is based on 3.0/3.5.

So, why does this happen? Its because of composition and XAML.

What? That's not enough? Okay, let me back up a minute.

Lets say you are designing a Grid control for use in WPF. You know a grid has to have columns and rows. The question now becomes, "how do you configure the number of columns and rows?" The naive choice is two public properties--Columns and Rows.

If you choose this route, you quickly find that while you can configure the number of rows and columns, you cannot say that row 5 will have height X and column 9 will have a width of Y. This is where composition comes in.

Instead of having public properties designating your row and column count, you have collections of RowDefinitions and ColumnDefinitions which contain classes of type RowDefinition and ColumnDefinition. These types not only encapsulate information about rows and columns but also the logic for determining their dimensions and setting layout.

So the Grid, in order to make itself amenable to definition via composition rather than configuration, cedes the process of defining its columns and rows to other types. So the Grid does not fully control and orchestrate its layout. This is an important point.

The second part is XAML, specifically how it is "rendered", or how the deserialization of the xml into a visual tree of objects happens.

XAML is, roughly, serialized object graphs represented by a subset of XML with a little extra flavor thrown in. The nature of serialization (graphs to a serial, or ordered, list) means that objects are deserialized in an order that is determined by their definition in xml. In short, order matters in XAML.


Where does this leave us with your question? Without going into great detail about the layout system of WPF (which requires a book to describe well enough), here's why the columns aren't even:

The columns start with an equal share of their container, based on the number of columns that exist. But then, each ColumnDefinition has their own chance to adjust layout based on their own configuration--something that the parent Grid doesn't know about. So, in order, they are allowed the opportunity to "fight it out" for room.

In the first example:

  • The first ColumnDefinition takes its allocated 1/3rd, is not happy, so takes its minimum width.
  • The second ColumnDefinition gets 1/2 of the rest of the available space in the container, and is happy.
  • The third ColumnDefinition gets its 1/2 of the rest and is happy as well.

In the second example:

  • The first ColumnDefinition takes its container-allocated 1/3rd and is happy.
  • The second ColumnDefinition takes its allocated 1/3rd, but finds it is lacking, and so takes up enough extra room to meet its configured minimum width.
  • The third ColumnDefinition is not configured for a minimum, and so takes what is left over.

Of course, this is a grand simplification of how the Grid is implemented, but it should explain in a way in which you can grasp what is happening. The Grid doesn't control child arrangement, it works with its ColumnDefinitions to perform this task. Each one, in the order in which it was defined in XAML, takes its own part in the layout of controls. There is only one pass performed, from first definition to last.

This is why the Grid doesn't intervene into the column widths to tidy things up (it is not in the Grid's contract to do so) and why the first column has 1/3rd of the width of the container and the 2nd column runs roughshod over the 3rd. In order for the Grid to do what you expect it to do, it would have to perform multiple passes, OR it would have to fully control column/row layouts. It was not designed to do so, and so it does not behave that way.

Get this, and you can understand other WPF control behaviors, such as the DockPanel's LastChildFill and why controls defined later in a Canvas have a higher z-index than ones defined earlier.

Will
Nicely explained.
Wonko the Sane
The images are correct when run in a WPF application. Kaxaml renders it differently (as does Silverlight actually). Also the Grid *does* have some say on child arrangement - it calls Arrange on each child with a Rect specifying it's offset (within the Grid) and the amount of space that cell has been allocated. The Grid calculates this. Also Row/ColumnDefintions don't really contain any logic, but mostly carry data that the Grid uses to perform this task. The order in the XAML is not really relevant to this question.
dwynne
@dwynne ouch on kaxaml. I wonder what is going on that causes this behavior change? As for your contention that RD/CD don't contain any logic, it is somewhat true. The logic is contained in their base class, DefinitionBase. They all work very closely with each other to perform layout; perhaps a bit too closely. While order isn't 100% relevant in this case, I think it does when trying to figure out how controls are rendered in a WPF form in general. It has more relevance in kaxaml, that's for sure! Gonna drop the dev a line and ask him about that...
Will
@dwynne Hmm, Kaxaml renders it correctly. For .NET 3.0 and 3.5. Apparently its done differently in 4.0. Create a WPF app and try targeting 3.5 and 4.0 and you'll see a difference in how this is rendered. Interesting... Updating my answer.
Will