Mister Goodcat

Peter's home of all things life

Sunday, 1/2/2011 9:35 AM
by Peter Kuhn
4 Comments

A Simplified Grid Markup Reloaded

Sunday, 1/2/2011 9:35 AM by Peter Kuhn | 4 Comments

This morning I had to type a lot of grid definitions manually (would've been so much easier with Blend...), and I remembered a post by Colin Eberhardt I had read some time ago, about a simplified grid markup for Silverlight (and WPF). The idea behind it is nice, and when I had to type something like this:

<StackPanel Grid.Row="2"
            Grid.Column="1"
            Grid.ColumnSpan="2">

... for the fifth time, I took a little break and extended Colin's approach to include the above declarations.

A word of caution

Neither Colin's approach nor what I describe in the following fully works with Blend. The designer shows the result correctly, but you're not able to edit these grids other than directly in XAML. So I see this more like a fun snippet, as it probably is only useful for you if you don't plan on using Blend.

A Grid Cell attached property

As extension to Colin's approach, I've added an attached property named "Cell":

/// <summary>
/// Identifies the Cell attached property
/// </summary>
public static readonly DependencyProperty CellProperty =
    DependencyProperty.RegisterAttached("Cell", typeof(string), typeof(GridUtils),
        new PropertyMetadata("", new PropertyChangedCallback(OnCellPropertyChanged)));

/// <summary>
/// Gets the value of the Cell property
/// </summary>
public static string GetCell(DependencyObject d)
{
    return (string)d.GetValue(CellProperty);
}

/// <summary>
/// Sets the value of the GridLocation property
/// </summary>
public static void SetCell(DependencyObject d, string value)
{
    d.SetValue(CellProperty, value);
}

The interesting part is the "OnCellPropertyChanged" method, where all the magic happens. The value of the attached property is split, and each of the single values is used to set the respective "Grid" property for the associated object:

/// <summary>
/// Handles the property changed event for the Cell property, setting the
/// Row, Column, RowSpan and ColumnSpan values on the element which this property is attached to.
/// </summary>
private static void OnCellPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // parse the location
    string locationDefs = e.NewValue as string;
    var locationDefArray = locationDefs.Split(',');

    for (int i = 0; i < locationDefArray.Length; i++)
    {
        string locationDef = locationDefArray[i].Trim();

        // if a value is missing, use zero
        if (string.IsNullOrEmpty(locationDef))
        {
            locationDef = "0";
        }

        int locationValue;
        if (int.TryParse(locationDef, out locationValue))
        {
            // the order is: row, column, rowspan, columnspan
            switch (i)
            {
                case 0:
                    d.SetValue(Grid.RowProperty, locationValue);
                    break;
                case 1:
                    d.SetValue(Grid.ColumnProperty, locationValue);
                    break;
                case 2:
                    d.SetValue(Grid.RowSpanProperty, locationValue > 0 ? locationValue : 1);
                    break;
                case 3:
                    d.SetValue(Grid.ColumnSpanProperty, locationValue > 0 ? locationValue : 1);
                    break;
            }
        }
    }
}


How to use it

With that, my initial sample of this post becomes something like:

<StackPanel local:GridUtils.Cell="2,1,2">

One can argue if this is simpler than the original notation, as you have to remember the order of values (row, column, row span, column span), but if you type a lot of grid definitions manually, it might save you quite some time eventually.

Here you can download the complete source code; it also includes Colin's original sample which I've reworked to use the above syntax. Note that the source code also includes the proposed fix for a problem with the original code someone pointed out in the comments of Colin's post. Have fun ;).

SimplifiedGridSilverlight.zip (7.84 kb)

Comments (4) -

Good post!

Probably System.Windows.Int32Rect type would be useful here. It has its own type converter so you don't need to parse the string value. Also it might be more Blend-friendly.

Hi Vladislav. Thank you for your comment, I appreciate it!

Int32Rect is not available in Silverlight. However, even with a rect type one would have to make the necessary adjustments for skipped values and the "span" properties.

The problem with Blend is that although the designer is able to handle this approach visually, the dynamically created rows and columns (from Colin's original source) are not recognized and therefore cannot be edited. The values of the "cell" extension apparently can be changed, but these changes are not persisted. The next time the control is opened or refreshed the original values are restored.

Both of which is logical as Blend doesn't know about the custom attached properties and how to handle them; I doubt there is a solution that'll work with Blend at all.

Nice addition to the simplified Grid markup. I am sure I will use this in the future.

Regards, Colin E.

Colin, thank you for that comment; and also thank your for the initial idea to do this, very cool.

Comments are closed