WPF Grid’s Row/Column Count Properties

One of my pet peeves with using WPF is that a Grid needs to have it’s Rows and Columns defined. One day I decided I was tired of messing with Row/Column definitions for simple Grids, so sat down to write some AttachedProperties.

Now, instead of writing something like this:


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

I can write this:


<Grid local:GridHelpers.RowCount="6"
      local:GridHelpers.StarRows="5"
      local:GridHelpers.ColumnCount="4"
      local:GridHelpers.StarColumns="1,3">

</Grid>

I can also use this to draw a Grid with a dynamic number of rows or columns, or where I don’t know the number of rows/columns beforehand such as an ItemsPanelTemplate for an ItemsControl.


<Grid local:GridHelpers.RowCount="{Binding RowCount}"
      local:GridHelpers.ColumnCount="{Binding ColumnCount}" />

GridHelpers Code

Here is the AttachedProperty code:

public class GridHelpers
{
    #region RowCount Property
    
    /// <summary>
    /// Adds the specified number of Rows to RowDefinitions. 
    /// Default Height is Auto
    /// </summary>
    public static readonly DependencyProperty RowCountProperty =
        DependencyProperty.RegisterAttached(
            "RowCount", typeof(int), typeof(GridHelpers),
            new PropertyMetadata(-1, RowCountChanged));
    
    // Get
    public static int GetRowCount(DependencyObject obj)
    {
        return (int)obj.GetValue(RowCountProperty);
    }
    
    // Set
    public static void SetRowCount(DependencyObject obj, int value)
    {
        obj.SetValue(RowCountProperty, value);
    }
    
    // Change Event - Adds the Rows
    public static void RowCountChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || (int)e.NewValue < 0)
            return;
    
        Grid grid = (Grid)obj;
        grid.RowDefinitions.Clear();
    
        for (int i = 0; i < (int)e.NewValue; i++)
            grid.RowDefinitions.Add(
                new RowDefinition() { Height = GridLength.Auto });
    
        SetStarRows(grid);
    }
    
    #endregion
    
    #region ColumnCount Property
    
    /// <summary>
    /// Adds the specified number of Columns to ColumnDefinitions. 
    /// Default Width is Auto
    /// </summary>
    public static readonly DependencyProperty ColumnCountProperty =
        DependencyProperty.RegisterAttached(
            "ColumnCount", typeof(int), typeof(GridHelpers),
            new PropertyMetadata(-1, ColumnCountChanged));
    
    // Get
    public static int GetColumnCount(DependencyObject obj)
    {
        return (int)obj.GetValue(ColumnCountProperty);
    }
    
    // Set
    public static void SetColumnCount(DependencyObject obj, int value)
    {
        obj.SetValue(ColumnCountProperty, value);
    }
    
    // Change Event - Add the Columns
    public static void ColumnCountChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || (int)e.NewValue < 0)
            return;
    
        Grid grid = (Grid)obj;
        grid.ColumnDefinitions.Clear();
    
        for (int i = 0; i < (int)e.NewValue; i++)
            grid.ColumnDefinitions.Add(
                new ColumnDefinition() { Width = GridLength.Auto });
    
        SetStarColumns(grid);
    }
    
    #endregion
    
    #region StarRows Property
    
    /// <summary>
    /// Makes the specified Row's Height equal to Star. 
    /// Can set on multiple Rows
    /// </summary>
    public static readonly DependencyProperty StarRowsProperty =
        DependencyProperty.RegisterAttached(
            "StarRows", typeof(string), typeof(GridHelpers),
            new PropertyMetadata(string.Empty, StarRowsChanged));
    
    // Get
    public static string GetStarRows(DependencyObject obj)
    {
        return (string)obj.GetValue(StarRowsProperty);
    }
    
    // Set
    public static void SetStarRows(DependencyObject obj, string value)
    {
        obj.SetValue(StarRowsProperty, value);
    }
    
    // Change Event - Makes specified Row's Height equal to Star
    public static void StarRowsChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
            return;
    
        SetStarRows((Grid)obj);
    }
    
    #endregion
    
    #region StarColumns Property
    
    /// <summary>
    /// Makes the specified Column's Width equal to Star. 
    /// Can set on multiple Columns
    /// </summary>
    public static readonly DependencyProperty StarColumnsProperty =
        DependencyProperty.RegisterAttached(
            "StarColumns", typeof(string), typeof(GridHelpers),
            new PropertyMetadata(string.Empty, StarColumnsChanged));
    
    // Get
    public static string GetStarColumns(DependencyObject obj)
    {
        return (string)obj.GetValue(StarColumnsProperty);
    }
    
    // Set
    public static void SetStarColumns(DependencyObject obj, string value)
    {
        obj.SetValue(StarColumnsProperty, value);
    }
    
    // Change Event - Makes specified Column's Width equal to Star
    public static void StarColumnsChanged(
        DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
            return;
    
        SetStarColumns((Grid)obj);
    }
    
    #endregion
    
    private static void SetStarColumns(Grid grid)
    {
        string[] starColumns = 
            GetStarColumns(grid).Split(',');
    
        for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
        {
            if (starColumns.Contains(i.ToString()))
                grid.ColumnDefinitions[i].Width = 
                    new GridLength(1, GridUnitType.Star);
        }
    }
    
    private static void SetStarRows(Grid grid)
    {
        string[] starRows = 
            GetStarRows(grid).Split(',');
    
        for (int i = 0; i < grid.RowDefinitions.Count; i++)
        {
            if (starRows.Contains(i.ToString()))
                grid.RowDefinitions[i].Height = 
                    new GridLength(1, GridUnitType.Star);
        }
    }
}
    

