dotNed Podcast–Windows 8 and Windows Phone 8

A couple of weeks ago Maurice de Beijer recorded a podcast interview with me for the dotNed usergroup. It’s about Windows 8 and Windows Phone 8, but completely in Dutch. If you’re interested but haven’t listened to it yet, give it a try.

dotNed Podcast - Windows 8 en Windows Phone 8 met Mark Monster

TechDays 2013 - Make your Windows 8 app stand out

Techdays2013

After attending the Dutch TechDays for quite a couple of years, only last year I wasn’t able to attend. But I’m back this year, not as an attendee but as a speaker. It’s a dream coming true!

The session will be in Dutch, so only the Dutch description makes sense, sorry. You can find my session here.

Make your Windows 8 app stand out

  • Windows 8 heeft een enorm bereik en kan veel voor je app betekenen. Hoe zorg je ervoor dat je app opvalt binnen de zee van applicaties in de Store? Deze sessie gaat in op hoe je optimaal gebruik kunt maken van de mogelijkheden die Windows 8 biedt, zoals:
    - Full, Snap en Fill views
    - Share contract
    - Search contract
    - Play to contract
    - Semantic zoom
    - Tiles en toast
    - Offline mode
    - Touch, Mouse en Keyboard
    Maak ook gebruik van het potentieel van de Windows Store, door middel van reviews, en marketing materiaal en verschillende verdienmodellen zoals trials, in-app purchases of advertenties.

MetroGridHelper for WinRT v1.1–Enhanced to work with Snapped and Portrait mode

Because the left-margin is different for Snapped and Portrait mode, the previous version of MetroGridHelper for WinRT was not optimal. It didn’t support a different left margins at all. Don’t be sad, v1.1 supports Filled (120px), Snapped(20px), Fullscreen Landscape(100px) and Fullscreen Portrait(100px).

You can find the updated version on NuGet.

If you’re new to the MetroGridHelper for winrt you can execute the below command in the NuGet Package Manager Console:

PM> Install-Package WinRT.MetroGridHelper

If you have already installed the previous version you can also do the Update-Package command via the Package Manager Console:

PM> Update-Package WinRT.MetroGridHelper

Ah you want to see how it looks like?

screenshot_07252012_204328

screenshot_08032012_224037screenshot_08032012_223554

The code? Yes of course.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.Graphics.Display;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace MC.MetroGridHelper
{
    /// <summary>
    /// A utility class that overlays a designer-friendly grid on top of the
    /// application frame, for use similar to the performance counters in
    /// App.xaml.cs. The color and opacity are configurable. The grid contains
    /// a number of squares that are 24x24, offset with 12px gutters, and all
    /// 24px away from the edge of the device.
    /// </summary>
    public static class MetroGridHelper
    {
        private static bool _visible;
        private static double _opacity = 0.15;
        private static Color _color = Colors.Red;
        private static List<Shape> _shapes;
        private static Grid _grid;
        private static bool _eventsAttached;
        private static double _width;
        private static double _height;

        /// <summary>
        /// Gets or sets a value indicating whether the designer grid is
        /// visible on top of the application's frame.
        /// </summary>
        public static bool IsVisible
        {
            get { return _visible; }
            set
            {
                _visible = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Gets or sets the color to use for the grid's squares.
        /// </summary>
        public static Color Color
        {
            get { return _color; }
            set
            {
                _color = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the opacity for the grid's squares.
        /// </summary>
        public static double Opacity
        {
            get { return _opacity; }
            set
            {
                _opacity = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Updates the grid (if it already has been created) or initializes it
        /// otherwise.
        /// </summary>
        private static void UpdateGrid()
        {
            if (_shapes != null)
            {
                var brush = new SolidColorBrush(_color);
                foreach (Shape square in _shapes)
                {
                    square.Fill = brush;
                }
                if (_grid != null)
                {
                    _grid.Visibility = _visible ? Visibility.Visible : Visibility.Collapsed;
                    _grid.Opacity = _opacity;
                }
            }
            else
            {
                BuildGrid();
            }
        }

        /// <summary>
        /// Builds the grid.
        /// </summary>
        private static async void BuildGrid()
        {
            _shapes = new List<Shape>();

            var frame = Window.Current.Content as Frame;
            if (frame == null || VisualTreeHelper.GetChildrenCount(frame) == 0)
            {
                await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, BuildGrid);
                return;
            }

            DependencyObject child = VisualTreeHelper.GetChild(frame, 0);
            var childAsBorder = child as Border;
            var childAsGrid = child as Grid;
            if (childAsBorder != null)
            {
                // Not a pretty way to control the root visual, but I did not
                // want to implement using a popup.
                UIElement content = childAsBorder.Child;
                if (content == null)
                {
                    await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, BuildGrid);
                    return;
                }
                childAsBorder.Child = null;
                await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                                                         () =>
                                                             {
                                                                 var newGrid = new Grid();
                                                                 childAsBorder.Child = newGrid;
                                                                 newGrid.Children.Add(content);
                                                                 PrepareGrid(newGrid);
                                                                 AttachEvents();
                                                             });
            }
            else if (childAsGrid != null)
            {
                PrepareGrid(childAsGrid);
                AttachEvents();
            }
            else
            {
                Debug.WriteLine("Dear developer:");
                Debug.WriteLine("Unfortunately the design overlay feature requires that the root frame visual");
                Debug.WriteLine("be a Border or a Grid. So the overlay grid just isn't going to happen.");
            }
        }

        private static void AttachEvents()
        {
            if (_eventsAttached)
                return;
            Window.Current.SizeChanged += (s, e) => BuildGridForCurrentView();
            DisplayProperties.OrientationChanged += s => BuildGridForCurrentView(); 
            _eventsAttached = true;
        }

        public static void CreateGrid()
        {
            IsVisible = true;
        }

        /// <summary>
        /// Does the actual work of preparing the grid once the parent frame is
        /// in the visual tree and we have a Grid instance to work with for
        /// placing the chilren.
        /// </summary>
        /// <param name="parent">The parent grid to insert the sub-grid into.</param>
        private static void PrepareGrid(Grid parent)
        {
            if (_grid == null)
            {
                _grid = new Grid
                            {
                                IsHitTestVisible = false,
                                Visibility = _visible ? Visibility.Visible : Visibility.Collapsed,
                                Opacity = _opacity,
                                CacheMode = new BitmapCache()
                            };
                parent.Children.Add(_grid);
            }
            BuildGridForCurrentView();
        }

        public static int GetMarginForCurrentView()
        {
            bool isSnapped = ApplicationView.Value == ApplicationViewState.Snapped;
            bool isPortrait = DisplayProperties.CurrentOrientation == DisplayOrientations.Portrait ||
                              DisplayProperties.CurrentOrientation == DisplayOrientations.PortraitFlipped;
            if (isSnapped)
                return 20;
            if (isPortrait)
                return 100;
            return 120;
        }

        public static void BuildGridForCurrentView()
        {
            // Places the grid into the visual tree. It is never removed once
            // being added.
            var frame = Window.Current.Content as Frame;
            if (frame == null)
                return;

            _width=Math.Max(_width, frame.ActualWidth);
            _height=Math.Max(_height, frame.ActualHeight);
            _grid.Children.Clear();
            _shapes.Clear();
            IEnumerable<Shape> shapes = GetGridShapesForMargin(_width, _height, GetMarginForCurrentView());
            foreach (Shape shape in shapes)
            {
                _grid.Children.Add(shape);
                _shapes.Add(shape);
            }
        }

        public static IEnumerable<Shape> GetGridShapesForMargin(double width, double height, int margin)
        {
            // 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.
            var brush = new SolidColorBrush(_color);
            double max = Math.Max(width, height);

            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,
                                     };
            yield return horizontalLine;
            var horizontalLine2 = new Line
                                      {
                                          IsHitTestVisible = false,
                                          Stroke = brush,
                                          X1 = 0,
                                          X2 = max,
                                          Y1 = 140 + (strokeWidth/2),
                                          Y2 = 140 + (strokeWidth/2),
                                          StrokeThickness = strokeWidth,
                                      };
            yield return horizontalLine2;

            var verticalLine = new Line
                                   {
                                       IsHitTestVisible = false,
                                       Stroke = brush,
                                       X1 = margin - (strokeWidth / 2),
                                       X2 = margin - (strokeWidth / 2),
                                       Y1 = 0,
                                       Y2 = max,
                                       StrokeThickness = strokeWidth,
                                   };
            yield return 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,
                                           };
            _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,
                                            };
            _shapes.Add(horizontalBottomLine2);
            yield return horizontalBottomLine2;

            const int tileHeight = 20;

            for (int x = margin; x < /*width*/ max; x += (tileHeight*2))
            {
                for (int y = 140; y < /*height*/ max; y += (tileHeight*2))
                {
                    var rect = new Rectangle
                                   {
                                       Width = tileHeight,
                                       Height = tileHeight,
                                       VerticalAlignment = VerticalAlignment.Top,
                                       HorizontalAlignment = HorizontalAlignment.Left,
                                       Margin = new Thickness(x, y, 0, 0),
                                       IsHitTestVisible = false,
                                       Fill = brush,
                                   };
                    yield return rect;
                }
            }
        }
    }
}

MetroGridHelper for WinRT, a helper to get the alignment right

It’s already more than a year ago that Jeff Wilcox wrote the MetroGridHelper for Windows Phone. I’ve been using it a lot since then. But we now have the option to build WinRT applications, sadly without the MetroGridHelper.

Wait no more, I ported the code from Jeff and also modified it to fit the metro guidelines that exist for WinRT applications. I’ve had some help from Martin Tirion and Matthijs Hoekstra, both working as Evangelist for Microsoft in The Netherlands.

The result is something that helps me during the design of the Windows 8, WinRT, apps.

You can simply install the NuGet Package. Through the console like this:

PM> Install-Package WinRT.MetroGridHelper

And inside your App.xaml.cs you can add the following to the end of the OnLaunched method.

// Place the frame in the current Window and ensure that it is active
Window.Current.Content = rootFrame;
Window.Current.Activate();

//Only when the debugger is attached
if (System.Diagnostics.Debugger.IsAttached)
{
    //Display the metro grid helper
    MC.MetroGridHelper.MetroGridHelper.CreateGrid();
}

screenshot_07252012_204308

screenshot_07252012_204316

screenshot_07252012_204328

The current version only works for WinRT apps that are written in Xaml, though I’m investigating on how to create something similar within HTML5+JS. If you have suggestions for this version please let me know, and I will adjust the MetroGridHelper if they are helpful.

To be complete, you can use the below source code as an alternative to the NuGet Package.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;

namespace MC.MetroGridHelper
{
    /// <summary>
    /// A utility class that overlays a designer-friendly grid on top of the
    /// application frame, for use similar to the performance counters in
    /// App.xaml.cs. The color and opacity are configurable. The grid contains
    /// a number of squares that are 24x24, offset with 12px gutters, and all
    /// 24px away from the edge of the device.
    /// </summary>
    public static class MetroGridHelper
    {
        private static bool _visible;
        private static double _opacity = 0.15;
        private static Color _color = Colors.Red;
        private static List<Shape> _shapes;
        private static Grid _grid;

        /// <summary>
        /// Gets or sets a value indicating whether the designer grid is
        /// visible on top of the application's frame.
        /// </summary>
        public static bool IsVisible
        {
            get { return _visible; }
            set
            {
                _visible = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Gets or sets the color to use for the grid's squares.
        /// </summary>
        public static Color Color
        {
            get { return _color; }
            set
            {
                _color = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating the opacity for the grid's squares.
        /// </summary>
        public static double Opacity
        {
            get { return _opacity; }
            set
            {
                _opacity = value;
                UpdateGrid();
            }
        }

        /// <summary>
        /// Updates the grid (if it already has been created) or initializes it
        /// otherwise.
        /// </summary>
        private static void UpdateGrid()
        {
            if (_shapes != null)
            {
                var brush = new SolidColorBrush(_color);
                foreach (Shape square in _shapes)
                {
                    square.Fill = brush;
                }
                if (_grid != null)
                {
                    _grid.Visibility = _visible ? Visibility.Visible : Visibility.Collapsed;
                    _grid.Opacity = _opacity;
                }
            }
            else
            {
                BuildGrid();
            }
        }

        /// <summary>
        /// Builds the grid.
        /// </summary>
        private static async void BuildGrid()
        {
            _shapes = new List<Shape>();

            var frame = Window.Current.Content as Frame;
            if (frame == null || VisualTreeHelper.GetChildrenCount(frame) == 0)
            {
                await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, BuildGrid);
                return;
            }

            DependencyObject child = VisualTreeHelper.GetChild(frame, 0);
            var childAsBorder = child as Border;
            var childAsGrid = child as Grid;
            if (childAsBorder != null)
            {
                // Not a pretty way to control the root visual, but I did not
                // want to implement using a popup.
                UIElement content = childAsBorder.Child;
                if (content == null)
                {
                    await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, BuildGrid);
                    return;
                }
                childAsBorder.Child = null;
                await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                                                                                            {
                                                                                                var newGrid = new Grid();
                                                                                                childAsBorder.Child =
                                                                                                    newGrid;
                                                                                                newGrid.Children.Add(
                                                                                                    content);
                                                                                                PrepareGrid(frame,
                                                                                                            newGrid);
                                                                                            });
            }
            else if (childAsGrid != null)
            {
                PrepareGrid(frame, childAsGrid);
            }
            else
            {
                Debug.WriteLine("Dear developer:");
                Debug.WriteLine("Unfortunately the design overlay feature requires that the root frame visual");
                Debug.WriteLine("be a Border or a Grid. So the overlay grid just isn't going to happen.");
            }
        }

        public static void CreateGrid()
        {
            IsVisible = true;
        }

        /// <summary>
        /// Does the actual work of preparing the grid once the parent frame is
        /// in the visual tree and we have a Grid instance to work with for
        /// placing the chilren.
        /// </summary>
        /// <param name="frame">The phone application frame.</param>
        /// <param name="parent">The parent grid to insert the sub-grid into.</param>
        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);

            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);

            const int tileHeight = 20;

            for (int x = 120; x < /*width*/ max; x += (tileHeight*2))
            {
                for (int y = 140; y < /*height*/ max; y += (tileHeight*2))
                {
                    var rect = new Rectangle
                                   {
                                       Width = tileHeight,
                                       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);
        }
    }
}