WPF Grid Row and Column MinHeight

April 26th, 2009

I ran into an issue last week with the WPF Grid control and how it handles RowDefinition.MinHeight and ColumnDefinition.MinWidth.   Let’s start with a simple 2 row Grid layout.

<Window x:Class="GridLayoutTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:GridLayoutTest"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button Grid.Row="0">Row 0</Button>
        <Button Grid.Row="1">Row 1</Button>
    </Grid>
</Window>

image

If we then change the second row to have <RowDefinition Height=”*” MinHeight=”200”/>

image

Since the second row’s MinHeight is 200, it takes up 2/3 of the 300 Height window.

The unexpected happens if you put a control into the first row that has a desired height > 100.  This example uses a stack panel with buttons in it.

<Window x:Class="GridLayoutTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:GridLayoutTest"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*" MinHeight="200" />
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
            <Button>Stack Panel Button</Button>
        </StackPanel>
        <Button Grid.Row="1">Row 1</Button>
    </Grid>
</Window>

image

The “Row 1” Button has shrunk below 200 height!   If you really want the “Row 1” button to always be at least 200 height, then this is a problem.  Depending on what kind of control you have in the first row, the second row height changes.   What’s happening behind the scenes is the Grid control treats the MInHeight constraint as lower priority to the size needed by the other controls.   Since the Grid control doesn’t allow you to easily override its MeasureOverride and ArrangeOverride without a lot of complex code, I searched for a simpler solution.   The key to the solution is creating a Panel derived class that ignores the measured size of it’s children and always returns Size(0.0,0.0).   The size returned from MeasureOverride is the desired size of the element, but size passed to ArrangeOverride is the actual size the element will be.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;

namespace GridLayoutTest
{
    public class NoGridResizePanel : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            Size measureSize = new Size(
                availableSize.Width ==
                    Double.PositiveInfinity ? 0.0 : availableSize.Width,
                availableSize.Height ==
                    Double.PositiveInfinity ? 0.0 : availableSize.Height);

            foreach (UIElement child in Children)
            {
                child.Measure(measureSize);
            }
            //always return Size(0.0,0.0) so grid will not expand
            //the cell we are in based on our size
            return new Size(0.0,0.0);
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            //overlay all children on top of each other
            foreach (UIElement child in Children)
            {
                child.Arrange(new Rect(new Point(0.0,0.0),finalSize));
            }
            return finalSize;
        }
    }
}

<Window x:Class="GridLayoutTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:GridLayoutTest"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*" MinHeight="200" />
        </Grid.RowDefinitions>
        <local:NoGridResizePanel>
            <StackPanel Grid.Row="0">
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
                <Button>Stack Panel Button</Button>
            </StackPanel>
        </local:NoGridResizePanel>
        <Button Grid.Row="1">Row 1</Button>
    </Grid>
</Window>

image

The MinHeight of the second row now behaves like you expect it to no matter what kind of control you put into the first row of the Grid.

Comments are closed.