16 Responses to WPF Grid’s Row/Column Count Properties

  1. Very neat! I’ll definitely find this handy, thanks :)

  2. Frank Wu says:

    Cool! I can see part of the code on iPad There is no scroll bar somehow.

    • Rachel says:

      Hi Frank,

      I’ll have to report that bug to WordPress. You should be able to highlight the code and drag your mouse to scroll/copy it since it’s all there. It’s just not showing the scrollbars. You can also use View Source to see it. Thanks for letting me know of that!

  3. x says:

    Wow! Works in design time too! Thanks a lot!

  4. Sam Naseri says:

    Hi,

    Thanks for your great post. I have done the same thing but slightly different. The advantage of your approach is that it suitable for binding.
    I have posted my approach here:
    http://samondotnet.blogspot.com.au/2012/03/wpf-grid-layout-simplified-using-rows.html

  5. Julien says:

    Hi,

    I’m using it in order to add ItemsControl items in the ItemsPanelTemplate as a Grid but the items always appear in the column 0 of my grid even if I bind the Grid.Column property in the ItemContainerStyle to a ColumnCount dp in my VM…

    Any guess why?

    • Julien says:

      i just found a way to do it, i just had to add this part at the end of the ColumnCountChanged event:

      for (int i = 0; i < childCount; i++)
      {
      var child = grid.Children[i] as FrameworkElement;
      Grid.SetColumn(child, i);
      }

      and i used a converter for a binding on a ColumnCount Property in my ViewModel to make all the column equally sized andd tadaa!!! it worked!!!

      thx anyway for all your amazing pieces of code!

  6. Angavar says:

    Hi Rachel, I really admire your work here and on StackOverflow! Keep it up!

  7. I wish I had run across this sooner, I’ve been trying to figure out how to do this for a while. I figured it would be easy to do, I just had no clue where to start. Thanks for posting this.

  8. Stefan says:

    Hi,

    first of all, thanks for the great code! I wanted to use it on my Windows Phone 8 project, but got a strange “Ambiguous match found” error message from the compiler.

    The solution is to rename the *private* methods SetStarRows and SetStarColumns to something else, like DoSetStarRows and DoSetStarColumns. For some reason, the compiler does not like having private methods named exactly like the public methods…

    Hope that helps,
    Stefan

    • Vadim says:

      Rachel, thanks for your blog!
      Stefan, thanks for your solution!
      I’ve got “Ambiguous match found” error message too. It happened when I tried to bind all four properties.

  9. Richard says:

    Hi Rachel,it is an excellent post. I am new on WPF and I would like to know if it is possible to add a GridSplitter between each column for example:
    column | gridsplitter | columns
    column | gridsplitter | column | gridsplitter | column

    If you can help me :)

    Thanks,
    Richard

    • Rachel says:

      Hi Richard, I don’t think that can be easily done with a simple Attached Property.

      The problem is you’d need to insert the GridSplitter between the ContentPresenter objects, which means you’d really need (ColumnCount * 2) – 1 Columns to account for the GridSplitters, and you’ll have to adjust the Grid.Column property of all child objects except the first one to place them at Grid.Column * 2, or they’ll overlap the GridSplitter columns.

      Such complexity is probably better suited for a new custom panel instead of an Attached Property.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 96 other followers

%d bloggers like this: