Alignment grid in Universal WinRT apps

Alignment grid in Universal WinRT apps

Alignment. So important and yet often forgotten by so many developers, especially the ones who don't work with designers. Remember Grid? It's here to stay with Windows Phone 8.1 and also with Universal apps. And yet, if you create a Silverlight Windows Phone 8.1 project, you get the alignment grid out of the box, but not in Universal apps. What if there was a Universal solution?

As I mentioned, you get the alignment grid in Silverlight Windows Phone apps in form of AlignmentGrid.png image file, but not when you create WinRT Windows Phone apps. Considering that the Silverlight version is nothing more than a .png file with a bunch of red squares, it might be reusable in WinRT.

But, there's a better idea. There's a MetroGridHelper (I wrote about performance of it earlier) which is for WinRT, but what if we could make it universal?

Universal MetroGridHelper (beta!)

Disclaimer - I am not 100% sure about the margin sizes. They may be different, I am still looking for official documentation info on this. I am putting this out there, so if anyone knows for sure, let's improve on this!

MetroGridHelper is a grid helper for WinRT by Mark Monster (and initially Jeff Wilcox), and Mark he blogged about it here. This can be easily modified to make it Universal, so that it works on both Windows 8.1 and Windows Phone 8.1 WinRT apps. Here's my suggestion.

The only part of the code which needs to change is the PrepareGrid method which does all the heavy lifting.

PrepareGrid method:

private static void PrepareGrid(Frame frame, Grid parent)  
{
    var brush = new SolidColorBrush(_color);

    _grid = new Grid { IsHitTestVisible = false };

    // To support both orientations, unfortunately more visuals need to
    // be used. An alternate implementation would be to react to the
    // orientation change event and re-draw/remove squares.
    double width = frame.ActualWidth;
    double height = frame.ActualHeight;
    double max = Math.Max(width, height);

    #if WINDOWS_APP

    const double strokeWidth = 2.0;

    var horizontalLine = new Line
    {
        IsHitTestVisible = false,
        Stroke = brush,
        X1 = 0,
        X2 = max,
        Y1 = 100 + (strokeWidth / 2),
        Y2 = 100 + (strokeWidth / 2),
        StrokeThickness = strokeWidth,
    };
    _grid.Children.Add(horizontalLine);
    _shapes.Add(horizontalLine);
    var horizontalLine2 = new Line
    {
        IsHitTestVisible = false,
        Stroke = brush,
        X1 = 0,
        X2 = max,
        Y1 = 140 + (strokeWidth / 2),
        Y2 = 140 + (strokeWidth / 2),
        StrokeThickness = strokeWidth,
    };
    _grid.Children.Add(horizontalLine2);
    _shapes.Add(horizontalLine2);

    var verticalLine = new Line
    {
        IsHitTestVisible = false,
        Stroke = brush,
        X1 = 120 - (strokeWidth / 2),
        X2 = 120 - (strokeWidth / 2),
        Y1 = 0,
        Y2 = max,
        StrokeThickness = strokeWidth,
    };
    _grid.Children.Add(verticalLine);
    _shapes.Add(verticalLine);

    var horizontalBottomLine = new Line
    {
        IsHitTestVisible = false,
        Stroke = brush,
        X1 = 0,
        X2 = max,
        Y1 = height - 130 + (strokeWidth / 2),
        Y2 = height - 130 + (strokeWidth / 2),
        StrokeThickness = strokeWidth,
    };
    _grid.Children.Add(horizontalBottomLine);
    _shapes.Add(horizontalBottomLine);
    var horizontalBottomLine2 = new Line
    {
        IsHitTestVisible = false,
        Stroke = brush,
        X1 = 0,
        X2 = max,
        Y1 = height - 50 + (strokeWidth / 2),
        Y2 = height - 50 + (strokeWidth / 2),
        StrokeThickness = strokeWidth,
    };
    _grid.Children.Add(horizontalBottomLine2);
    _shapes.Add(horizontalBottomLine2);

    #endif

    double tileWidth = 20;
    double tileHeight = 20;

    #if WINDOWS_PHONE_APP
    double x = 19.2;
    double y = 38.4;
    double block = 29.6;
    #else
    double x = 120;
    double y = 140;
    double block = 40;
    #endif

    for (; x < /*width*/ max; x += block)
    {
        #if WINDOWS_PHONE_APP
        y = 38.4;
        #else
        y = 140;
        #endif

        for (; y < /*height*/ max; y += block)
        {
            var rect = new Rectangle
            {
                Width = tileWidth,
                Height = tileHeight,
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left,
                Margin = new Thickness(x, y, 0, 0),
                IsHitTestVisible = false,
                Fill = brush,
            };
            _grid.Children.Add(rect);
            _shapes.Add(rect);
        }
    }

    _grid.Visibility = _visible ? Visibility.Visible : Visibility.Collapsed;
    _grid.Opacity = _opacity;

    // For performance reasons a single surface should ideally be used
    // for the grid.
    _grid.CacheMode = new BitmapCache();

    // Places the grid into the visual tree. It is never removed once
    // being added.
    parent.Children.Add(_grid);
}

The whole class is available on GitHub.

https://github.com/igrali/Universal-MetroGridHelper

This time, the method checks if we're running Windows Phone 8.1 app or Windows 8.1 app, and makes Grid dimensions according to that.

Why am I calling this a beta? Because I am not absolutely sure about the margins. I was told that everything is now 80% of what it was before, so if we look at the code from Jeff Wilcox, these numbers seem to be right, my scaled numbers seem to be OK. Are they? Let me know in the comments, thanks!

When you add it to WinRT app, you get:
Windows 8.1 WinRT alignment grid

and on Windows Phone 8.1
Alignment grid for Windows Phone 8.1 WinRT

Igor Ralic

igor ralic

View Comments
Microsoft Certified Solutions Developer: Windows Store Apps in C#