Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / XAML

FloTiles: An Innovative Metro Game

Rate me:
Please Sign up or sign in to vote.
5.00/5 (11 votes)
2 Dec 2012CPOL13 min read 28.8K   8   11
A Windows 8 Metro App showcasing an innovative jigsaw puzzle game while utilising the powerful features of an Ultrabook.

Image 1

Download FloTiles from Windows Store

Table of Contents

Introduction

FloTiles is an innovative jigsaw puzzle game which brings in a few rules of its own and makes the game unique and more exciting.

Update

The last few weeks were pretty hectic shaping up the app from concept to a practical Windows Store App. I completed the first version of the app early morning on the 20th of Nov and uploaded it to the Windows Store. I had initially thought that Nov 21st was the last date by which the app should be uploaded and certified by Microsoft. I had to cut down a few features just to accommodate the deadline. Later I found out that the judges will be checking the app in the Windows store on Dec 1st. That gave me a few more days to slip in the features which I had chopped and would also give me ample time to polish my app. So I brought back to the drawing board all the features which I had chopped off and stared my work on them. On 28th November, I uploaded the first revision of the app with all the planned features incorporated. Microsoft surprised me by certifying and approving the app in about 10 hrs. This gave me the motivation to plan for another quick update to polish my app further. The second revision was uploaded on 29th November early morning and it has passed the certification process and is available in the Windows Store. I have been concentrating on finishing my article since then.

Note

I will not be uploading the entire code in this article. Instead, I will be posting code snippets of the critical components of the app.

Background

FloTiles draws its inspiration from the game FlipSaw, which was my first Windows 8 Metro App. In FlipSaw the user plays with two jigsaw puzzles simultaneously, whereas in FloTiles the user plays with a single jigsaw puzzle. The USP of FloTiles lies in the way the user has to arrange the tiles to obtain the final image.

Another control which acts as one of the core components in FloTiles is an updated version of FluidWrapPanel. FluidWrapPanel is one of the components of my WPFSpark project. This panel is responsible for arranging the tiles and handle user interaction. More detail on this in the later sections.

FloTiles Demystified

Image 2

The Concept

A jigsaw puzzle has the following concept: An image (or a number series) is split into several smaller tiles to form a grid of m x n rows which are then jumbled and one of the tile is removed to allow the movement of tiles one at a time until the original image is reconstructed. Only the tiles adjacent to the empty space can be moved (either horizontally or vertically).

FloTiles builds upon this simple concept. In FloTiles too, the image (or a number series) is split into several smaller tiles to form a grid of m x n rows which are then jumbled. The similarity with a jigsaw puzzle stops here. No tiles are removed so there is no empty space to move adjacent tiles. Instead, the user can drag a tile from any location in the grid and place it in any other location. The other tiles also readjust automatically to accommodate the tile in its new position. Now it may look like the player now has a lot of freedom and the game has become easier. Believe me, it is not. Smile | <img src=

The technique to keep the game interesting lies in the arrangement of the tiles which I have termed FloPattern.

FloPattern

FloPattern depicts the order of arrangement of the tiles in the GameBoard. Let me give an example. Consider 5 people (A, B, C, D & E) standing in a queue in a straight line(in the same order). The index of each person is the position of a person within the queue. So in this case the indices are: A - 1, B - 2, C - 3, D - 4, E - 5.

Image 4

Now lets say, the person B moves out of the queue. So B no longer has a valid index. However the index occupied by B is not being used by any other person. So the person with the next higher index (person C) moves in B's place and his index is readjusted to B's old index. Now person D moves in to fill person C's place. This continues till the end of the queue. So now the new indices are: A- 1, C - 2, D - 3, E - 4.

Image 5

Now person B moves back into the queue and takes its position after person C. So person B takes the index of person D. In order to accommodate person B in the queue, all the persons, after person B, in the queue move 1 index down. So now the indices are: A - 1, C - 2, B - 3, D - 4, E - 5.

Image 6

Consider the same scenario if the people were standing in a circle or in a zig-zag manner, but still following the queue system. If you replace the people with the tiles that make up a larger picture, then you get the core essence of the concept of the game.

Image 7

So, FloPattern defines the order of the indices within the gameboard. These indices ultimately define the order of arrangement of the tiles. The index of each tile is termed as FloIndex.

FloTiles Workflow

Image 8

Each of the FloPattern represents a Challenge. Currently there are 10 Challenges defined in the game. They are:

  • Straight Shot Challenge - I
  • Straight Shot Challenge - II
  • Straight Shot Challenge - III
  • Straight Shot Challenge - IV
  • Zig-Zag Challenge - I
  • Zig-Zag Challenge - II
  • Zig-Zag Challenge - III
  • Zig-Zag Challenge - IV
  • Spiral Challenge - I
  • Spiral Challenge - II

Image 9

Each Challenge contains 9 Levels representing a GameBoard of a particular size. In these Levels :

  • The first sublevel in each Level will be a trial Level of size 5x5. Also the tiles will not contain sections of the images. Instead they will contain numbers from 1 to 25. The user can play this Level to get a feel of how the FloPattern behaves whenever the user moves a tile. This will also help the user to build a strategy to solve the puzzle in later Levels. However it is optional for the user to play this Level.
  • The next 8 Levels will be having GameBoard sizes ranging from 3x3 to 10x10.

Initially all the Challenges and Levels (except the practice Level and the first Level in the Straight Shot Challenge - I) will be locked. Successful completion of a Level will unlock the next Level in the Challenge.

Successful completion of the first five Levels of any Challenge will unlock the next Challenge. The last three Levels (of sizes 8x8, 9x9 and 10x10) are of high complexity and may require considerable time and patience from the user. Thus they are optional for the user to complete and they can be skipped to proceed to the next Challenge. However the user is free to complete the last three Levels first before proceeding to the next Challenge.

Image 10

Image 11

During the game, the user is presented with 4 options in the Bottom AppBar which can be accessed by swiping from the bottom of the screen upwards. The options are:

  • Pause/Resume: Allows the user to pause or resume the current game Level.
  • Reset: Allows the user to play the current game Level again after restoring the tiles to its initial shuffled position.
  • Show/Hide Pattern: Shows/Hides the FloPattern used for the current game Level i.e. the order of arrangment of the tiles in the GameBoard. Using this feature will automatically add 30 seconds to the total duration. 
  • Solve/Resume Game: Clicking on this button will solve the FloTile puzzle to show the original image. Clicking on the button again with restore the tiles to the last played postion. Using this feature will automatically add 20 seconds to the total duration
  • Image 12

    In addition to the above challenges, the user can also create custom FloTile challenges using images from the user’s machine or by using the webcam to take a picture. Only the challenges completed or currently being played by the user will be available to create custom FloTile Challenge. The remaining challenges will be locked. They will be unlocked once the user has unlocked them by playing the Levels of the Challenge in the Main Menu.

    Image 13

    While playing each Level, the time taken and the number of moves made will be displayed. At the completion of each Level, these statistics will be persisted. These statistics will be displayed in the Scoreboard contents will be determined. It will display the number of moves and the duration taken to complete a Level along with the star rating for that Level. If a Level has not been played by the user yet, then it will display the lock icon.

    Reward System

    Image 14

    Each Level completion will be graded based on an algorithm which will take the number of moves, the time taken (to solve the Level) and the GameBoard size into consideration. Based on the output of the algorithm, the user will be awarded points and a rating of 1 to 3 stars for that Level.

    FloTiles Modules

    Image 15

    Common

    The Common module consists of the common definitions, enums, constants, structures etc which is used throughout the app. Here are a few of them:

    PatternType

    This enum defines the various kinds of FloPatterns available.

    C#
    public enum PatternType : int
    {
    	None = 0,
    	StraightTopLeft = 1,
    	StraightTopRight = 2,
    	StraightBottomRight = 3,
    	StraightBottomLeft = 4,
    	ZigZagTopLeft = 5,
    	ZigZagTopRight = 6,
    	ZigZagBottomRight = 7,
    	ZigZagBottomLeft = 8,
    	SpiralIn = 9,
    	SpiralOut = 10,
    }

    GameLevelType

    This enum defines the various Levels available.

    C#
    public enum GameLevelType : int
    {
    	None = 0,
    	Level0 = 1,
    	Level1 = 2,
    	Level2 = 3,
    	Level3 = 4,
    	Level4 = 5,
    	Level5 = 6,
    	Level6 = 7,
    	Level7 = 8,
    	Level8 = 9
    }

    LevelStatusType

    This enum defines the different states of a Level within a Challenge.

    C#
    public enum LevelStatusType : int
    {
    	None = 0,
    	Locked = 1,
    	Playing = 2,
    	OneStar = 3,
    	TwoStars = 4,
    	ThreeStars = 5
    }

    GameStatusType

    This enum defines the different states of the Game within a Level.

    C#
    public enum GameStatusType : int
    {
    	None = 0,
    	Initializing = 1,
    	Loading = 2,
    	TapToPlay = 3,
    	Playing = 4,
    	Paused = 5,
    	Solved = 6,
    	GameOver = 7,
    	PracticeGameOver = 8,
    	CustomGameOver = 9
    }

    GameOverType

    This enum defines the different types of GameOver states for a Level.

    C#
    public enum GameOverType : int
    {
    	None = 0,
    	LevelComplete = 1,
    	NextChallengeUnlocked = 2,
    	LevelCompletePostNCUnlock = 3,
    	ChallengeComplete = 4
    }

    Cell

    This class defines the structure used to identify a tile location within the GameBoard.

    C#
    public struct Cell
    {
    	public int Row { get; set; }
    	public int Column { get; set; }
    
    	public static bool operator ==(Cell a, Cell b)
    	{
    		return ((a.Row == b.Row) && (a.Column == b.Column));
    	}
    
    	public static bool operator !=(Cell a, Cell b)
    	{
    		return ((a.Row != b.Row) || (a.Column != b.Column));
    	}
    
    	public override bool Equals(object obj)
    	{
    		try
    		{
    			if (obj is Cell)
    			{
    				return this == (Cell)obj;
    			}
    		}
    		catch (Exception)
    		{
    
    		}
    
    		return false;
    	}
    
    	public override int GetHashCode()
    	{
    		// Multiply the row by a prime (101) and add the column value to it
    		return (this.Row * 101) + this.Column;
    	}
    }

    Cortex

    This module contains the core classes of FloTiles which are used to create the game based on the Challenge and the Level selected. 

    GamePanel

    The GamePanel class represents the GameBoard of the FloTile game. This component is used to host the GameTiles. It allows the user to drag and drop a tile from one location to another. It is an updated version of the FluidWrapPanel which is one of the components of my WPFSpark project. The current code incorporates the asynchronous model provided in WinRT framework.

    C#
    public class GamePanel : Panel
    {
    	#region Constants
    
    	private const double NORMAL_SCALE = 1.0d;
    	private const double DRAG_SCALE_DEFAULT = 1.3d;
    	private const double NORMAL_OPACITY = 1.0d;
    	private const double DRAG_OPACITY_DEFAULT = 0.6d;
    	private const double OPACITY_MIN = 0.1d;
    	private const Int32 Z_INDEX_NORMAL = 0;
    	private const Int32 Z_INDEX_INTERMEDIATE = 1;
    	private const Int32 Z_INDEX_DRAG = 10;
    	private static TimeSpan DEFAULT_ANIMATION_TIME_WITHOUT_EASING = TimeSpan.FromMilliseconds(200);
    	private static TimeSpan DEFAULT_ANIMATION_TIME_WITH_EASING = TimeSpan.FromMilliseconds(300);
    	private static TimeSpan FIRST_TIME_ANIMATION_DURATION = TimeSpan.FromMilliseconds(220);
    
    	#endregion
    
    	#region Delegates and Events
    
    	public event EventHandler InitializationCompleted;
    	public event EventHandler TileMoved;
    	public event EventHandler GameOver;
    
    	#endregion
    
    	#region Fields
    
    	Point _dragStartPoint = new Point();
    	UIElement _dragTile = null;
    	UIElement _lastDragElement = null;
    	List<UIElement> _boardTiles = null;
    	List<UIElement> _intermediateTiles = null;
    	GamePanelHelper _gamePanelHelper = null;
    	double _itemWidth = 0.0;
    	double _itemHeight = 0.0;
    	double _animDelay = 0.0;
    	int _dragStartIndex = -1;
    	int _dragEndIndex = -1;
    
    	#endregion
    
    	#region Dependency Properties
    
    	...
    
    	#endregion
    
    	#region Overrides
    
    	/// <summary>
    	/// Override for the Measure Layout Phase
    	/// </summary>
    	/// <param name="availableSize">Available Size</param>
    	/// <returns>Size required by the panel</returns>
    	protected override Size MeasureOverride(Size availableSize)
    	{
    		Size availableItemSize = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
    
    		foreach (UIElement child in Children.Where(c => c != null))
    		{
    			// Ask the child how much size it needs
    			child.Measure(availableItemSize);
    		}
    
    		if ((MaxRows > 0) && (MaxCols > 0))
    		{
    			Size resultSize = new Size(MaxCols * _itemWidth, MaxRows * _itemHeight);
    
    			return resultSize;
    		}
    
    		return availableSize;
    	}
    
    	/// <summary>
    	/// Override for the Arrange Layout Phase
    	/// </summary>
    	/// <param name="finalSize">Available size provided by the FluidGameBoard</param>
    	/// <returns>Size taken up by the Panel</returns>
    	protected override Size ArrangeOverride(Size finalSize)
    	{
    		UpdateFluidLayout();
    
    		return finalSize;
    	}
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public GamePanel()
    	{
    		_gamePanelHelper = new GamePanelHelper();
    		_boardTiles = new List<UIElement>();
    		_intermediateTiles = new List<UIElement>();
    	}
    
    	#endregion
    
    	#region APIs
    
    	async internal Task InitializeAsync(StorageFile imageFile, PatternType pattern, int maxRows, int maxCols, bool isPractice, bool isRestoring, SerializedGameTile[] serData)
    	{
    		await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    			{
    				if (_boardTiles == null)
    					_boardTiles = new List<UIElement>();
    
    				// Clear up the previous tiles
    				CleanUp();
    
    				Pattern = pattern;
    				MaxRows = maxRows;
    				MaxCols = maxCols;
    				// Initialize the _gamePanelHelper
    				_itemWidth = this.Width / (double)MaxCols;
    				_itemHeight = this.Height / (double)MaxRows;
    				_gamePanelHelper.Initialize(this.Dispatcher, this.Width, this.Height, MaxRows, MaxCols, _itemWidth, _itemHeight, Pattern);
    
    				_animDelay = (10 - MaxRows) * 3.01 + 4;
    
    				var tiles = isPractice ? await _gamePanelHelper.CreatePracticeGameTiles() :
    										await _gamePanelHelper.CreateGameTiles(imageFile);
    
    				GameTile[] gameTiles = new GameTile[MaxRows * MaxCols];
    
    				for (int i = 0; i < (MaxRows * MaxCols); i++)
    				{
    					int index = _gamePanelHelper.GetIndexFromCellIndex(i);
    
    					GameTile child = tiles.ElementAt(index);
    
    					if (child != null)
    					{
    						Children.Add(child);
    						gameTiles[i] = child;
    
    						child.Initialize(i, this);
    
    						// Initialize its RenderTransform
    						child.RenderTransform = _gamePanelHelper.CreateTransform(-_itemWidth, -_itemHeight, NORMAL_SCALE, NORMAL_SCALE);
    					}
    				}
    
    				if (isRestoring)
    				{
    					RestoreSuspendedState(serData);
    					_boardTiles.AddRange(Children.OfType<GameTile>().OrderBy(x => x.CurrentIndex));
    				}
    				else
    				{
    					// Shuffle the tiles
    					_boardTiles.AddRange(_gamePanelHelper.ShuffleGameTiles(gameTiles.AsEnumerable()));
    				}
    
    				InvalidateMeasure();
    
    				IsComposing = true;
    
    				// Raise the InitializationCompleted event if it is not restoring
    				if (!isRestoring)
    				{
    					var eventHandler = this.InitializationCompleted;
    					if (eventHandler != null)
    						eventHandler(this, null);
    				}
    			});
    	}
    
    	internal void SolveGame()
    	{
    		IsComposing = false;
    
    		_intermediateTiles.Clear();
    		_intermediateTiles.AddRange(_boardTiles);
    
    		_boardTiles.Clear();
    		int count = 0;
    		foreach (UIElement child in Children)
    		{
    			((GameTile)child).CurrentIndex = count++;
    			_boardTiles.Add(child);
    		}
    
    		InvalidateArrange();
    	}
    
    	internal void ResumeGame()
    	{
    		_boardTiles.Clear();
    		int count = 0;
    		foreach (UIElement child in _intermediateTiles)
    		{
    			((GameTile)child).CurrentIndex = count++;
    			_boardTiles.Add(child);
    		}
    
    		InvalidateArrange();
    
    		IsComposing = true;
    	}
    
    	internal void ResetGame()
    	{
    		foreach (GameTile tile in _boardTiles)
    		{
    			tile.CurrentIndex = tile.InitialIndex;
    		}
    		_boardTiles = _boardTiles.OfType<GameTile>().OrderBy(x => x.InitialIndex).ToList<UIElement>();
    
    		InvalidateArrange();
    
    		IsComposing = true;
    	}
    
    	internal SerializedGameTile[] GetSuspendedState()
    	{
    		List<SerializedGameTile> result = new List<SerializedGameTile>();
    
    		foreach (GameTile tile in Children)
    		{
    			result.Add(tile.Serialize());
    		}
    
    		return result.ToArray();
    	}
    
    	internal void RestoreSuspendedState(SerializedGameTile[] serData)
    	{
    		var sortedData = serData.OrderBy(x => x.HomeIndex);
    
    		int count = 0;
    		foreach (GameTile tile in Children)
    		{
    			tile.Deserialize(sortedData.ElementAt(count++));
    		}
    	}
    
    	#endregion
    
    	#region Helpers
    
    	/// <summary>
    	/// Iterates through all the fluid elements and animate their
    	/// movement to their new location.
    	/// </summary>
    	private void UpdateFluidLayout(bool showEasing = true)
    	{
    		if (_boardTiles == null)
    			return;
    
    		int dragTileIndex = -1;
    		if (_dragTile != null)
    			dragTileIndex = _boardTiles.IndexOf(_dragTile);
    
    		// Iterate through all the fluid elements and animate their
    		// movement to their new location.
    		for (int index = 0; index < _boardTiles.Count; index++)
    		{
    			UIElement element = _boardTiles[index];
    
    			if (element == null)
    				continue;
    
    			// If an child is currently being dragged, then no need to animate it
    			if (index == dragTileIndex)
    				continue;
    
    			element.Arrange(new Rect(0, 0, element.DesiredSize.Width,
    					element.DesiredSize.Height));
    
    			// Get the cell position of the current index
    			Point pos = _gamePanelHelper.GetPointFromIndex(index);
    
    			Storyboard transition;
    			// Is the child being animated the same as the child which was last dragged?
    			if (element == _lastDragElement)
    			{
    				if (!showEasing)
    				{
    					// Create the Storyboard for the transition
    					transition = _gamePanelHelper.CreateTransition(element, pos, FIRST_TIME_ANIMATION_DURATION, null);
    				}
    				else
    				{
    					// Is easing function specified for the animation?
    					TimeSpan duration = (DragEasing != null) ? DEFAULT_ANIMATION_TIME_WITH_EASING : DEFAULT_ANIMATION_TIME_WITHOUT_EASING;
    					// Create the Storyboard for the transition
    					transition = _gamePanelHelper.CreateTransition(element, pos, duration, DragEasing);
    				}
    
    				// When the user releases the drag child, it's Z-Index is set to 1 so that 
    				// during the animation it does not go below other elements.
    				// After the animation has completed set its Z-Index to 0
    				transition.Completed += (s, e) =>
    				{
    					if (_lastDragElement != null)
    					{
    						_lastDragElement.SetValue(Canvas.ZIndexProperty, 0);
    						_lastDragElement = null;
    					}
    				};
    			}
    			else // It is a non-dragElement
    			{
    				if (!showEasing)
    				{
    					// Create the Storyboard for the transition
    					transition = _gamePanelHelper.CreateTransition(element, pos, TimeSpan.FromMilliseconds(FIRST_TIME_ANIMATION_DURATION.Milliseconds + (index * _animDelay)), null);
    				}
    				else
    				{
    					// Is easing function specified for the animation?
    					TimeSpan duration = (ElementEasing != null) ? TimeSpan.FromMilliseconds(DEFAULT_ANIMATION_TIME_WITH_EASING.Milliseconds + (index * _animDelay)) :
    																	TimeSpan.FromMilliseconds(DEFAULT_ANIMATION_TIME_WITHOUT_EASING.Milliseconds + (index * _animDelay));
    					// Create the Storyboard for the transition
    					transition = _gamePanelHelper.CreateTransition(element, pos, duration, ElementEasing);
    				}
    			}
    
    			// Start the animation
    			transition.Begin();
    		}
    	}
    
    	/// <summary>
    	/// Moves the dragElement to the new Index
    	/// </summary>
    	/// <param name="newIndex">Index of the new location</param>
    	/// <returns>True-if dragElement was moved otherwise False</returns>
    	private bool UpdateDragElementIndex(int newIndex)
    	{
    		// Check if the dragElement is being moved to its current place
    		// If yes, then no need to proceed further. (Improves efficiency!)
    		int dragCellIndex = _boardTiles.IndexOf(_dragTile);
    		if (dragCellIndex == newIndex)
    			return false;
    
    		_boardTiles.RemoveAt(dragCellIndex);
    		_boardTiles.Insert(newIndex, _dragTile);
    		for (int i = 0; i < _boardTiles.Count; i++)
    		{
    			GameTile bTile = _boardTiles[i] as GameTile;
    			if (bTile != null)
    			{
    				bTile.CurrentIndex = i;
    			}
    		}
    
    		if (_dragTile is GameTile)
    		{
    			((GameTile)_dragTile).CurrentIndex = newIndex;
    		}
    
    		return true;
    	}
    
    	/// <summary>
    	/// Removes all the children from the FluidGameBoard
    	/// </summary>
    	private void CleanUp()
    	{
    		foreach (GameTile gTile in Children)
    		{
    			gTile.CleanUp();
    		}
    
    		_boardTiles.Clear();
    		Children.Clear();
    	}
    
    	async private Task CheckIfGameOverAsync()
    	{
    		bool gameOver = true;
    
    		await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    		{
    			foreach (GameTile tile in _boardTiles)
    			{
    				if ((tile != null) && (!tile.IsHome))
    				{
    					gameOver = false;
    					break;
    				}
    			}
    
    			if (gameOver)
    			{
    				var eventHandler = this.GameOver;
    				if (eventHandler != null)
    					eventHandler(this, null);
    			}
    		});
    	}
    
    	#endregion
    
    	#region FluidDrag Event Handlers
    
    	/// <summary>
    	/// Handler for the event when the user starts dragging the dragElement.
    	/// </summary>
    	/// <param name="child">UIElement being dragged</param>
    	/// <param name="position">Position in the child where the user clicked</param>
    	async internal void BeginFluidDragAsync(UIElement child, Point position, Pointer pointer)
    	{
    		if ((child == null) || (!IsComposing))
    			return;
    
    		// Call the event handler core on the Dispatcher. (Improves efficiency!)
    		await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    			{
    				child.Opacity = DragOpacity;
    				child.SetValue(Canvas.ZIndexProperty, Z_INDEX_DRAG);
    				// Capture further mouse events
    				child.CapturePointer(pointer);
    				_dragTile = child;
    				_lastDragElement = null;
    				_dragStartIndex = _boardTiles.IndexOf(child);
    				_dragEndIndex = -1;
    
    				// Since we are scaling the dragElement by DragScale, the clickPoint also shifts
    				_dragStartPoint = new Point(position.X * DragScale, position.Y * DragScale);
    			});
    	}
    
    	/// <summary>
    	/// Handler for the event when the user drags the dragElement.
    	/// </summary>
    	/// <param name="child">UIElement being dragged</param>
    	/// <param name="position">Position where the user clicked w.r.t. the UIElement being dragged</param>
    	/// <param name="positionInParent">Position where the user clicked w.r.t. the FluidGameBoard (the parentFWPanel of the UIElement being dragged</param>
    	async internal void FluidDragAsync(UIElement child, Point position, Point positionInParent)
    	{
    		if ((child == null) || (!IsComposing))
    			return;
    
    		// Call the event handler core on the Dispatcher. (Improves efficiency!)
    		await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    				{
    					if ((_dragTile != null) && (_gamePanelHelper != null))
    					{
    						// Create the new transform of the tile being dragged
    						_dragTile.RenderTransform = _gamePanelHelper.CreateTransform(positionInParent.X - _dragStartPoint.X,
    																				positionInParent.Y - _dragStartPoint.Y,
    																				DragScale,
    																				DragScale);
    
    						// Get the index in the fluidElements list corresponding to the current mouse location
    						Point currentPt = positionInParent;
    						int index = _gamePanelHelper.GetIndexFromPoint(currentPt);
    
    						// If no valid cell index is obtained, add the child to the end of the 
    						// fluidElements list.
    						if ((index == -1) || (index >= _boardTiles.Count))
    						{
    							index = _boardTiles.Count - 1;
    						}
    
    						// If the dragElement is moved to a new location, then only
    						// call the updation of the layout.
    						if (UpdateDragElementIndex(index))
    						{
    							InvalidateArrange();
    						}
    					}
    				});
    	}
    
    	/// <summary>
    	/// Handler for the event when the user stops dragging the dragElement and releases it.
    	/// </summary>
    	/// <param name="child">UIElement being dragged</param>
    	/// <param name="position">Position where the user clicked w.r.t. the UIElement being dragged</param>
    	/// <param name="positionInParent">Position where the user clicked w.r.t. the FluidGameBoard (the parentFWPanel of the UIElement being dragged</param>
    	async internal void EndFluidDragAsync(UIElement child, Point position, Point positionInParent, Pointer pointer)
    	{
    		if ((child == null) || (!IsComposing))
    			return;
    
    		// Call the event handler core on the Dispatcher. (Improves efficiency!)
    		await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
    			{
    				if ((_dragTile != null) && (_gamePanelHelper != null))
    				{
    					// Create the final transform for the tile being dragged
    					_dragTile.RenderTransform = _gamePanelHelper.CreateTransform(positionInParent.X - _dragStartPoint.X,
    																				positionInParent.Y - _dragStartPoint.Y,
    																				DragScale,
    																				DragScale);
    					// Remove the transparency
    					child.Opacity = NORMAL_OPACITY;
    					// Z-Index is set to 1 so that during the animation it does not go below other elements.
    					child.SetValue(Canvas.ZIndexProperty, Z_INDEX_INTERMEDIATE);
    					// Release the mouse capture
    					child.ReleasePointerCapture(pointer);
    
    					// Reference used to set the Z-Index to 0 during the UpdateFluidLayout
    					_lastDragElement = _dragTile;
    
    					_dragEndIndex = _boardTiles.IndexOf(_dragTile);
    					_dragTile = null;
    
    					// Raise the TileMoved event if the move is valid
    					if (_dragStartIndex != _dragEndIndex)
    					{
    						var eventHandler = this.TileMoved;
    						if (eventHandler != null)
    							eventHandler(this, null);
    					}
    
    					await CheckIfGameOverAsync();
    				}
    
    				InvalidateArrange();
    			});
    	}
    
    	#endregion
    }

    GamePanelHelper

    This class is a helper class for the GamePanel class and is responsible for most of the mathematical calculations required for the functioning of the GamePanel like calculation the location of the tiles, the splitting of images into tiles, creation of animations for fluid movement of tiles etc.

    C#
    internal sealed class GamePanelHelper
    {
    	#region Fields
    
    	private Size _panelSize;
    	private Size _cellSize;
    	private int _maxRows;
    	private int _maxCols;
    	private PatternType _arrangeStyle;
    	Dictionary<int, Cell> _cellIndex;
    	CoreDispatcher _dispatcher = null;
    
    	int _imgWidth = 0;
    	int _imgHeight = 0;
    	byte[] _imgBuffer = null;
    
    	#endregion
    
    	#region APIs
    
    	/// <summary>
    	/// Calculates the initial location of the child in the FluidWrapPanel
    	/// when the child is added.
    	/// </summary>
    	/// <param name="index">Index of the child in the FluidWrapPanel</param>
    	/// <returns></returns>
    	internal Point GetInitialLocationOfChild(int index)
    	{
    		Point result = new Point();
    
    		int row, column;
    
    		GetCellFromIndex(index, out row, out column);
    
    		int maxRows = (Int32)Math.Floor(_panelSize.Height / _cellSize.Height);
    		int maxCols = (Int32)Math.Floor(_panelSize.Width / _cellSize.Width);
    
    		bool isLeft = true;
    		bool isTop = true;
    		bool isCenterHeight = false;
    		bool isCenterWidth = false;
    
    		int halfRows = 0;
    		int halfCols = 0;
    
    		halfRows = (int)((double)maxRows / (double)2);
    
    		// Even number of rows
    		if ((maxRows % 2) == 0)
    		{
    			isTop = row < halfRows;
    		}
    		// Odd number of rows
    		else
    		{
    			if (row == halfRows)
    			{
    				isCenterHeight = true;
    				isTop = false;
    			}
    			else
    			{
    				isTop = row < halfRows;
    			}
    		}
    
    		halfCols = (int)((double)maxCols / (double)2);
    
    		// Even number of columns
    		if ((maxCols % 2) == 0)
    		{
    			isLeft = column < halfCols;
    		}
    		// Odd number of columns
    		else
    		{
    			if (column == halfCols)
    			{
    				isCenterWidth = true;
    				isLeft = false;
    			}
    			else
    			{
    				isLeft = column < halfCols;
    			}
    		}
    
    		if (isCenterHeight && isCenterWidth)
    		{
    			double posX = (halfCols) * _cellSize.Width;
    			double posY = (halfRows + 2) * _cellSize.Height;
    
    			return new Point(posX, posY);
    		}
    
    		if (isCenterHeight)
    		{
    			if (isLeft)
    			{
    				double posX = ((halfCols - column) + 1) * _cellSize.Width;
    				double posY = (halfRows) * _cellSize.Height;
    
    				result = new Point(-posX, posY);
    			}
    			else
    			{
    				double posX = ((column - halfCols) + 1) * _cellSize.Width;
    				double posY = (halfRows) * _cellSize.Height;
    
    				result = new Point(_panelSize.Width + posX, posY);
    			}
    
    			return result;
    		}
    
    		if (isCenterWidth)
    		{
    			if (isTop)
    			{
    				double posX = (halfCols) * _cellSize.Width;
    				double posY = ((halfRows - row) + 1) * _cellSize.Height;
    
    				result = new Point(posX, -posY);
    			}
    			else
    			{
    				double posX = (halfCols) * _cellSize.Width;
    				double posY = ((row - halfRows) + 1) * _cellSize.Height;
    
    				result = new Point(posX, _panelSize.Height + posY);
    			}
    
    			return result;
    		}
    
    		if (isTop)
    		{
    			if (isLeft)
    			{
    				double posX = ((halfCols - column) + 1) * _cellSize.Width;
    				double posY = ((halfRows - row) + 1) * _cellSize.Height;
    
    				result = new Point(-posX, -posY);
    			}
    			else
    			{
    				double posX = ((column - halfCols) + 1) * _cellSize.Width;
    				double posY = ((halfRows - row) + 1) * _cellSize.Height;
    
    				result = new Point(posX + _panelSize.Width, -posY);
    			}
    		}
    		else
    		{
    			if (isLeft)
    			{
    				double posX = ((halfCols - column) + 1) * _cellSize.Width;
    				double posY = ((row - halfRows) + 1) * _cellSize.Height;
    
    				result = new Point(-posX, _panelSize.Height + posY);
    			}
    			else
    			{
    				double posX = ((column - halfCols) + 1) * _cellSize.Width;
    				double posY = ((row - halfRows) + 1) * _cellSize.Height;
    
    				result = new Point(posX + _panelSize.Width, _panelSize.Height + posY);
    			}
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Initializes the FluidLayoutManager
    	/// </summary>
    	/// <param name="panelWidth">Width of the FluidWrapPanel</param>
    	/// <param name="panelHeight">Height of the FluidWrapPanel</param>
    	/// <param name="cellWidth">Width of each child in the FluidWrapPanel</param>
    	/// <param name="cellHeight">Height of each child in the FluidWrapPanel</param>
    	/// <param name="orientation">Orientation of the panel - Horizontal or Vertical</param>
    	internal void Initialize(CoreDispatcher dispatcher, double panelWidth, double panelHeight, int maxRows, int maxCols, double cellWidth, double cellHeight, PatternType style)
    	{
    		_dispatcher = dispatcher;
    		this._maxRows = maxRows;
    		this._maxCols = maxCols;
    		_arrangeStyle = style;
    
    		_cellIndex = IndexGenerator.GenerateIndex(maxRows, maxCols, style);
    
    		if (panelWidth <= 0.0d)
    			panelWidth = cellWidth;
    		if (panelHeight <= 0.0d)
    			panelHeight = cellHeight;
    		if ((cellWidth <= 0.0d) || (cellHeight <= 0.0d))
    		{
    			return;
    		}
    
    		if ((_panelSize.Width != panelWidth) ||
    			(_panelSize.Height != panelHeight) ||
    			(_cellSize.Width != cellWidth) ||
    			(_cellSize.Height != cellHeight))
    		{
    			_panelSize = new Size(panelWidth, panelHeight);
    			_cellSize = new Size(cellWidth, cellHeight);
    		}
    	}
    
    	/// <summary>
    	/// Gets the index of the child from the given cell index
    	/// </summary>
    	/// <param name="index">Cell Index</param>
    	/// <returns>True Index</returns>
    	internal int GetIndexFromCellIndex(int index)
    	{
    		int result = -1;
    
    		if (_cellIndex.ContainsKey(index))
    		{
    			Cell cell = _cellIndex[index];
    			result = ((cell.Row * _maxCols) + cell.Column);
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Provides the index of the child (in the FluidWrapPanel's children) from the given row and column
    	/// </summary>
    	/// <param name="row">Row</param>
    	/// <param name="column">Column</param>
    	/// <returns>Index</returns>
    	internal int GetIndexFromCell(int row, int column)
    	{
    		int result = -1;
    
    		if ((row >= 0) && (column >= 0))
    		{
    			Cell cell = new Cell { Row = row, Column = column };
    			result = _cellIndex.Where(x => x.Value == cell).Select(x => x.Key).FirstOrDefault();
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Provides the index of the child (in the FluidWrapPanel's children) from the given point
    	/// </summary>
    	/// <param name="p"></param>
    	/// <returns></returns>
    	internal int GetIndexFromPoint(Point p)
    	{
    		int result = -1;
    		if ((p.X > 0.00D) &&
    			(p.X < _panelSize.Width) &&
    			(p.Y > 0.00D) &&
    			(p.Y < _panelSize.Height))
    		{
    			int row;
    			int column;
    
    			GetCellFromPoint(p, out row, out column);
    			result = GetIndexFromCell(row, column);
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Provides the row and column of the child based on its index in the FluidWrapPanel.Children
    	/// </summary>
    	/// <param name="index">Index</param>
    	/// <param name="row">Row</param>
    	/// <param name="column">Column</param>
    	internal void GetCellFromIndex(int index, out int row, out int column)
    	{
    		row = column = -1;
    
    		if (_cellIndex.ContainsKey(index))
    		{
    			Cell cell = _cellIndex[index];
    			row = cell.Row;
    			column = cell.Column;
    		}
    	}
    
    	/// <summary>
    	/// Provides the row and column of the child based on its location in the FluidWrapPanel
    	/// </summary>
    	/// <param name="p">Location of the child in the parent</param>
    	/// <param name="row">Row</param>
    	/// <param name="column">Column</param>
    	internal void GetCellFromPoint(Point p, out int row, out int column)
    	{
    		row = column = -1;
    
    		if ((p.X < 0.00D) ||
    			(p.X > _panelSize.Width) ||
    			(p.Y < 0.00D) ||
    			(p.Y > _panelSize.Height))
    		{
    			return;
    		}
    
    		row = (int)(p.Y / _cellSize.Height);
    		column = (int)(p.X / _cellSize.Width);
    	}
    
    	/// <summary>
    	/// Provides the location of the child in the FluidWrapPanel based on the given row and column
    	/// </summary>
    	/// <param name="row">Row</param>
    	/// <param name="column">Column</param>
    	/// <returns>Location of the child in the panel</returns>
    	internal Point GetPointFromCell(int row, int column)
    	{
    		Point result = new Point();
    
    		if ((row >= 0) && (column >= 0))
    		{
    			result = new Point(_cellSize.Width * column, _cellSize.Height * row);
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Provides the location of the child in the FluidWrapPanel based on the given row and column
    	/// </summary>
    	/// <param name="index">Index</param>
    	/// <returns>Location of the child in the panel</returns>
    	internal Point GetPointFromIndex(int index)
    	{
    		Point result = new Point();
    
    		if (index >= 0)
    		{
    			int row;
    			int column;
    
    			GetCellFromIndex(index, out row, out column);
    			result = GetPointFromCell(row, column);
    		}
    
    		return result;
    	}
    
    	/// <summary>
    	/// Creates a TransformGroup based on the given Translation, Scale and Rotation
    	/// </summary>
    	/// <param name="transX">Translation in the X-axis</param>
    	/// <param name="transY">Translation in the Y-axis</param>
    	/// <param name="scaleX">Scale factor in the X-axis</param>
    	/// <param name="scaleY">Scale factor in the Y-axis</param>
    	/// <param name="rotAngle">Rotation</param>
    	/// <returns>TransformGroup</returns>
    	internal TransformGroup CreateTransform(double transX, double transY, double scaleX, double scaleY, double rotAngle = 0.0D)
    	{
    		TranslateTransform translation = new TranslateTransform();
    		translation.X = transX;
    		translation.Y = transY;
    
    		ScaleTransform scale = new ScaleTransform();
    		scale.ScaleX = scaleX;
    		scale.ScaleY = scaleY;
    
    		//RotateTransform rotation = new RotateTransform();
    		//rotation.Angle = rotAngle;
    
    		TransformGroup transform = new TransformGroup();
    		// THE ORDER OF TRANSFORM IS IMPORTANT
    		// First, scale, then rotate and finally translate
    		transform.Children.Add(scale);
    		//transform.Children.Add(rotation);
    		transform.Children.Add(translation);
    
    		return transform;
    	}
    
    	/// <summary>
    	/// Creates the storyboard for animating a child from its old location to the new location.
    	/// The Translation and Scale properties are animated.
    	/// </summary>
    	/// <param name="element">UIElement for which the storyboard has to be created</param>
    	/// <param name="newLocation">New location of the UIElement</param>
    	/// <param name="period">Duration of animation</param>
    	/// <param name="easing">Easing function</param>
    	/// <returns>Storyboard</returns>
    	internal Storyboard CreateTransition(UIElement element, Point newLocation, TimeSpan period, EasingFunctionBase easing)
    	{
    		Duration duration = new Duration(period);
    
    		// Animate X
    		DoubleAnimation translateAnimationX = new DoubleAnimation();
    		translateAnimationX.To = newLocation.X;
    		translateAnimationX.Duration = duration;
    		if (easing != null)
    			translateAnimationX.EasingFunction = easing;
    
    		Storyboard.SetTarget(translateAnimationX, element);
    		Storyboard.SetTargetProperty(translateAnimationX,
    			"(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.X)");
    
    		// Animate Y
    		DoubleAnimation translateAnimationY = new DoubleAnimation();
    		translateAnimationY.To = newLocation.Y;
    		translateAnimationY.Duration = duration;
    		if (easing != null)
    			translateAnimationY.EasingFunction = easing;
    
    		Storyboard.SetTarget(translateAnimationY, element);
    		Storyboard.SetTargetProperty(translateAnimationY,
    			"(UIElement.RenderTransform).(TransformGroup.Children)[1].(TranslateTransform.Y)");
    
    		// Animate ScaleX
    		DoubleAnimation scaleAnimationX = new DoubleAnimation();
    		scaleAnimationX.To = 1.0D;
    		scaleAnimationX.Duration = duration;
    		if (easing != null)
    			scaleAnimationX.EasingFunction = easing;
    
    		Storyboard.SetTarget(scaleAnimationX, element);
    		Storyboard.SetTargetProperty(scaleAnimationX,
    			"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)");
    
    		// Animate ScaleY
    		DoubleAnimation scaleAnimationY = new DoubleAnimation();
    		scaleAnimationY.To = 1.0D;
    		scaleAnimationY.Duration = duration;
    		if (easing != null)
    			scaleAnimationY.EasingFunction = easing;
    
    		Storyboard.SetTarget(scaleAnimationY, element);
    		Storyboard.SetTargetProperty(scaleAnimationY,
    			"(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)");
    
    		Storyboard sb = new Storyboard();
    		sb.Duration = duration;
    		sb.Children.Add(translateAnimationX);
    		sb.Children.Add(translateAnimationY);
    		sb.Children.Add(scaleAnimationX);
    		sb.Children.Add(scaleAnimationY);
    
    		return sb;
    	}
    
    	async internal Task<IEnumerable<GameTile>> CreateGameTiles(StorageFile imageFile)
    	{
    		IEnumerable<GameTile> result = null;
    
    		if (imageFile != null)
    		{
    			await ObtainImageDetailsAsync(imageFile);
    
    			result = await GenerateGameTilesAsync(imageFile);
    		}
    
    		return result;
    	}
    
    	async internal Task<IEnumerable<GameTile>> CreatePracticeGameTiles()
    	{
    		GameTile[] result = null;
    
    		await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    			{
    				int totalNum = _maxRows * _maxCols;
    
    				result = new GameTile[totalNum];
    
    				double tileWidth = _panelSize.Width / _maxCols;
    				double tileHeight = _panelSize.Height / _maxRows;
    
    				for (int i = 0; i < _maxRows; i++)
    				{
    					for (int j = 0; j < _maxCols; j++)
    					{
    						int index = i * _maxCols + j;
    						GameTile gTile = new GameTile
    						{
    							Width = tileWidth,
    							Height = tileHeight,
    							DisplayImage = null,
    							IsContentImage = false,
    							DisplayValue = (index + 1).ToString(),
    							HomeIndex = index,
    							InitialIndex = index,
    							CurrentIndex = index
    						};
    						result[index] = gTile;
    					}
    				}
    			});
    
    		return result;
    	}
    
    	/// <summary>
    	/// Shuffles the Game Tiles
    	/// </summary>
    	/// <param name="tiles">GameTiles array</param>
    	/// <returns>Shuffled GameTiles array</returns>
    	internal IEnumerable<GameTile> ShuffleGameTiles(IEnumerable<GameTile> tiles)
    	{
    		int totalNum = _maxRows * _maxCols;
    
    		GameTile[] result = new GameTile[totalNum];
    
    		//// TODO: REMOVE
    		//result[0] = tiles.ElementAt(1);
    		//result[1] = tiles.ElementAt(0);
    
    		//for (int i = 2; i < tiles.Count(); i++)
    		//{
    		//    result[i] = tiles.ElementAt(i);
    		//}
    
    		Random rnd = new Random();
    		List<int> indices = new List<int>();
    		for (int i = 0; i < totalNum; i++)
    		{
    			indices.Add(i);
    		}
    
    		int currCount = 0;
    
    		while (indices.Count > 1)
    		{
    			int nextRnd = rnd.Next(indices.Count);
    			if (indices[nextRnd] != currCount)
    			{
    				tiles.ElementAt(currCount).InitialIndex = indices[nextRnd];
    				tiles.ElementAt(currCount).CurrentIndex = indices[nextRnd];
    				result[indices[nextRnd]] = tiles.ElementAt(currCount);
    				indices.RemoveAt(nextRnd);
    				currCount++;
    			}
    		}
    
    		tiles.ElementAt(currCount).CurrentIndex = indices[0];
    		result[indices[0]] = tiles.ElementAt(currCount);
    
    		return result;
    	}
    
    	#endregion
    
    	#region Helpers
    
    	async Task ObtainImageDetailsAsync(StorageFile storageFile)
    	{
    		using (IRandomAccessStream fileStream = await storageFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
    		{
    			BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
    			BitmapFrame frame = await decoder.GetFrameAsync(0);
    			PixelDataProvider framePixelProvider = await frame.GetPixelDataAsync();
    
    			try
    			{
    				_imgWidth = (int)decoder.PixelWidth;
    				_imgHeight = (int)decoder.PixelHeight;
    				_imgBuffer = framePixelProvider.DetachPixelData();
    			}
    			catch (Exception e)
    			{
    				string msg = e.Message;
    			}
    		}
    	}
    
    	async Task<IEnumerable<GameTile>> GenerateGameTilesAsync(StorageFile imageFile)
    	{
    		List<GameTile> result = new List<GameTile>();
    
    		double tileWidth = _imgWidth / (double)_maxCols;
    		double tileHeight = _imgHeight / (double)_maxRows;
    
    		for (int i = 0; i < _maxRows; i++)
    		{
    			for (int j = 0; j < _maxCols; j++)
    			{
    				int stride = _imgWidth * 4;
    				List<byte> frameBytes = new List<byte>();
    
    				int startRow = (int)(i * tileHeight);
    				int endRow = (int)(startRow + tileHeight);
    
    				int startCol = (int)(j * tileWidth);
    				int endCol = (int)(startCol + tileWidth);
    
    				try
    				{
    					for (int r = startRow; r < endRow; r++)
    					{
    						for (int c = startCol; c < endCol; c++)
    						{
    							frameBytes.Add(_imgBuffer[r * stride + c * 4 + 0]);
    							frameBytes.Add(_imgBuffer[r * stride + c * 4 + 1]);
    							frameBytes.Add(_imgBuffer[r * stride + c * 4 + 2]);
    							frameBytes.Add(_imgBuffer[r * stride + c * 4 + 3]);
    						}
    					}
    				}
    				catch (IndexOutOfRangeException)
    				{
    
    				}
    
    				try
    				{
    					using (InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream())
    					{
    						BitmapEncoder enc = null;
    						if (imageFile.FileType == ".png")
    							enc = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, ras);
    						else
    							enc = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, ras);
    
    						// Write pixel data
    						enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)tileWidth, (uint)tileHeight, 96, 96, frameBytes.ToArray());
    						// Flush (all data is in "ras")
    						await enc.FlushAsync();
    
    						await _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    						{
    							BitmapImage bImg = new BitmapImage();
    							ras.Seek(0);
    							bImg.SetSource(ras);
    							ImageBrush imgBrush = new ImageBrush() { Stretch = Stretch.Fill };
    							imgBrush.ImageSource = bImg;
    							int index = i * _maxCols + j;
    							GameTile gTile = new GameTile
    							{
    								Width = _cellSize.Width,
    								Height = _cellSize.Height,
    								DisplayImage = imgBrush,
    								IsContentImage = true,
    								HomeIndex = index,
    								InitialIndex = index,
    								CurrentIndex = index
    							};
    
    							result.Add(gTile);
    						});
    					}
    				}
    				catch (Exception e)
    				{
    					string msg = e.Message;
    				}
    			}
    		}
    
    		return result;
    	}
    
    	#endregion
    }

    GameTile

    The GameTile is the control which is responsible for displaying the content of each tile. When the GameTile is not in its correct position, then it will be enclosed within a thick black border. Once the tile it placed in the correct location, then the thick border will be removed.

    XML
    <UserControl x:Class="FloTiles.Cortex.GameTile"
    			xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    			xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    			xmlns:local="using:FloTiles.Cortex"
    			xmlns:converters="using:FloTiles.Converters"
    			xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    			xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    			mc:Ignorable="d"
    			d:DesignHeight="300"
    			d:DesignWidth="400">
    	<UserControl.Resources>
    		<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityReverseConverter"
    												IsReverse="True"></converters:BooleanToVisibilityConverter>
    		<SolidColorBrush x:Key="SolidBrush"
    						Color="#0276FD"></SolidColorBrush>
    		<LinearGradientBrush x:Key="GradientBrush"
    							EndPoint="1.436,1.406"
    							StartPoint="0.552,0.456">
    			<GradientStop Color="#FF0276FD"
    						Offset="0.081" />
    			<GradientStop Color="White"
    						Offset="0.912" />
    			<GradientStop Color="#FF0276FD"
    						Offset="0.011" />
    		</LinearGradientBrush>
    	</UserControl.Resources>
    	<Grid x:Name="LayoutRoot">
    		<Border Name="BG"
    				Margin="0"
    				HorizontalAlignment="Stretch"
    				VerticalAlignment="Stretch"
    				BorderBrush="Transparent"
    				BorderThickness="0">
    		</Border>
    		<Border Name="TileBG"
    				Margin="0"
    				HorizontalAlignment="Stretch"
    				VerticalAlignment="Stretch"
    				Background="{Binding DisplayImage}"
    				BorderBrush="Transparent"
    				BorderThickness="0" />
    		<Viewbox Margin="10"
    				HorizontalAlignment="Stretch"
    				VerticalAlignment="Stretch">
    			<TextBlock HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					FontFamily="Segoe WP"
    					FontSize="58"
    					FontWeight="Light"
    					Foreground="White"
    					Text="{Binding Path=DisplayValue}" />
    		</Viewbox>
    		<Border Name="TileBorder"
    				HorizontalAlignment="Stretch"
    				VerticalAlignment="Stretch"
    				Background="Transparent"
    				BorderBrush="Black"
    				BorderThickness="4"
    				Visibility="{Binding Path=IsHome,
    									Converter={StaticResource BoolToVisibilityReverseConverter}}" />
    	</Grid>
    </UserControl>
    C#
    public sealed partial class GameTile : UserControl
    {
    	#region Fields
    
    	GamePanel _parent = null;
    	Brush _solidBrush = null;
    	Brush _gradientBrush = null;
    
    	#endregion
    
    	#region Dependency Properties
    
    	...
    
    	#endregion
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public GameTile()
    	{
    		InitializeComponent();
    
    		_solidBrush = this.Resources["SolidBrush"] as SolidColorBrush;
    		_gradientBrush = this.Resources["GradientBrush"] as LinearGradientBrush;
    
    		BG.Background = IsHome ? _solidBrush : _gradientBrush;
    
    		this.DataContext = this;
    	}
    
    	#endregion
    
    	#region APIs
    
    	public void Initialize(Int32 index, GamePanel parentPanel)
    	{
    		HomeIndex = index;
    		InitialIndex = index;
    		CurrentIndex = index;
    		_parent = parentPanel;
    
    		this.PointerPressed += OnPointerPressed;
    		this.PointerMoved += OnPointerMoved;
    		this.PointerReleased += OnPointerReleased;
    	}
    
    	public void CleanUp()
    	{
    		_parent = null;
    		this.PointerPressed -= OnPointerPressed;
    		this.PointerMoved -= OnPointerMoved;
    		this.PointerReleased -= OnPointerReleased;
    	}
    
    	public SerializedGameTile Serialize()
    	{
    		return new SerializedGameTile
    									{
    										HomeIndex = this.HomeIndex,
    										InitialIndex = this.InitialIndex,
    										CurrentIndex = this.CurrentIndex
    									};
    	}
    
    	public void Deserialize(SerializedGameTile serTile)
    	{
    		if (serTile != null)
    		{
    			this.HomeIndex = serTile.HomeIndex;
    			this.InitialIndex = serTile.InitialIndex;
    			this.CurrentIndex = serTile.CurrentIndex;
    		}
    	}
    
    	#endregion
    
    	#region Event Handlers
    
    	void OnPointerPressed(object sender, PointerRoutedEventArgs e)
    	{
    		if (_parent != null)
    		{
    			PointerPoint position = e.GetCurrentPoint(this);
    			_parent.BeginFluidDragAsync(this, position.Position, e.Pointer);
    		}
    
    	}
    
    	void OnPointerMoved(object sender, PointerRoutedEventArgs e)
    	{
    		if (_parent != null)
    		{
    			PointerPoint position = e.GetCurrentPoint(this);
    			PointerPoint positionInParent = e.GetCurrentPoint(_parent);
    			_parent.FluidDragAsync(this, position.Position, positionInParent.Position);
    		}
    	}
    
    	void OnPointerReleased(object sender, PointerRoutedEventArgs e)
    	{
    		if (_parent != null)
    		{
    			PointerPoint position = e.GetCurrentPoint(this);
    			PointerPoint positionInParent = e.GetCurrentPoint(_parent);
    			_parent.EndFluidDragAsync(this, position.Position, positionInParent.Position, e.Pointer);
    		}
    	}
    
    	#endregion
    }

    DataModel

    Image 16

    This module consists of the classes which encapsulate the various kinds of data that would be flowing between the various modules. Here are a few of them:

    MenuItemBase

    This abstract class derives from BindableBase acts as the base class for the MenuItem and SubMenuItem classes which represent the Challenge and the Level in the game respectively.

    C#
    [DataContract]
    public abstract class MenuItemBase : BindableBase
    {
    	private static Uri _baseUri = new Uri("ms-appx:///");
    
    	#region Properties
    
    	#region Status
    
    	private LevelStatusType _status = LevelStatusType.None;
    
    	/// <summary>
    	/// Gets or sets the Status property. This observable property 
    	/// indicates status of the level.
    	/// </summary>
    	[DataMember]
    	public LevelStatusType Status
    	{
    		get { return _status; }
    		set { this.SetProperty(ref this._status, value); }
    	}
    
    	#endregion
    
    	#region Info
    
    	private GameInfo _info = null;
    
    	/// <summary>
    	/// Gets or sets the Info property. This observable property 
    	/// indicates the information associated with this MenuItemBase.
    	/// </summary>
    	[DataMember]
    	public GameInfo Info
    	{
    		get { return _info; }
    		set { this.SetProperty(ref this._info, value); }
    	}
    
    	#endregion
    
    	#region Title
    
    	private string _title = string.Empty;
    
    	/// <summary>
    	/// Gets or sets the Title property. This observable property 
    	/// indicates the title of the Group.
    	/// </summary>
    	[DataMember]
    	public string Title
    	{
    		get { return _title; }
    		set { this.SetProperty(ref this._title, value); }
    	}
    
    	#endregion
    
    	#region Description
    
    	private string _description = string.Empty;
    
    	/// <summary>
    	/// Gets or sets the Description property. This observable property 
    	/// indicates the group description.
    	/// </summary>
    	[DataMember]
    	public string Description
    	{
    		get { return _description; }
    		set { this.SetProperty(ref this._description, value); }
    	}
    
    	#endregion
    
    	#region IconImagePath
    
    	private string _iconImagePath = string.Empty;
    
    	/// <summary>
    	/// Gets or sets the IconImagePath property. This observable property 
    	/// indicates the image path.
    	/// </summary>
    	[DataMember]
    	public string IconImagePath
    	{
    		get { return _iconImagePath; }
    		set { this.SetProperty(ref this._iconImagePath, value); }
    	}
    
    	#endregion
    
    	#region IconImage
    
    	private ImageSource _iconImage = null;
    
    	public ImageSource IconImage
    	{
    		get
    		{
    			if ((this._iconImage == null) && (!String.IsNullOrWhiteSpace(this._iconImagePath)))
    			{
    				CreateIconImage();
    			}
    
    			return this._iconImage;
    		}
    	}
    
    	#endregion
    
    	#endregion
    
    	#region Helpers
    
    	private void CreateIconImage()
    	{
    		this._iconImage = new BitmapImage(new Uri(MenuItemBase._baseUri, this._iconImagePath));
    	}
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public MenuItemBase(string uniqueId, PatternType pattern, GameLevelType level, string title, string description, string iconImagePath, LevelStatusType levelStatus = LevelStatusType.None)
    	{
    		_info = new GameInfo { UniqueId = uniqueId, Pattern = pattern, Level = level };
    		_status = levelStatus;
    		_title = title;
    		_description = description;
    		_iconImagePath = iconImagePath;
    		CreateIconImage();
    	}
    
    	#endregion
    }

    MenuItem

    This class derives from MenuItemBase and represents the Challenge in the game.

    C#
    [DataContract]
    public class MenuItem : MenuItemBase
    {
    	#region Properties
    
    	#region SubMenuItems
    
    	private ObservableCollection<SubMenuItem> _subMenuItems = new ObservableCollection<SubMenuItem>();
    
    	/// <summary>
    	/// Gets or sets the SubMenuItems property. This observable property 
    	/// indicates the collection of submenu dataSource within a menu item.
    	/// </summary>
    	[DataMember]
    	public ObservableCollection<SubMenuItem> SubMenuItems
    	{
    		get { return _subMenuItems; }
    		set { this.SetProperty(ref this._subMenuItems, value); }
    	}
    
    	#endregion
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public MenuItem(string uniqueId, PatternType pattern, GameLevelType level, string title, string description, string iconImagePath, LevelStatusType levelStatus = LevelStatusType.None) :
    		base(uniqueId, pattern, level, title, description, iconImagePath, levelStatus)
    	{
    
    	}
    
    	#endregion
    
    	#region APIs
    
    	public bool IsLocked
    	{
    		get
    		{
    			bool result = true;
    
    			if ((_subMenuItems != null) && (_subMenuItems.Count() > 0))
    			{
    				var unlockedItems = _subMenuItems.Where(s => (!s.Info.UniqueId.EndsWith("_L_0")) && (s.Status != LevelStatusType.None) && (s.Status != LevelStatusType.Locked));
    				if ((unlockedItems != null) && (unlockedItems.Count() > 0))
    				{
    					result = false;
    				}
    			}
    
    			return result;
    		}
    	}
    
    	#endregion
    }

    SubMenuItem

    This class derives from MenuItemBase and represents a Level within a Challenge. A MenuItems contains a collection of several SubMenuItems.

    C#
    [DataContract]
    public class SubMenuItem : MenuItemBase
    {
    	#region Properties
    
    	#region Menu
    
    	#region ItemColor
    
    	private SolidColorBrush _itemColor = null;
    
    	/// <summary>
    	/// Gets or sets the ItemColor property. This observable property 
    	/// indicates the color of the item.
    	/// </summary>
    	[DataMember]
    	public SolidColorBrush ItemColor
    	{
    		get { return _itemColor; }
    		set { this.SetProperty(ref this._itemColor, value); }
    	}
    
    	#endregion
    
    	#region ItemSpan
    
    	private int _itemSpan = 1;
    
    	/// <summary>
    	/// Gets or sets the ItemSpan property. This observable property 
    	/// indicates the span of the item.
    	/// </summary>
    	[DataMember]
    	public int ItemSpan
    	{
    		get { return _itemSpan; }
    		set { this.SetProperty(ref this._itemSpan, value); }
    	}
    
    	#endregion
    
    	[DataMember]
    	private MenuItem _menu = null;
    
    	/// <summary>
    	/// Gets or sets the Menu property. This observable property 
    	/// indicates the parent Menu Item.
    	/// </summary>
    	public MenuItem Menu
    	{
    		get { return _menu; }
    		set { this.SetProperty(ref this._menu, value); }
    	}
    
    	#endregion
    
    	#region TotalMoves
    
    	private string _totalMoves = Constants.NO_VALUE;
    
    	/// <summary>
    	/// Gets or sets the TotalMoves property. This observable property 
    	/// indicates the total number of moves made to complete this level.
    	/// </summary>
    	public string TotalMoves
    	{
    		get { return _totalMoves; }
    		set { this.SetProperty(ref this._totalMoves, value); }
    	}
    
    	#endregion
    
    	#region TotalDuration
    
    	private string _totalDuration = Constants.NO_VALUE;
    
    	/// <summary>
    	/// Gets or sets the TotalDuration property. This observable property 
    	/// indicates the total duration taken to complete this level.
    	/// </summary>
    	public string TotalDuration
    	{
    		get { return _totalDuration; }
    		set { this.SetProperty(ref this._totalDuration, value); }
    	}
    
    	#endregion
    
    	#region IsLevel
    
    	private bool _isLevel = true;
    
    	/// <summary>
    	/// Gets or sets the IsLevel property. This observable property 
    	/// indicates whether this is a practices or a game level.
    	/// </summary>
    	public bool IsLevel
    	{
    		get { return _isLevel; }
    		set { this.SetProperty(ref this._isLevel, value); }
    	}
    
    	#endregion
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public SubMenuItem(string uniqueId, PatternType pattern, GameLevelType level, string title, string description, string imagePath,
    		MenuItem parent, SolidColorBrush itemColor, int itemSpan = 1, LevelStatusType levelStatus = LevelStatusType.None, string moves = Constants.NO_VALUE, string duration = Constants.NO_VALUE, bool isLevel = true) :
    		base(uniqueId, pattern, level, title, description, imagePath, levelStatus)
    	{
    		_itemColor = itemColor;
    		_itemSpan = itemSpan;
    		_menu = parent;
    		_totalMoves = moves;
    		_totalDuration = duration;
    		_isLevel = isLevel;
    	}
    
    	#endregion
    }

    GroupData

    This class represents the grouping of a MenuItem and its SubMenuItems collection.

    C#
    public class GroupData : BindableBase
    {
    	#region Menu
    
    	private MenuItem _menu = null;
    
    	/// <summary>
    	/// Gets or sets the Menu property. This observable property 
    	/// indicates the Menu Item.
    	/// </summary>
    	public MenuItem Menu
    	{
    		get { return _menu; }
    		set { this.SetProperty(ref this._menu, value); }
    	}
    
    	#endregion
    
    	#region SubMenuItems
    
    	private ObservableCollection<SubMenuItem> _subMenuItems = null;
    
    	/// <summary>
    	/// Gets or sets the SubMenuItems property. This observable property 
    	/// indicates the SubMenu Items.
    	/// </summary>
    	public ObservableCollection<SubMenuItem> SubMenuItems
    	{
    		get { return _subMenuItems; }
    		set { this.SetProperty(ref this._subMenuItems, value); }
    	}
    
    	#endregion
    }

    GameMenuSource

    This class contains the hardcoded list of MenuItems and SubMenuItems representing the Challenges and their respective Levels in the game.

    C#
    public static class GameMenuSource
    {
    	public static IEnumerable<MenuItem> GetMenuItems()
    	{
    		ObservableCollection<MenuItem> menuItems = new ObservableCollection<MenuItem>();
    
    		var menu1 = new MenuItem("PAT_1", PatternType.StraightTopLeft, GameLevelType.None, "Straight Shot Challenge - I", "The arrangement of tiles begins at the Top-Left end of the GameBoard and moves right towards the last column on the same row. Once the last column of the row is reached, it continues from the first column of the next row.", "Assets/Icons/Patterns/Pattern01.png");
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_0", PatternType.StraightTopLeft, GameLevelType.Level0, "Practice Level", "Practice your moves.", "Assets/Icons/Levels/Level00.png", menu1, new SolidColorBrush(Colors.Orange), 2, isLevel: false, levelStatus: LevelStatusType.Playing));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_1", PatternType.StraightTopLeft, GameLevelType.Level1, "Level 1", "3 x 3 Puzzle.", "Assets/Icons/Levels/Level01.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Playing));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_2", PatternType.StraightTopLeft, GameLevelType.Level2, "Level 2", "4 x 4 Puzzle.", "Assets/Icons/Levels/Level02.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_3", PatternType.StraightTopLeft, GameLevelType.Level3, "Level 3", "5 x 5 Puzzle.", "Assets/Icons/Levels/Level03.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_4", PatternType.StraightTopLeft, GameLevelType.Level4, "Level 4", "6 x 6 Puzzle.", "Assets/Icons/Levels/Level04.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_5", PatternType.StraightTopLeft, GameLevelType.Level5, "Level 5", "7 x 7 Puzzle.", "Assets/Icons/Levels/Level05.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_6", PatternType.StraightTopLeft, GameLevelType.Level6, "Level 6", "8 x 8 Puzzle.", "Assets/Icons/Levels/Level06.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_7", PatternType.StraightTopLeft, GameLevelType.Level7, "Level 7", "9 x 9  Puzzle.", "Assets/Icons/Levels/Level07.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menu1.SubMenuItems.Add(new SubMenuItem("CHL_1_L_8", PatternType.StraightTopLeft, GameLevelType.Level8, "Level 8", "10 x 10 Puzzle.", "Assets/Icons/Levels/Level08.png", menu1, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 1, LevelStatusType.Locked));
    		menuItems.Add(menu1);
    
    		...
    		
    		var menu11 = new MenuItem("PAT_11", PatternType.None, GameLevelType.None, "My FloTiles", "Create your own FloTile.", "Assets/Icons/Patterns/MyFloTiles.png");
    		menu11.SubMenuItems.Add(new SubMenuItem("MFT_CAM", PatternType.None, GameLevelType.None, "FloTile from Camera.", "Create a FloTile from your camera.", "Assets/Icons/Levels/Camera.png", menu11, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 2, isLevel: false));
    		menu11.SubMenuItems.Add(new SubMenuItem("MFT_ALB", PatternType.None, GameLevelType.None, "FloTile from Photo Album.", "Create a FloTile from your Photo Album.", "Assets/Icons/Levels/MyPictures.png", menu11, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 2, isLevel: false));
    		menu11.SubMenuItems.Add(new SubMenuItem("SCO_BRD", PatternType.None, GameLevelType.None, "Scoreboard", "Get the statistics of the completed levels.", "Assets/Icons/Levels/Scorecard.png", menu11, new SolidColorBrush(Color.FromArgb(255, 2, 118, 253)), 2, isLevel: false));
    		menuItems.Add(menu11);
    
    		return menuItems;
    	}
    }

    SerializedMenuItem

    This class is represents the serialized state of a MenuItem.

    C#
    [DataContract]
    public class SerializedMenuItem
    {
    	[DataMember]
    	public string UniqueId { get; set; }
    	[DataMember]
    	public PatternType Pattern { get; set; }
    	[DataMember]
    	public GameLevelType Level { get; set; }
    	[DataMember]
    	public string Title { get; set; }
    	[DataMember]
    	public string Description { get; set; }
    	[DataMember]
    	public string IconImagePath { get; set; }
    	[DataMember]
    	public SerializedSubMenuItem[] Items { get; set; }
    }

    SerializedSubMenuItem

    This class represents the serialized state of a SubMenuItem.

    C#
    [DataContract]
    public class SerializedSubMenuItem
    {
    	[DataMember]
    	public string UniqueId { get; set; }
    	[DataMember]
    	public PatternType Pattern { get; set; }
    	[DataMember]
    	public GameLevelType Level { get; set; }
    	[DataMember]
    	public string Title { get; set; }
    	[DataMember]
    	public string Description { get; set; }
    	[DataMember]
    	public string IconImagePath { get; set; }
    	[DataMember]
    	public byte A { get; set; }
    	[DataMember]
    	public byte R { get; set; }
    	[DataMember]
    	public byte G { get; set; }
    	[DataMember]
    	public byte B { get; set; }
    	[DataMember]
    	public int ItemSpan { get; set; }
    	[DataMember]
    	public LevelStatusType Status { get; set; }
    	[DataMember]
    	public string TotalMoves { get; set; }
    	[DataMember]
    	public string TotalDuration { get; set; }
    	[DataMember]
    	public bool IsLevel { get; set; }
    }

    SerializedGameTile

    This class represents the serialized state of a GameTile.

    C#
    [DataContract]
    public class SerializedGameTile
    {
    	[DataMember]
    	public int HomeIndex { get; set; }
    	[DataMember]
    	public int InitialIndex { get; set; }
    	[DataMember]
    	public int CurrentIndex { get; set; }
    }

    Views

    Image 17

    The views define the various screens that are presented to the user based on the user's interaction. These views derive from LayoutAwarePage which is provided along with the Windows Store App templates in Visual Studio 2012. There are 5 views defined in FloTiles app:

    • MainMenuPage
    • SubMenuPage
    • GamePage
    • ScoreboardPage
    • MyFloTilesPage

    MainMenuPage

    Image 18

    This page shows the Challenges and their Levels in a GridView format. This page also supports semantic zoom. Therefore, it responds to the pinch gesture by showing the list of Challenges only. It also supports the portrait and snapped states.

    Image 19

    Code:

    XML
    <common:LayoutAwarePage x:Name="pageRoot"
    						x:Class="FloTiles.Views.MainMenuPage"
    						DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    						xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    						xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    						xmlns:local="using:FloTiles"
    						xmlns:common="using:FloTiles.Common"
    						xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    						xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    						Foreground="Black"
    						mc:Ignorable="d"
    						Tag="3">
    	<Page.Resources>
    		<!--
    			Collection of grouped items displayed by this page, bound to a subset
    			of the complete item list because items in groups cannot be virtualized
    		-->
    		<CollectionViewSource x:Name="groupedItemsViewSource"
    							Source="{Binding MenuItems}"
    							IsSourceGrouped="true"
    							ItemsPath="SubMenuItems" />
    	</Page.Resources>
    
    	<!--
    		This grid acts as a root panel for the page that defines two rows:
    		* Row 0 contains the back button and page title
    		* Row 1 contains the rest of the page layout
    	-->
    	<Grid Style="{StaticResource LayoutRootStyle}">
    		<Grid.RowDefinitions>
    			<RowDefinition Height="140" />
    			<RowDefinition Height="*" />
    		</Grid.RowDefinitions>
    		<Grid.Background>
    			<ImageBrush ImageSource="../Assets/Images/Background/BG03.jpg"
    						Stretch="UniformToFill"
    						AlignmentX="Left"
    						AlignmentY="Top"></ImageBrush>
    		</Grid.Background>
    
    		<!-- Back button and Title Logo -->
    		<Grid>
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto" />
    				<ColumnDefinition Width="*" />
    			</Grid.ColumnDefinitions>
    			<Button x:Name="backButton"
    					Click="GoBack"
    					IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
    					Style="{StaticResource BackButtonStyle}" />
    			<Image x:Name="TitleLogo"
    				Grid.Column="1"
    				Source="../Assets/Icons/FloTilesHeader.png"
    				Stretch="Uniform"
    				Width="280"
    				Height="140"
    				Margin="-20,0,20,0"
    				IsHitTestVisible="False"
    				HorizontalAlignment="Left" />
    		</Grid>
    
    		<!-- Semantic Zoom -->
    		<SemanticZoom x:Name="SemanticZoomView"
    					Grid.Row="1">
    			<SemanticZoom.ZoomedInView>
    				<!-- Horizontal scrolling grid used in most view states -->
    				<GridView x:Name="itemGridView"
    						AutomationProperties.AutomationId="ItemGridView"
    						AutomationProperties.Name="Grouped Items"
    						Padding="116,10,40,46"
    						ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
    						ItemTemplate="{StaticResource MainMenuTemplate}"
    						SelectionMode="None"
    						IsSwipeEnabled="false"
    						IsItemClickEnabled="True"
    						ItemClick="OnSubMenuItemSelected"
    						Tag="Horizontal">
    					<GridView.GroupStyle>
    						<GroupStyle>
    							<GroupStyle.HeaderTemplate>
    								<DataTemplate>
    									<Grid Margin="4,10,0,10">
    										<Button AutomationProperties.Name="Group Title"
    												Style="{StaticResource TextPrimaryButtonStyle}"
    												Click="OnMenuItemSelected">
    											<StackPanel Orientation="Horizontal">
    												<Grid Width="40"
    													Height="40">
    													<Border Background="#0276FD"></Border>
    													<Border>
    														<Border.Background>
    															<LinearGradientBrush EndPoint="1.436,1.406"
    																				StartPoint="0.552,0.456">
    																<GradientStop Color="#00FFFFFF"
    																			Offset="0.081" />
    																<GradientStop Color="White"
    																			Offset="0.912" />
    																<GradientStop Color="#00FFFFFF"
    																			Offset="0.011" />
    															</LinearGradientBrush>
    														</Border.Background>
    														<Image Source="{Binding Menu.IconImage}"
    															Stretch="Uniform" />
    													</Border>
    												</Grid>
    												<TextBlock Text="{Binding Menu.Title}"
    														Margin="15,-4,10,10"
    														VerticalAlignment="Center"
    														Style="{StaticResource GroupHeaderTextStyle}" />
    											</StackPanel>
    										</Button>
    									</Grid>
    								</DataTemplate>
    							</GroupStyle.HeaderTemplate>
    							<GroupStyle.Panel>
    								<ItemsPanelTemplate>
    									<VariableSizedWrapGrid Orientation="Horizontal"
    														Margin="0,0,80,0"
    														MaximumRowsOrColumns="{Binding ElementName=pageRoot, Path=Tag}" />
    								</ItemsPanelTemplate>
    							</GroupStyle.Panel>
    						</GroupStyle>
    					</GridView.GroupStyle>
    					<GridView.ItemsPanel>
    						<ItemsPanelTemplate>
    							<VirtualizingStackPanel Orientation="Horizontal" />
    						</ItemsPanelTemplate>
    					</GridView.ItemsPanel>
    				</GridView>
    			</SemanticZoom.ZoomedInView>
    			<SemanticZoom.ZoomedOutView>
    				<ListView x:Name="ZoomOut"
    						Width="1200"
    						Height="700"
    						Padding="10,50"
    						SelectionMode="None"
    						VerticalAlignment="Center"
    						HorizontalAlignment="Center">
    					<ListView.ItemTemplate>
    						<DataTemplate>
    							<Grid Width="200"
    								Height="200"
    								Background="#0276FD"
    								Margin="0,10">
    								<Grid.RowDefinitions>
    									<RowDefinition Height="*"></RowDefinition>
    									<RowDefinition Height="50"></RowDefinition>
    								</Grid.RowDefinitions>
    								<Border>
    									<Border.Background>
    										<LinearGradientBrush EndPoint="1.436,1.406"
    															StartPoint="0.552,0.456">
    											<GradientStop Color="#00FFFFFF"
    														Offset="0.081" />
    											<GradientStop Color="White"
    														Offset="0.912" />
    											<GradientStop Color="#00FFFFFF"
    														Offset="0.011" />
    										</LinearGradientBrush>
    									</Border.Background>
    								</Border>
    								<Border>
    									<Image Source="{Binding Group.Menu.IconImage}"
    										Stretch="Uniform"
    										Width="120"
    										Height="120"
    										Margin="-20,10,0,0"
    										VerticalAlignment="Top"
    										HorizontalAlignment="Center" />
    								</Border>
    								<Border Grid.RowSpan="2"
    										VerticalAlignment="Bottom"
    										Background="#323232"
    										Height="60">
    									<TextBlock Text="{Binding Group.Menu.Title}"
    											Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
    											FontFamily="Segoe UI Light"
    											FontWeight="Light"
    											FontSize="14"
    											Height="50"
    											Margin="5,0,0,0"
    											HorizontalAlignment="Left"
    											TextAlignment="Left"
    											VerticalAlignment="Center"
    											TextWrapping="Wrap" />
    								</Border>
    							</Grid>
    						</DataTemplate>
    					</ListView.ItemTemplate>
    					<ListView.ItemsPanel>
    						<ItemsPanelTemplate>
    							<WrapGrid ItemWidth="200"
    									ItemHeight="200"
    									Orientation="Horizontal"
    									MaximumRowsOrColumns="10"
    									VerticalChildrenAlignment="Center" />
    						</ItemsPanelTemplate>
    					</ListView.ItemsPanel>
    				</ListView>
    			</SemanticZoom.ZoomedOutView>
    		</SemanticZoom>
    
    		<!-- Vertical scrolling list only used when snapped -->
    		<ListView x:Name="SnappedListView"
    				AutomationProperties.AutomationId="SnappedListView"
    				AutomationProperties.Name="Grouped Items"
    				Grid.Row="1"
    				Visibility="Collapsed"
    				Margin="0,-10,0,0"
    				Padding="10,0,0,60"
    				ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
    				ItemTemplate="{StaticResource Standard80ItemTemplate}"
    				SelectionMode="None"
    				IsSwipeEnabled="false"
    				IsItemClickEnabled="True"
    				ItemClick="OnSubMenuItemSelected">
    
    			<ListView.GroupStyle>
    				<GroupStyle>
    					<GroupStyle.HeaderTemplate>
    						<DataTemplate>
    							<Grid Margin="7,10">
    								<Button AutomationProperties.Name="Group Title"
    										Style="{StaticResource TextPrimaryButtonStyle}"
    										Click="OnMenuItemSelected">
    									<StackPanel Orientation="Horizontal">
    										<Grid Width="40"
    											Height="40">
    											<Border Background="#0276FD"></Border>
    											<Border>
    												<Border.Background>
    													<LinearGradientBrush EndPoint="1.436,1.406"
    																		StartPoint="0.552,0.456">
    														<GradientStop Color="#00FFFFFF"
    																	Offset="0.081" />
    														<GradientStop Color="White"
    																	Offset="0.912" />
    														<GradientStop Color="#00FFFFFF"
    																	Offset="0.011" />
    													</LinearGradientBrush>
    												</Border.Background>
    												<Image Source="{Binding Menu.IconImage}"
    													Stretch="Uniform" />
    											</Border>
    										</Grid>
    										<TextBlock Text="{Binding Menu.Title}"
    												Margin="10,-4,10,10"
    												VerticalAlignment="Center"
    												Style="{StaticResource GroupHeaderTextStyleSnapped}"
    												TextWrapping="Wrap" />
    									</StackPanel>
    								</Button>
    							</Grid>
    						</DataTemplate>
    					</GroupStyle.HeaderTemplate>
    				</GroupStyle>
    			</ListView.GroupStyle>
    		</ListView>
    
    		<VisualStateManager.VisualStateGroups>
    
    			<!-- Visual states reflect the application's view state -->
    			<VisualStateGroup x:Name="ApplicationViewStates">
    				<VisualState x:Name="FullScreenLandscape">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageRoot"
    													Storyboard.TargetProperty="Tag">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="3" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    				<VisualState x:Name="Filled">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ZoomOut"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="900" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ZoomOut"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="700" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageRoot"
    													Storyboard.TargetProperty="Tag">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="3" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    
    				<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
    				<VisualState x:Name="FullScreenPortrait">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource PortraitBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ZoomOut"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="700" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ZoomOut"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="1200" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<!-- CHECK -->
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView"
    													Storyboard.TargetProperty="Padding">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="96,55,10,56" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="pageRoot"
    													Storyboard.TargetProperty="Tag">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="2" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    
    				<!--
    					The back button and title have different styles when snapped, and the list representation is substituted
    					for the grid displayed in all other view states
    				-->
    				<VisualState x:Name="Snapped">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource SnappedBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="250" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="125" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,10,0,-10" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedListView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Visible" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SemanticZoomView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Collapsed" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    	</Grid>
    </common:LayoutAwarePage>
    C#
    /// <summary>
    /// A page that displays a grouped collection of dataSource.
    /// </summary>
    public sealed partial class MainMenuPage : FloTiles.Common.LayoutAwarePage
    {
    	MessageDialog warningDialog = null;
    
    	public MainMenuPage()
    	{
    		this.InitializeComponent();
    
    		warningDialog = new MessageDialog("This level is locked. Please complete previous levels to unlock this level.", "FloTiles");
    		warningDialog.Commands.Add(new UICommand("Ok", null));
    
    		warningDialog.DefaultCommandIndex = 0;
    		warningDialog.CancelCommandIndex = 0;
    	}
    
    	/// <summary>
    	/// Populates the page with content passed during navigation.  Any saved state is also
    	/// provided when recreating a page from a prior session.
    	/// </summary>
    	/// <param name="navigationParameter">The parameter value passed to
    	/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
    	/// </param>
    	/// <param name="pageState">A dictionary of state preserved by this page during an earlier
    	/// session.  This will be null the first time a page is visited.</param>
    	/// <param name="gameState">Common Game State values</param>
    	protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		IEnumerable<MenuItem> dataSource = (gameState != null) ? gameState.MenuItems : GameMenuSource.GetMenuItems();
    
    		var menuItems = from item in dataSource
    						group item by item into g
    						select new GroupData { Menu = g.Key, SubMenuItems = g.Key.SubMenuItems };
    		this.DefaultViewModel["MenuItems"] = menuItems.ToList();
    
    		ZoomOut.ItemsSource = groupedItemsViewSource.View.CollectionGroups;
    	}
    
    	async private void OnSubMenuItemSelected(object sender, ItemClickEventArgs e)
    	{
    		// Navigate to the appropriate destination page, configuring the new page
    		// by passing required information as a navigation parameter
    		var subMenuItem = e.ClickedItem as SubMenuItem;
    		if (subMenuItem != null)
    		{
    			if (subMenuItem.Status != Common.LevelStatusType.Locked)
    			{
    				string uniqueId = ((SubMenuItem)subMenuItem).Info.UniqueId;
    				Type pageType = LevelHelper.GetPageType(uniqueId);
    				this.Frame.Navigate(pageType, uniqueId);
    			}
    			else
    				await warningDialog.ShowAsync();
    		}
    	}
    
    	private void OnMenuItemSelected(object sender, RoutedEventArgs e)
    	{
    		// Determine what MenuItem the Button instance represents
    		var group = ((sender as FrameworkElement).DataContext) as GroupData;
    
    		if (group != null)
    		{
    			// Navigate to the appropriate destination page, configuring the new page
    			// by passing required information as a navigation parameter
    			string uniqueId = ((MenuItem)group.Menu).Info.UniqueId;
    			Type pageType = LevelHelper.GetPageType(uniqueId);
    			this.Frame.Navigate(pageType, uniqueId);
    		}
    	}
    }

    SubMenuPage

    Image 20

    This page shows the Challenge selected by the user and its Levels in a GridView format. This page also supports the portrait and snapped states.

    Image 21

    Code:

    XML
    <common:LayoutAwarePage x:Name="pageRoot"
    						x:Class="FloTiles.Views.SubMenuPage"
    						DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    						xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    						xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    						xmlns:local="using:FloTiles"
    						xmlns:common="using:FloTiles.Common"
    						xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    						xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    						mc:Ignorable="d">
    
    	<Page.Resources>
    		<!-- Collection of items displayed by this page -->
    		<CollectionViewSource x:Name="itemsViewSource"
    							Source="{Binding SubMenuItems}" />
    	</Page.Resources>
    
    	<!--
    		This grid acts as a root panel for the page that defines two rows:
    		* Row 0 contains the back button and page title
    		* Row 1 contains the rest of the page layout
    	-->
    	<Grid DataContext="{Binding MenuItem}"
    		Style="{StaticResource LayoutRootStyle}">
    		<Grid.RowDefinitions>
    			<RowDefinition Height="140" />
    			<RowDefinition Height="*" />
    		</Grid.RowDefinitions>
    
    		<Grid.Background>
    			<ImageBrush ImageSource="../Assets/Images/Background/BG03.jpg"
    						Stretch="UniformToFill"
    						AlignmentX="Left"
    						AlignmentY="Top"></ImageBrush>
    		</Grid.Background>
    
    		<!-- Back button and Title Logo -->
    		<Grid>
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto" />
    				<ColumnDefinition Width="*" />
    			</Grid.ColumnDefinitions>
    			<Button x:Name="backButton"
    					Click="GoBack"
    					IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
    					Style="{StaticResource BackButtonStyle}" />
    			<Image x:Name="TitleLogo"
    				Grid.Column="1"
    				Source="../Assets/Icons/FloTilesHeader.png"
    				Stretch="Uniform"
    				Width="280"
    				Height="140"
    				IsHitTestVisible="False"
    				HorizontalAlignment="Left" />
    		</Grid>
    
    		<!-- Horizontal scrolling grid used in most view states -->
    		<GridView x:Name="LandscapeGridView"
    				AutomationProperties.AutomationId="LandscapeGridView"
    				AutomationProperties.Name="Items In Group"
    				TabIndex="1"
    				Grid.Row="1"
    				Padding="120,10,120,50"
    				ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
    				ItemTemplate="{StaticResource SubMenuItemTemplate}"
    				SelectionMode="None"
    				IsSwipeEnabled="false"
    				IsItemClickEnabled="True"
    				ItemClick="OnSubMenuItemSelected">
    
    			<GridView.Header>
    				<StackPanel Width="240"
    							Margin="0,4,14,0">
    					<TextBlock Text="{Binding Title}"
    							Margin="0,0,18,20"
    							Style="{StaticResource SubheaderTextStyle}"
    							MaxHeight="60" />
    					<Grid Height="240"
    						Margin="0"
    						Width="240"
    						AutomationProperties.Name="{Binding Title}">
    						<Border Background="#0276FD"></Border>
    						<Border>
    							<Border.Background>
    								<LinearGradientBrush EndPoint="1.436,1.406"
    													StartPoint="0.552,0.456">
    									<GradientStop Color="#00FFFFFF"
    												Offset="0.081" />
    									<GradientStop Color="White"
    												Offset="0.912" />
    									<GradientStop Color="#00FFFFFF"
    												Offset="0.011" />
    								</LinearGradientBrush>
    							</Border.Background>
    							<Image Source="{Binding IconImage}"
    								Stretch="Uniform" />
    						</Border>
    					</Grid>
    					<TextBlock Text="{Binding Description}"
    							Margin="0,0,10,0"
    							Style="{StaticResource BodyTextStyle}"
    							FontSize="16"
    							TextAlignment="Justify"/>
    				</StackPanel>
    			</GridView.Header>
    			<GridView.ItemContainerStyle>
    				<Style TargetType="FrameworkElement">
    					<Setter Property="Margin"
    							Value="52,0,0,10" />
    				</Style>
    			</GridView.ItemContainerStyle>
    		</GridView>
    
    		<!-- Vertical scrolling list only used when snapped -->
    		<ListView x:Name="SnappedListView"
    				AutomationProperties.AutomationId="ItemListView"
    				AutomationProperties.Name="Items In Group"
    				TabIndex="1"
    				Grid.Row="1"
    				Visibility="Collapsed"
    				Padding="10,0,0,60"
    				ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
    				ItemTemplate="{StaticResource Standard80ItemTemplate}"
    				SelectionMode="None"
    				IsSwipeEnabled="false"
    				IsItemClickEnabled="True"
    				ItemClick="OnSubMenuItemSelected">
    
    			<ListView.Header>
    				<StackPanel Width="240"
    							Margin="4,-10,4,40">
    					<TextBlock Text="{Binding Title}"
    							Margin="0,0,18,20"
    							Style="{StaticResource SubheaderTextStyle}"
    							MaxHeight="60" />
    					<Grid Height="160"
    						Margin="0"
    						Width="160"
    						HorizontalAlignment="Left"
    						AutomationProperties.Name="{Binding Title}">
    						<Border Background="#0276FD"></Border>
    						<Border>
    							<Border.Background>
    								<LinearGradientBrush EndPoint="1.436,1.406"
    													StartPoint="0.552,0.456">
    									<GradientStop Color="#00FFFFFF"
    												Offset="0.081" />
    									<GradientStop Color="White"
    												Offset="0.912" />
    									<GradientStop Color="#00FFFFFF"
    												Offset="0.011" />
    								</LinearGradientBrush>
    							</Border.Background>
    							<Image Source="{Binding IconImage}"
    								Stretch="Uniform" />
    						</Border>
    					</Grid>
    					<TextBlock Text="{Binding Description}"
    							Margin="5,10,10,0"
    							FontSize="16"
    							Style="{StaticResource BodyTextStyle}"
    							TextAlignment="Justify"/>
    				</StackPanel>
    			</ListView.Header>
    		</ListView>
    
    		<VisualStateManager.VisualStateGroups>
    
    			<!-- Visual states reflect the application's view state -->
    			<VisualStateGroup x:Name="ApplicationViewStates">
    				<VisualState x:Name="FullScreenLandscape" />
    				<VisualState x:Name="Filled" />
    
    				<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
    				<VisualState x:Name="FullScreenPortrait">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource PortraitBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<!-- CHECK -->
    						<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView"
    													Storyboard.TargetProperty="Padding">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="100,126,90,0" />
    						</ObjectAnimationUsingKeyFrames>-->
    					</Storyboard>
    				</VisualState>
    
    				<!--
    					The back button and title have different styles when snapped, and the list representation is substituted
    					for the grid displayed in all other view states
    				-->
    				<VisualState x:Name="Snapped">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource SnappedBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="250" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="125" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,10,0,-10" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="LandscapeGridView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Collapsed" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedListView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Visible" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    	</Grid>
    </common:LayoutAwarePage>
    C#
    /// <summary>
    /// A page that displays an overview of a single group, including a preview of the dataSource
    /// within the group.
    /// </summary>
    public sealed partial class SubMenuPage : FloTiles.Common.LayoutAwarePage
    {
    	MessageDialog warningDialog = null;
    
    	public SubMenuPage()
    	{
    		this.InitializeComponent();
    
    		warningDialog = new MessageDialog("This level is locked. Please complete previous levels to unlock this level.", "FloTiles");
    		warningDialog.Commands.Add(new UICommand("Ok", null));
    
    		warningDialog.DefaultCommandIndex = 0;
    		warningDialog.CancelCommandIndex = 0;
    	}
    
    	/// <summary>
    	/// Populates the page with content passed during navigation.  Any saved state is also
    	/// provided when recreating a page from a prior session.
    	/// </summary>
    	/// <param name="navigationParameter">The parameter value passed to
    	/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
    	/// </param>
    	/// <param name="pageState">A dictionary of state preserved by this page during an earlier
    	/// session.  This will be null the first time a page is visited.</param>
    	/// <param name="gameState">Common Game State values</param>
    	protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		// TODO: Assign a bindable group to this.DefaultViewModel["Group"]
    		// TODO: Assign a collection of bindable dataSource to this.DefaultViewModel["Items"]
    		var menuItem = gameState.GetMenuItem(navigationParameter as string);
    		this.DefaultViewModel["MenuItem"] = menuItem;
    		this.DefaultViewModel["SubMenuItems"] = menuItem.SubMenuItems;
    	}
    
    	async private void OnSubMenuItemSelected(object sender, ItemClickEventArgs e)
    	{
    		var subMenuItem = e.ClickedItem as SubMenuItem;
    		if (subMenuItem != null)
    		{
    			if (subMenuItem.Status != Common.LevelStatusType.Locked)
    				this.Frame.Navigate(typeof(GamePage), subMenuItem.Info.UniqueId);
    			else
    				await warningDialog.ShowAsync();
    		}
    	}
    
    	protected override void SaveState(Dictionary<string, object> pageState)
    	{
    		base.SaveState(pageState);
    	}
    }

    GamePage

    Image 22

    This page hosts the GamePanel and this is where the action happens. On the Top Right corner of this page, the number of moves made and the time consumed is displayed. This page has the AppBar at the bottom which contains the 4 options described in the earlier section.This page also supports the portrait and snapped states.

    Code:

    XML
    <common:LayoutAwarePage x:Name="pageRoot"
    						x:Class="FloTiles.Views.GamePage"
    						DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    						xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    						xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    						xmlns:local="using:FloTiles"
    						xmlns:common="using:FloTiles.Common"
    						xmlns:cortex="using:FloTiles.Cortex"
    						xmlns:converters="using:FloTiles.Converters"
    						xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    						xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    						Background="White"
    						mc:Ignorable="d">
    
    	<Page.Resources>
    		<converters:ContentVisibilityConverter x:Key="ContentVisibilityHelper"></converters:ContentVisibilityConverter>
    		<converters:ContentEnablerConverter x:Key="ContentEnabler"></converters:ContentEnablerConverter>
    		<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityHelper"></converters:BooleanToVisibilityConverter>
    		<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityReverseHelper"
    												IsReverse="True"></converters:BooleanToVisibilityConverter>
    		<converters:TimeConverter x:Key="TimeHelper"></converters:TimeConverter>
    	</Page.Resources>
    
    	<!-- Bottom AppBar -->
    	<Page.BottomAppBar>
    		<AppBar x:Name="GameBar"
    				Padding="10,0,10,0"
    				Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing|Paused|Solved}">
    			<Grid HorizontalAlignment="Stretch"
    				VerticalAlignment="Stretch">
    				<Grid x:Name="GameBarGrid"
    					HorizontalAlignment="Stretch"
    					VerticalAlignment="Stretch">
    					<Grid.ColumnDefinitions>
    						<ColumnDefinition Width="Auto"></ColumnDefinition>
    						<ColumnDefinition Width="Auto"></ColumnDefinition>
    						<ColumnDefinition Width="*"></ColumnDefinition>
    						<ColumnDefinition Width="Auto"></ColumnDefinition>
    						<ColumnDefinition Width="Auto"></ColumnDefinition>
    					</Grid.ColumnDefinitions>
    					<Button x:Name="PauseGameBtn"
    							Grid.Column="0"
    							Click="OnPauseGame"
    							IsEnabled="{Binding GameStatus, Converter={StaticResource ContentEnabler}, ConverterParameter=Playing}"
    							Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing|Solved}"
    							Style="{StaticResource PauseGameAppBarButtonStyle}"></Button>
    					<Button x:Name="ResumeGameBtn"
    							Grid.Column="0"
    							Click="OnResumeGame"
    							Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Paused}"
    							Style="{StaticResource ResumeGameAppBarButtonStyle}"></Button>
    					<Button x:Name="ResetGameBtn"
    							Grid.Column="1"
    							Click="OnResetGame"
    							IsEnabled="{Binding GameStatus, Converter={StaticResource ContentEnabler}, ConverterParameter=Playing}"
    							Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing|Paused|Solved}"
    							Style="{StaticResource ResetGameAppBarButtonStyle}"></Button>
    					<Button x:Name="ShowPatternBtn"
    							Grid.Column="3"
    							Click="OnShowPattern"
    							IsEnabled="{Binding GameStatus, Converter={StaticResource ContentEnabler}, ConverterParameter=Playing|Paused}"
    							Visibility="{Binding IsPatternVisible, Converter={StaticResource BoolToVisibilityReverseHelper}}"
    							Style="{StaticResource ShowPatternAppBarButtonStyle}"></Button>
    					<Button x:Name="HidePatternBtn"
    							Grid.Column="3"
    							Click="OnHidePattern"
    							Visibility="{Binding IsPatternVisible, Converter={StaticResource BoolToVisibilityHelper}}"
    							Style="{StaticResource HidePatternAppBarButtonStyle}"></Button>
    					<Button x:Name="SolveBtn"
    							Grid.Column="4"
    							Click="OnSolve"
    							IsEnabled="{Binding GameStatus, Converter={StaticResource ContentEnabler}, ConverterParameter=Playing}"
    							Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing|Paused}"
    							Style="{StaticResource SolveAppBarButtonStyle}"></Button>
    					<Button x:Name="UnSolveBtn"
    							Grid.Column="4"
    							Click="OnContinueGaming"
    							Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Solved}"
    							Style="{StaticResource UnsolveAppBarButtonStyle}"></Button>
    				</Grid>
    				<Grid x:Name="SnappedGameBarGrid"
    					HorizontalAlignment="Stretch"
    					VerticalAlignment="Stretch"
    					Visibility="Collapsed">
    				</Grid>
    			</Grid>
    		</AppBar>
    	</Page.BottomAppBar>
    	<!--
    		This grid acts as a root panel for the page that defines two rows:
    		* Row 0 contains the back button and page title
    		* Row 1 contains the rest of the page layout
    	-->
    	<Grid Style="{StaticResource LayoutRootStyle}">
    		<Grid.RowDefinitions>
    			<RowDefinition Height="140" />
    			<RowDefinition Height="*" />
    		</Grid.RowDefinitions>
    		<Grid.Background>
    			<ImageBrush ImageSource="../Assets/Images/Background/Woven.png"
    						Stretch="None"
    						AlignmentX="Center"
    						AlignmentY="Center"></ImageBrush>
    		</Grid.Background>
    
    		<!-- Applause Sound -->
    		<MediaElement x:Name="applauseSound"
    					HorizontalAlignment="Left"
    					VerticalAlignment="Top"
    					Width="10"
    					Height="10"
    					AutoPlay="False"
    					AudioCategory="GameMedia"
    					IsLooping="False"></MediaElement>
    
    		<!-- Back button and Title Logo -->
    		<Grid>
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto" />
    				<ColumnDefinition Width="*" />
    			</Grid.ColumnDefinitions>
    			<Button x:Name="backButton"
    					Click="GoBackInternal"
    					IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
    					Style="{StaticResource BackButtonStyle}" />
    			<Image x:Name="TitleLogo"
    				Grid.Column="1"
    				Source="../Assets/Icons/FloTilesHeader.png"
    				Stretch="Uniform"
    				Width="280"
    				Height="140"
    				IsHitTestVisible="False"
    				HorizontalAlignment="Left" />
    		</Grid>
    
    		<!-- Challenge Image-->
    		<Grid x:Name="ChallengeImage"
    			Width="80"
    			Height="80"
    			Margin="0,35,0,0"
    			VerticalAlignment="Top"
    			HorizontalAlignment="Center">
    			<Border Background="#0276FD"></Border>
    			<Border>
    				<Border.Background>
    					<LinearGradientBrush EndPoint="1.436,1.406"
    										StartPoint="0.552,0.456">
    						<GradientStop Color="#00FFFFFF"
    									Offset="0.081" />
    						<GradientStop Color="White"
    									Offset="0.912" />
    						<GradientStop Color="#00FFFFFF"
    									Offset="0.011" />
    					</LinearGradientBrush>
    				</Border.Background>
    				<Image Source="{Binding ChallengeImage}"
    					Stretch="Uniform" />
    			</Border>
    		</Grid>
    
    		<!-- KeepersGrid -->
    		<Grid x:Name="KeepersGrid"
    			HorizontalAlignment="Right"
    			VerticalAlignment="Top"
    			Margin="0,20,20,0">
    			<Grid.RowDefinitions>
    				<RowDefinition Height="60"></RowDefinition>
    				<RowDefinition Height="60"></RowDefinition>
    			</Grid.RowDefinitions>
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto"></ColumnDefinition>
    			</Grid.ColumnDefinitions>
    			<TextBlock x:Name="TimeKeeper"
    					HorizontalAlignment="Right"
    					FontSize="42"
    					Foreground="White"
    					FontFamily="Segoe UI Light"
    					FontWeight="Light"
    					Text="{Binding TotalDuration, Converter={StaticResource TimeHelper}}"></TextBlock>
    			<TextBlock x:Name="MoveKeeper"
    					HorizontalAlignment="Right"
    					Grid.Row="1"
    					FontSize="42"
    					Foreground="White"
    					FontFamily="Segoe UI Light"
    					FontWeight="Light"
    					Text="{Binding TotalMoves}"></TextBlock>
    		</Grid>
    
    		<!-- Game Board -->
    		<Viewbox Margin="0"
    				Grid.Row="1"
    				VerticalAlignment="Stretch"
    				HorizontalAlignment="Stretch">
    			<Grid Margin="0">
    				<cortex:GamePanel x:Name="gameBoard"
    								Width="1280"
    								Height="853"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Bottom"
    								Margin="0,0,0,20">
    					<cortex:GamePanel.ElementEasing>
    						<BackEase Amplitude="0.45"
    								EasingMode="EaseOut" />
    					</cortex:GamePanel.ElementEasing>
    					<cortex:GamePanel.DragEasing>
    						<BackEase Amplitude="0.65"
    								EasingMode="EaseOut" />
    					</cortex:GamePanel.DragEasing>
    				</cortex:GamePanel>
    				<!-- Loading Grid -->
    				<Grid x:Name="LoadingGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#44111111"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Loading}">
    					<ProgressRing Foreground="LawnGreen"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								Width="50"
    								Height="50"></ProgressRing>
    					<TextBlock HorizontalAlignment="Center"
    							VerticalAlignment="Center"
    							TextWrapping="Wrap"
    							FontSize="96"
    							Foreground="White"
    							FontFamily="Segoe UI Light"
    							FontWeight="Light"
    							Margin="25,10"
    							Text="Loading..."></TextBlock>
    				</Grid>
    
    				<!-- Initializing Grid -->
    				<Grid x:Name="InitializingGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#44111111"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Initializing}">
    					<ProgressRing Foreground="LawnGreen"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								Width="50"
    								Height="50"></ProgressRing>
    					<TextBlock HorizontalAlignment="Center"
    							VerticalAlignment="Center"
    							TextWrapping="Wrap"
    							FontSize="96"
    							Foreground="White"
    							FontFamily="Segoe UI Light"
    							FontWeight="ExtraLight"
    							Margin="25,10"
    							Text="Initializing..."></TextBlock>
    				</Grid>
    
    				<!-- Tap To Play Grid -->
    				<Grid x:Name="TapToPlayGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#AA121212"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=TapToPlay}"
    					PointerPressed="OnTapToStart">
    					<TextBlock x:Name="StartGameMsg"
    							HorizontalAlignment="Center"
    							VerticalAlignment="Center"
    							TextWrapping="Wrap"
    							FontSize="96"
    							Foreground="White"
    							FontFamily="Segoe UI Light"
    							FontWeight="ExtraLight"
    							Margin="25,10"
    							Text="Tap to start the game..."></TextBlock>
    				</Grid>
    
    				<!-- Game Paused Grid -->
    				<Grid x:Name="GamePausedGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#AA121212"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Paused}">
    					<Grid.RowDefinitions>
    						<RowDefinition></RowDefinition>
    						<RowDefinition></RowDefinition>
    					</Grid.RowDefinitions>
    					<StackPanel HorizontalAlignment="Center"
    								VerticalAlignment="Bottom">
    						<TextBlock x:Name="PausedMovesTB"
    								HorizontalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="72"
    								Foreground="Red"
    								FontFamily="Segoe UI Light"
    								FontWeight="ExtraLight"
    								Margin="0,10"
    								Text="Game Paused!"></TextBlock>
    					</StackPanel>
    					<Button x:Name="MainResumeGameBtn"
    							Grid.Row="1"
    							Width="120"
    							Height="120"
    							HorizontalAlignment="Center"
    							VerticalAlignment="Top"
    							Margin="0,50,0,0"
    							FontSize="64"
    							Content="&#xE102;"
    							Style="{StaticResource MetroButton}"
    							Click="OnResumeGame">
    					</Button>
    				</Grid>
    
    				<Grid x:Name="GameOverGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Left"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#88202020"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=GameOver}"
    					PointerPressed="OnPlayNextLevel">
    					<Grid Width="750"
    						Height="500"
    						Background="#AA121212"
    						HorizontalAlignment="Center"
    						VerticalAlignment="Center">
    						<Grid.RowDefinitions>
    							<RowDefinition Height="1.2*"></RowDefinition>
    							<RowDefinition Height="*"></RowDefinition>
    							<RowDefinition Height="*"></RowDefinition>
    							<RowDefinition Height="0.75*"></RowDefinition>
    							<RowDefinition Height="0.5*"></RowDefinition>
    						</Grid.RowDefinitions>
    						<TextBlock HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="76"
    								Foreground="LawnGreen"
    								FontFamily="Segoe UI Light"
    								FontWeight="Light"
    								Margin="0"
    								Text="Level Complete!"></TextBlock>
    						<Grid Grid.RowSpan="2"
    							Width="280"
    							Height="140"
    							Margin="0"
    							HorizontalAlignment="Center"
    							VerticalAlignment="Bottom">
    							<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing}">
    								<Image Source="../Assets/Icons/LevelStatus/NoHStar.png"
    									Stretch="Fill"></Image>
    							</Border>
    							<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=OneStar}">
    								<Image Source="../Assets/Icons/LevelStatus/OneHStar.png"
    									Stretch="Fill"></Image>
    							</Border>
    							<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=TwoStars}">
    								<Image Source="../Assets/Icons/LevelStatus/TwoHStars.png"
    									Stretch="Fill"></Image>
    							</Border>
    							<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=ThreeStars}">
    								<Image Source="../Assets/Icons/LevelStatus/ThreeHStars.png"
    									Stretch="Fill"></Image>
    							</Border>
    						</Grid>
    						<TextBlock Grid.Row="2"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="48"
    								Foreground="Yellow"
    								FontFamily="Segoe UI Light"
    								FontWeight="Light"
    								Margin="0"
    								Text="Challenge successfully completed!"
    								Visibility="{Binding GameOverStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=ChallengeComplete}"></TextBlock>
    						<TextBlock Grid.Row="2"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="48"
    								Foreground="Yellow"
    								FontFamily="Segoe UI Light"
    								FontWeight="Light"
    								Margin="0"
    								Text="Next Challenge Unlocked!"
    								Visibility="{Binding GameOverStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=NextChallengeUnlocked}"></TextBlock>
    						<TextBlock Grid.Row="3"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="32"
    								Foreground="White"
    								FontFamily="Segoe UI Light"
    								FontWeight="ExtraLight"
    								Margin="0"
    								Text="Tap to play the next level..."
    								Visibility="{Binding GameOverStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=LevelComplete|NextChallengeUnlocked|LevelCompletePostNCUnlock}"></TextBlock>
    						<TextBlock Grid.Row="3"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"
    								TextWrapping="Wrap"
    								FontSize="32"
    								Foreground="White"
    								FontFamily="Segoe UI Light"
    								FontWeight="ExtraLight"
    								Margin="0"
    								Text="Tap to start the next Challenge..."
    								Visibility="{Binding GameOverStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=ChallengeComplete}"></TextBlock>
    						<TextBlock Grid.Row="4"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Top"
    								TextWrapping="Wrap"
    								FontSize="24"
    								Foreground="White"
    								FontFamily="Segoe UI Light"
    								FontWeight="ExtraLight"
    								Margin="0"
    								Text="Or press Back button to access the next Challenge..."
    								Visibility="{Binding GameOverStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=NextChallengeUnlocked|LevelCompletePostNCUnlock}"></TextBlock>
    					</Grid>
    				</Grid>
    				
    				<!-- Practice Game Over Grid -->
    				<Grid x:Name="PracticeGameOverGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#88202020"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=PracticeGameOver}"
    					PointerPressed="OnPracticeGameOver">
    					<Grid Width="700"
    						Height="500"
    						Background="#AA121212"
    						HorizontalAlignment="Center"
    						VerticalAlignment="Center">
    						<StackPanel HorizontalAlignment="Center"
    									VerticalAlignment="Center">
    							<TextBlock HorizontalAlignment="Center"
    									VerticalAlignment="Center"
    									TextWrapping="Wrap"
    									FontSize="80"
    									Foreground="LawnGreen"
    									FontFamily="Segoe UI Light"
    									FontWeight="Light"
    									Margin="25,10"
    									TextAlignment="Center"
    									Text="Practice Complete!"></TextBlock>
    							<Grid Width="280"
    								Height="140"
    								Margin="10"
    								HorizontalAlignment="Center">
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing}">
    									<Image Source="../Assets/Icons/LevelStatus/NoHStar.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=OneStar}">
    									<Image Source="../Assets/Icons/LevelStatus/OneHStar.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=TwoStars}">
    									<Image Source="../Assets/Icons/LevelStatus/TwoHStars.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=ThreeStars}">
    									<Image Source="../Assets/Icons/LevelStatus/ThreeHStars.png"
    										Stretch="Fill"></Image>
    								</Border>
    							</Grid>
    							<TextBlock HorizontalAlignment="Center"
    									VerticalAlignment="Center"
    									TextWrapping="Wrap"
    									FontSize="32"
    									Foreground="White"
    									FontFamily="Segoe UI Light"
    									FontWeight="ExtraLight"
    									Margin="25,30"
    									Text="Tap to continue..."></TextBlock>
    						</StackPanel>
    					</Grid>
    				</Grid>
    
    				<!-- Custom Game Over Grid -->
    				<Grid x:Name="CustomGameOverGrid"
    					Canvas.ZIndex="3"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="#88202020"
    					Visibility="{Binding GameStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=CustomGameOver}"
    					PointerPressed="OnPracticeGameOver">
    					<Grid Width="700"
    						Height="500"
    						Background="#AA121212"
    						HorizontalAlignment="Center"
    						VerticalAlignment="Center">
    						<StackPanel HorizontalAlignment="Center"
    									VerticalAlignment="Center">
    							<TextBlock HorizontalAlignment="Center"
    									VerticalAlignment="Center"
    									TextWrapping="Wrap"
    									FontSize="80"
    									Foreground="LawnGreen"
    									FontFamily="Segoe UI Light"
    									FontWeight="Light"
    									Margin="25,10"
    									TextAlignment="Center"
    									Text="Game Over!"></TextBlock>
    							<Grid Width="280"
    								Height="140"
    								Margin="10"
    								HorizontalAlignment="Center">
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=Playing}">
    									<Image Source="../Assets/Icons/LevelStatus/NoHStar.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=OneStar}">
    									<Image Source="../Assets/Icons/LevelStatus/OneHStar.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=TwoStars}">
    									<Image Source="../Assets/Icons/LevelStatus/TwoHStars.png"
    										Stretch="Fill"></Image>
    								</Border>
    								<Border Visibility="{Binding LevelStatus, Converter={StaticResource ContentVisibilityHelper}, ConverterParameter=ThreeStars}">
    									<Image Source="../Assets/Icons/LevelStatus/ThreeHStars.png"
    										Stretch="Fill"></Image>
    								</Border>
    							</Grid>
    							<TextBlock HorizontalAlignment="Center"
    									VerticalAlignment="Center"
    									TextWrapping="Wrap"
    									FontSize="32"
    									Foreground="White"
    									FontFamily="Segoe UI Light"
    									FontWeight="ExtraLight"
    									Margin="25,30"
    									Text="Tap to continue..."></TextBlock>
    						</StackPanel>
    					</Grid>
    				</Grid>
    				
    				<!-- Pattern Grid -->
    				<Grid x:Name="PatternGrid"
    					Width="1280"
    					Height="853"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="0,0,0,20"
    					Background="Transparent"
    					IsHitTestVisible="False"
    					Visibility="{Binding IsPatternVisible, Converter={StaticResource BoolToVisibilityHelper}}"></Grid>
    			</Grid>
    		</Viewbox>
    
    		<VisualStateManager.VisualStateGroups>
    
    			<!-- Visual states reflect the application's view state -->
    			<VisualStateGroup x:Name="ApplicationViewStates">
    				<VisualState x:Name="FullScreenLandscape" />
    				<VisualState x:Name="Filled" />
    
    				<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
    				<VisualState x:Name="FullScreenPortrait">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource PortraitBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="KeepersGrid"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,150,10,-150" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChallengeImage"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,160,10,-160" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    
    				<!-- The back button and title have different styles when snapped -->
    				<VisualState x:Name="Snapped">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource SnappedBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="250" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="125" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,10,0,-10" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="KeepersGrid"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,150,10,-150" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChallengeImage"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="10,160,-10,-160" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ChallengeImage"
    													Storyboard.TargetProperty="HorizontalAlignment">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Left" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    	</Grid>
    	
    </common:LayoutAwarePage>
    C#
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class GamePage : LayoutAwarePage
    {
    	#region Fields
    
    	DispatcherTimer _gameTimer = null;
    	long _totalDuration = 0;
    	long _totalMoves = 0;
    	MessageDialog _confirmQuitGameDlg = null;
    	MessageDialog _confirmResetGameDlg = null;
    	object _syncObject = new object();
    	string currentUniqueId = string.Empty;
    	bool _isPracticeGame = false;
    	bool _isCustomFloTileGame = false;
    	PatternType _customPattern = PatternType.None;
    	GameLevelType _customGameLevel = GameLevelType.None;
    
    	#endregion
    
    	#region Construction / Initialization
    
    	public GamePage()
    	{
    		InitializeComponent();
    		_gameTimer = new DispatcherTimer();
    		_gameTimer.Interval = TimeSpan.FromSeconds(1);
    		_gameTimer.Tick += UpdateTimeKeeper;
    
    		gameBoard.InitializationCompleted += OnInitializationCompleted;
    		gameBoard.TileMoved += OnTileMoved;
    		gameBoard.GameOver += OnGameOver;
    
    		applauseSound.Stop();
    		applauseSound.Source = new Uri("ms-appx:/Assets/Sounds/Applause.mp3");
    
    		CreateConfirmationDialogs();
    	}
    
    	#endregion
    
    	#region Overrides
    
    	/// <summary>
    	/// Populates the page with content passed during navigation.  Any saved state is also
    	/// provided when recreating a page from a prior session.
    	/// </summary>
    	/// <param name="navigationParameter">The parameter value passed to
    	/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
    	/// </param>
    	/// <param name="pageState">A dictionary of state preserved by this page during an earlier
    	/// session.  This will be null the first time a page is visited.</param>
    	/// <param name="gameState">Common Game State values</param>
    	async protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		currentUniqueId = navigationParameter as string;
    		if (!String.IsNullOrWhiteSpace(currentUniqueId))
    		{
    			_isCustomFloTileGame = LevelHelper.IsCustomFloTileGame(currentUniqueId);
    
    			if (_isCustomFloTileGame)
    			{
    				await LoadCustomFloTileGameState(pageState);
    			}
    			else
    			{
    				await LoadGameState(pageState, gameState);
    			}
    		}
    	}
    
    	private async Task LoadCustomFloTileGameState(Dictionary<String, Object> pageState)
    	{
    		string[] tokens = currentUniqueId.Split(new string[] { Constants.DEFAULT_SEPARATOR }, StringSplitOptions.RemoveEmptyEntries);
    		if (tokens.Count() == 4)
    		{
    			_customPattern = (PatternType)Enum.Parse(typeof(PatternType), tokens[1]);
    			_customGameLevel = (GameLevelType)Enum.Parse(typeof(GameLevelType), tokens[2]);
    
    			int row, col;
    			GameStatusType savedGameStatus = GameStatusType.None;
    			string randomImageFilePath = string.Empty;
    			LevelHelper.GetLevelDetails(_customGameLevel, out row, out col, out _isPracticeGame);
    			bool isRestoring = false;
    			SerializedGameTile[] serData = null;
    
    			DefaultViewModel["ChallengeImage"] = new BitmapImage(new Uri("ms-appx:///Assets/Icons/Patterns/MyFloTiles.png"));
    			DefaultViewModel["GameOverStatus"] = GameOverType.None;
    
    			if (pageState != null)
    			{
    				savedGameStatus = (GameStatusType)pageState["GameStatus"];
    				DefaultViewModel["GameStatus"] = GameStatusType.Loading;
    				DefaultViewModel["TotalMoves"] = _totalDuration = (long)pageState["TotalDuration"];
    				DefaultViewModel["TotalDuration"] = _totalDuration = (long)pageState["TotalMoves"];
    				DefaultViewModel["IsPatternVisible"] = pageState["IsPatternVisible"];
    				DefaultViewModel["LevelStatus"] = (LevelStatusType)pageState["LevelStatus"];
    				randomImageFilePath = pageState["GameImagePath"] as string;
    				isRestoring = true;
    				serData = pageState["SerializedGameBoard"] as SerializedGameTile[];
    				ImageRandomizer.Deserialize(pageState["CachedImages"] as int[]);
    			}
    			else
    			{
    				DefaultViewModel["GameStatus"] = GameStatusType.Initializing;
    				DefaultViewModel["TotalMoves"] = _totalDuration = 0;
    				DefaultViewModel["TotalDuration"] = _totalDuration = 0;
    				DefaultViewModel["IsPatternVisible"] = false;
    				DefaultViewModel["LevelStatus"] = LevelStatusType.None;
    				randomImageFilePath = tokens[3];
    			}
    
    			DefaultViewModel["GameImagePath"] = randomImageFilePath;
    
    			StorageFile file = null;
    			StorageFolder imageFolder = await ApplicationData.Current.LocalFolder.GetFolderAsync("ImageBackup");
    			if (imageFolder != null)
    			{
    				file = await imageFolder.GetFileAsync(randomImageFilePath);
    			}
    
    			await gameBoard.InitializeAsync(file, _customPattern, row, col, _isPracticeGame, isRestoring, serData);
    			// Initialize the Pattern Grid
    			await InitializePatternGridAsync(_customPattern, row, col);
    
    			if (savedGameStatus != GameStatusType.None)
    			{
    				DefaultViewModel["GameStatus"] = savedGameStatus;
    				if ((savedGameStatus == GameStatusType.Playing) && (!_gameTimer.IsEnabled))
    					_gameTimer.Start();
    			}
    		}
    	}
    
    	private async Task LoadGameState(Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		var submenuItem = gameState.GetSubMenuItem(currentUniqueId);
    		if (submenuItem != null)
    		{
    			string randomImageFilePath = string.Empty;
    			GameStatusType savedGameStatus = GameStatusType.None;
    			int row, col;
    			LevelHelper.GetLevelDetails(submenuItem.Info.Level, out row, out col, out _isPracticeGame);
    
    			bool isRestoring = false;
    			SerializedGameTile[] serData = null;
    
    			DefaultViewModel["ChallengeImage"] = submenuItem.Menu.IconImage;
    
    			if (pageState != null)
    			{
    				savedGameStatus = (GameStatusType)pageState["GameStatus"];
    				DefaultViewModel["GameStatus"] = GameStatusType.Loading;
    				DefaultViewModel["TotalMoves"] = _totalDuration = (long)pageState["TotalDuration"];
    				DefaultViewModel["TotalDuration"] = _totalDuration = (long)pageState["TotalMoves"];
    				DefaultViewModel["IsPatternVisible"] = pageState["IsPatternVisible"];
    				DefaultViewModel["LevelStatus"] = (LevelStatusType)pageState["LevelStatus"];
    				randomImageFilePath = pageState["GameImagePath"] as string;
    				isRestoring = true;
    				serData = pageState["SerializedGameBoard"] as SerializedGameTile[];
    				ImageRandomizer.Deserialize(pageState["CachedImages"] as int[]);
    				DefaultViewModel["GameOverStatus"] = pageState["GameOverStatus"];
    			}
    			else
    			{
    				if (!_isPracticeGame)
    				{
    					randomImageFilePath = ImageRandomizer.NextImage();
    				}
    				DefaultViewModel["GameStatus"] = GameStatusType.Initializing;
    				DefaultViewModel["TotalMoves"] = _totalDuration = 0;
    				DefaultViewModel["TotalDuration"] = _totalDuration = 0;
    				DefaultViewModel["IsPatternVisible"] = false;
    				DefaultViewModel["LevelStatus"] = submenuItem.Status;
    				DefaultViewModel["GameOverStatus"] = GameOverType.None;
    			}
    
    			DefaultViewModel["GameImagePath"] = randomImageFilePath;
    			StorageFile file = null;
    			if (!_isPracticeGame)
    			{
    				file = await Package.Current.InstalledLocation.GetFileAsync(randomImageFilePath);
    				if (submenuItem.Status == LevelStatusType.Locked)
    				{
    					submenuItem.Status = LevelStatusType.Playing;
    					await SuspensionManager.SaveAsync();
    				}
    			}
    
    			await gameBoard.InitializeAsync(file, submenuItem.Info.Pattern, row, col, _isPracticeGame, isRestoring, serData);
    			// Initialize the Pattern Grid
    			await InitializePatternGridAsync(submenuItem.Info.Pattern, row, col);
    
    			if (savedGameStatus != GameStatusType.None)
    			{
    				DefaultViewModel["GameStatus"] = savedGameStatus;
    				if ((savedGameStatus == GameStatusType.Playing) && (!_gameTimer.IsEnabled))
    					_gameTimer.Start();
    			}
    		}
    	}
    
    	/// <summary>
    	/// Preserves state associated with this page in case the application is suspended or the
    	/// page is discarded from the navigation cache.  Values must conform to the serialization
    	/// requirements of <see cref="SuspensionManager.SessionState"/>.
    	/// </summary>
    	/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
    	protected override void SaveState(Dictionary<String, Object> pageState)
    	{
    		pageState["GameStatus"] = DefaultViewModel["GameStatus"];
    		pageState["TotalDuration"] = DefaultViewModel["TotalDuration"];
    		pageState["TotalMoves"] = DefaultViewModel["TotalMoves"];
    		pageState["IsPatternVisible"] = DefaultViewModel["IsPatternVisible"];
    		pageState["GameImagePath"] = DefaultViewModel["GameImagePath"];
    		pageState["SerializedGameBoard"] = gameBoard.GetSuspendedState();
    		pageState["LevelStatus"] = DefaultViewModel["LevelStatus"];
    		pageState["CachedImages"] = ImageRandomizer.Serialize();
    		pageState["GameOverStatus"] = DefaultViewModel["GameOverStatus"];
    		base.SaveState(pageState);
    	}
    
    	protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
    	{
    		applauseSound.Stop();
    		base.OnNavigatingFrom(e);
    	}
    
    	#endregion
    
    	#region Event Handlers
    
    	void OnInitializationCompleted(object sender, EventArgs e)
    	{
    		DefaultViewModel["GameStatus"] = GameStatusType.TapToPlay;
    	}
    
    	void OnTileMoved(object sender, EventArgs e)
    	{
    		DefaultViewModel["TotalMoves"] = ++_totalMoves;
    	}
    
    	async void OnGameOver(object sender, EventArgs e)
    	{
    		if (_gameTimer.IsEnabled)
    			_gameTimer.Stop();
    		applauseSound.Play();
    
    		int row, col;
    		bool isPractice;
    
    		if (_isCustomFloTileGame)
    		{
    			DefaultViewModel["GameStatus"] = GameStatusType.CustomGameOver;
    
    			LevelHelper.GetLevelDetails(_customGameLevel, out row, out col, out isPractice);
    			var status = LevelHelper.GetRating(_customPattern, row, col, _totalMoves, _totalDuration);
    			DefaultViewModel["LevelStatus"] = status;
    			return;
    		}
    
    		var frameState = SuspensionManager.SessionStateForFrame(this.Frame);
    		if (frameState.ContainsKey("GameState"))
    		{
    			GameStateViewModel gVM = frameState["GameState"] as GameStateViewModel;
    			var submenuItem = gVM.GetSubMenuItem(currentUniqueId);
    			if (submenuItem != null)
    			{
    				LevelHelper.GetLevelDetails(submenuItem.Info.Level, out row, out col, out isPractice);
    				var status = LevelHelper.GetRating(submenuItem.Info.Pattern, row, col, _totalMoves, _totalDuration);
    				DefaultViewModel["LevelStatus"] = status;
    
    				submenuItem.TotalMoves = _totalMoves.ToString();
    				submenuItem.TotalDuration = _totalDuration.ToString();
    
    				if (_isPracticeGame)
    				{
    					DefaultViewModel["GameStatus"] = GameStatusType.PracticeGameOver;
    				}
    				else
    				{
    					submenuItem.Status = status;
    					DefaultViewModel["GameOverStatus"] = LevelHelper.GetGameOverStatus(currentUniqueId);
    					DefaultViewModel["GameStatus"] = GameStatusType.GameOver;
    
    					// Unlock the next level in the challenge
    					string nextLevelId = LevelHelper.GetNextLevel(currentUniqueId);
    					if (!String.IsNullOrWhiteSpace(nextLevelId))
    					{
    						var nextSubMenuItem = gVM.GetSubMenuItem(nextLevelId);
    						if (nextSubMenuItem != null)
    							nextSubMenuItem.Status = LevelStatusType.Playing;
    					}
    
    					// If the currently completed level is the Boundary level (5th level), then 
    					// unlock the next challenge's first level.
    					if (LevelHelper.IsBoundaryLevel(currentUniqueId))
    					{
    						string nextChallengeLevelId = LevelHelper.GetNextChallengeLevel(currentUniqueId);
    						if (!String.IsNullOrWhiteSpace(nextChallengeLevelId))
    						{
    							var nextChallengeLevel = gVM.GetSubMenuItem(nextChallengeLevelId);
    							if (nextChallengeLevel != null)
    								nextChallengeLevel.Status = LevelStatusType.Playing;
    						}
    					}
    				}
    
    				await SuspensionManager.SaveAsync();
    			}
    		}
    	}
    
    	async private void GoBackInternal(object sender, RoutedEventArgs e)
    	{
    		if (((GameStatusType)DefaultViewModel["GameStatus"] != GameStatusType.GameOver) &&
    			((GameStatusType)DefaultViewModel["GameStatus"] != GameStatusType.PracticeGameOver))
    		{
    			var result = await ConfirmQuitGameAsync();
    			if (result.Label == "Yes")
    				base.GoBack(this, new RoutedEventArgs());
    		}
    		else
    		{
    			base.GoBack(this, new RoutedEventArgs());
    		}
    	}
    
    	void UpdateTimeKeeper(object sender, object e)
    	{
    		lock (_syncObject)
    		{
    			DefaultViewModel["TotalDuration"] = ++_totalDuration;
    		}
    	}
    
    	void OnTapToStart(object sender, PointerRoutedEventArgs e)
    	{
    		DefaultViewModel["GameStatus"] = GameStatusType.Playing;
    		ResetKeepers();
    		_gameTimer.Start();
    	}
    
    	void OnPauseGame(object sender, RoutedEventArgs e)
    	{
    		if (_gameTimer.IsEnabled)
    			_gameTimer.Stop();
    		DefaultViewModel["GameStatus"] = GameStatusType.Paused;
    	}
    
    	void OnResumeGame(object sender, RoutedEventArgs e)
    	{
    		DefaultViewModel["GameStatus"] = GameStatusType.Playing;
    		_gameTimer.Start();
    	}
    
    	void OnPlayNextLevel(object sender, PointerRoutedEventArgs e)
    	{
    		applauseSound.Stop();
    		if (_isCustomFloTileGame)
    		{
    			base.GoBack(this, new RoutedEventArgs());
    		}
    		else
    		{
    			string nextLevelId = LevelHelper.GetNextLevel(currentUniqueId);
    			if (!String.IsNullOrWhiteSpace(nextLevelId))
    			{
    				GoBack(this, new RoutedEventArgs());
    				this.Frame.Navigate(typeof(GamePage), nextLevelId);
    			}
    		}
    	}
    
    	private void OnPracticeGameOver(object sender, PointerRoutedEventArgs e)
    	{
    		base.GoBack(this, new RoutedEventArgs());
    	}
    
    	async void OnResetGame(object sender, RoutedEventArgs e)
    	{
    		await ConfirmResetGameAsync();
    	}
    
    	void OnShowPattern(object sender, RoutedEventArgs e)
    	{
    		lock (_syncObject)
    		{
    			_totalDuration += 30;
    			DefaultViewModel["TotalDuration"] = _totalDuration;
    		}
    
    		DefaultViewModel["IsPatternVisible"] = true;
    	}
    
    	void OnHidePattern(object sender, RoutedEventArgs e)
    	{
    		DefaultViewModel["IsPatternVisible"] = false;
    	}
    
    	void OnSolve(object sender, RoutedEventArgs e)
    	{
    		lock (_syncObject)
    		{
    			_totalDuration += 20;
    			DefaultViewModel["TotalDuration"] = _totalDuration;
    		}
    
    		DefaultViewModel["GameStatus"] = GameStatusType.Solved;
    		gameBoard.SolveGame();
    	}
    
    	void OnContinueGaming(object sender, RoutedEventArgs e)
    	{
    		gameBoard.ResumeGame();
    		DefaultViewModel["GameStatus"] = GameStatusType.Playing;
    	}
    
    	#endregion
    
    	#region Helpers
    
    	void CreateConfirmationDialogs()
    	{
    		_confirmQuitGameDlg = new MessageDialog("Do you want to discard the current game?", "FloTiles");
    		_confirmQuitGameDlg.Commands.Add(new UICommand("Yes"));
    
    		_confirmQuitGameDlg.Commands.Add(new UICommand("No", async (x) =>
    		{
    			await gameBoard.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    			{
    				if ((GameStatusType)DefaultViewModel["GameStatus"] == GameStatusType.Playing)
    					_gameTimer.Start();
    			});
    		}));
    		_confirmQuitGameDlg.DefaultCommandIndex = 1;
    		_confirmQuitGameDlg.CancelCommandIndex = 1;
    
    		_confirmResetGameDlg = new MessageDialog("Are you sure you want to reset the current game?", "FloTiles");
    		_confirmResetGameDlg.Commands.Add(new UICommand("Yes", (x) =>
    		{
    			ResetTheGame();
    		}));
    
    		_confirmResetGameDlg.Commands.Add(new UICommand("No", async (x) =>
    		{
    			await gameBoard.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    			{
    				if ((GameStatusType)DefaultViewModel["GameStatus"] == GameStatusType.Playing)
    					_gameTimer.Start();
    			});
    		}));
    		_confirmResetGameDlg.DefaultCommandIndex = 1;
    		_confirmResetGameDlg.CancelCommandIndex = 1;
    	}
    
    	async Task<IUICommand> ConfirmQuitGameAsync()
    	{
    		if (_gameTimer.IsEnabled)
    			_gameTimer.Stop();
    
    		return await _confirmQuitGameDlg.ShowAsync();
    	}
    
    	async Task ConfirmResetGameAsync()
    	{
    		_gameTimer.Stop();
    		await _confirmResetGameDlg.ShowAsync();
    	}
    
    	void ResetTheGame()
    	{
    		DefaultViewModel["GameStatus"] = GameStatusType.Initializing;
    		if (_gameTimer.IsEnabled)
    			_gameTimer.Stop();
    
    		ResetKeepers();
    
    		gameBoard.ResetGame();
    		DefaultViewModel["GameStatus"] = GameStatusType.TapToPlay;
    	}
    
    	void ResetKeepers()
    	{
    		DefaultViewModel["TotalMoves"] = _totalDuration = 0;
    		DefaultViewModel["TotalDuration"] = _totalDuration = 0;
    	}
    
    	async Task InitializePatternGridAsync(PatternType pattern, int maxRows, int maxCols)
    	{
    		await gameBoard.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    			{
    				PatternGrid.RowDefinitions.Clear();
    				PatternGrid.ColumnDefinitions.Clear();
    				PatternGrid.Children.Clear();
    
    				Dictionary<int, Cell> cellIndex = IndexGenerator.GenerateIndex(maxRows, maxCols, pattern);
    
    				if (cellIndex == null)
    					return;
    
    				for (int i = 0; i < maxRows; i++)
    				{
    					RowDefinition rd = new RowDefinition();
    					rd.Height = new GridLength(1, GridUnitType.Star);
    					PatternGrid.RowDefinitions.Add(rd);
    				}
    
    				for (int i = 0; i < maxCols; i++)
    				{
    					ColumnDefinition cd = new ColumnDefinition();
    					cd.Width = new GridLength(1, GridUnitType.Star);
    					PatternGrid.ColumnDefinitions.Add(cd);
    				}
    
    				for (int i = 0; i < maxRows * maxCols; i++)
    				{
    					PatternBadge badge = new PatternBadge { Text = (i + 1).ToString(), Width = 50, Height = 50, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top };
    					Cell c = cellIndex[i];
    					Grid.SetRow(badge, c.Row);
    					Grid.SetColumn(badge, c.Column);
    					PatternGrid.Children.Add(badge);
    
    					//
    				}
    			});
    	}
    
    	#endregion
    }

    ScoreboardPage

    Image 23

    This page is responsible for showing the statistics of the Challenges and Levels played so far. This page also supports the portrait and snapped states.

    Image 24

    Code:

    XML
    <common:LayoutAwarePage x:Name="pageRoot"
    						x:Class="FloTiles.Views.ScoreBoardPage"
    						DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    						xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    						xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    						xmlns:local="using:FloTiles.Views"
    						xmlns:common="using:FloTiles.Common"
    						xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    						xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    						mc:Ignorable="d">
    
    	<Page.Resources>
    
    		<!-- Collection of items displayed by this page -->
    		<CollectionViewSource x:Name="itemsViewSource"
    							Source="{Binding MenuItems}" />
    
    		<CollectionViewSource x:Name="snappedItemsViewSource"
    							Source="{Binding MenuItems}"
    							IsSourceGrouped="true"
    							ItemsPath="SubMenuItems" />
    	</Page.Resources>
    
    	<!--
    		This grid acts as a root panel for the page that defines two rows:
    		* Row 0 contains the back button and page title
    		* Row 1 contains the rest of the page layout
    	-->
    	<Grid Style="{StaticResource LayoutRootStyle}">
    		<Grid.RowDefinitions>
    			<RowDefinition Height="140" />
    			<RowDefinition Height="*" />
    		</Grid.RowDefinitions>
    		<!--<Grid.ColumnDefinitions>
    			<ColumnDefinition x:Name="primaryColumn"
    							Width="500" />
    			<ColumnDefinition Width="*" />
    		</Grid.ColumnDefinitions>-->
    
    		<!-- Background -->
    		<Grid.Background>
    			<ImageBrush ImageSource="../Assets/Images/Background/BG06.jpg"
    						Stretch="UniformToFill"
    						AlignmentX="Left"
    						AlignmentY="Top"></ImageBrush>
    		</Grid.Background>
    
    		<!-- Back button and Title Logo -->
    		<Grid x:Name="titlePanel"
    			Grid.ColumnSpan="2">
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto" />
    				<ColumnDefinition Width="*" />
    			</Grid.ColumnDefinitions>
    			<Button x:Name="backButton"
    					Click="GoBack"
    					IsEnabled="{Binding DefaultViewModel.CanGoBack, ElementName=pageRoot}"
    					Style="{StaticResource BackButtonStyle}" />
    			<Image x:Name="TitleLogo"
    				Grid.Column="1"
    				Source="../Assets/Icons/FloTilesHeader.png"
    				Stretch="Uniform"
    				Width="280"
    				Height="140"
    				Margin="-20,0,20,0"
    				IsHitTestVisible="False"
    				HorizontalAlignment="Left" />
    		</Grid>
    
    		<!-- View shown when the not snapped -->
    		<Viewbox x:Name="UnsnappedView"
    				Margin="10,0,-10,0"
    				Grid.Row="1"
    				Grid.ColumnSpan="2"
    				MaxWidth="1250"
    				MaxHeight="700"
    				VerticalAlignment="Stretch"
    				HorizontalAlignment="Stretch">
    			<Grid Width="1250"
    				Height="700"
    				Margin="20,0"
    				HorizontalAlignment="Center">
    				<Grid.ColumnDefinitions>
    					<ColumnDefinition Width="450"></ColumnDefinition>
    					<ColumnDefinition Width="10"></ColumnDefinition>
    					<ColumnDefinition Width="750"></ColumnDefinition>
    				</Grid.ColumnDefinitions>
    				<!-- Vertical scrolling item list -->
    				<ListBox x:Name="LandscapeListView"
    						AutomationProperties.AutomationId="ItemsListView"
    						AutomationProperties.Name="Items"
    						Width="450"
    						Height="660"
    						Grid.Row="1"
    						VerticalAlignment="Top"
    						FontFamily="Segoe UI Light"
    						FontWeight="Light"
    						FontSize="16"
    						Foreground="White"
    						HorizontalAlignment="Stretch"
    						ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
    						ItemContainerStyle="{StaticResource ScoreboardLBItem}"
    						SelectionChanged="ItemListView_SelectionChanged"
    						Style="{StaticResource ScoreboardLBStyle}"
    						ItemTemplate="{StaticResource ScoreBoardMenuItemTemplate}">
    				</ListBox>
    
    				<!-- Details for selected item -->
    				<Grid x:Name="DetailsGrid"
    					HorizontalAlignment="Stretch"
    					VerticalAlignment="Top"
    					Grid.Column="2"
    					Width="750"
    					Height="700"
    					Margin="0">
    					<Grid.RowDefinitions>
    						<RowDefinition Height="65"></RowDefinition>
    						<RowDefinition Height="*"></RowDefinition>
    					</Grid.RowDefinitions>
    					<Grid Height="65"
    						Width="750"
    						Background="#4B0082">
    						<Grid.ColumnDefinitions>
    							<ColumnDefinition Width="*" />
    							<ColumnDefinition Width="0.75*" />
    							<ColumnDefinition Width="0.75*" />
    							<ColumnDefinition Width="0.75*" />
    						</Grid.ColumnDefinitions>
    						<TextBlock Grid.Column="0"
    								Text="Level"
    								FontFamily="Segoe UI Light"
    								FontSize="24"
    								FontWeight="SemiBold"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"></TextBlock>
    						<TextBlock Grid.Column="1"
    								Text="Rating"
    								FontFamily="Segoe UI Light"
    								FontSize="24"
    								FontWeight="SemiBold"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"></TextBlock>
    						<TextBlock Grid.Column="2"
    								Text="Moves"
    								FontFamily="Segoe UI Light"
    								FontSize="24"
    								Margin="0,0,0,0"
    								FontWeight="SemiBold"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"></TextBlock>
    						<TextBlock Grid.Column="3"
    								Text="Duration"
    								FontFamily="Segoe UI Light"
    								FontSize="24"
    								Margin="0,0,0,0"
    								FontWeight="SemiBold"
    								HorizontalAlignment="Center"
    								VerticalAlignment="Center"></TextBlock>
    					</Grid>
    					<ListBox x:Name="itemDetail"
    							AutomationProperties.AutomationId="SubItemsListView"
    							AutomationProperties.Name="SubItems"
    							TabIndex="1"
    							Grid.Row="1"
    							Width="750"
    							VerticalAlignment="Top"
    							Margin="0,0,0,0"
    							Padding="0"
    							FontFamily="Segoe UI Light"
    							FontWeight="Light"
    							FontSize="16"
    							Foreground="White"
    							ItemContainerStyle="{StaticResource ScoreboardLBItemNR}"
    							ItemsSource="{Binding SelectedItem.SubMenuItems, ElementName=LandscapeListView}"
    							ItemTemplate="{StaticResource ScoreBoardDetailsTemplate}"
    							Style="{StaticResource ScoreboardLBStyle}">
    						<ListBox.ItemsPanel>
    							<ItemsPanelTemplate>
    								<StackPanel></StackPanel>
    							</ItemsPanelTemplate>
    						</ListBox.ItemsPanel>
    					</ListBox>
    				</Grid>
    
    				<Border Grid.ColumnSpan="3"
    						VerticalAlignment="Top"
    						Margin="0,-83,0,80"
    						Width="350"
    						Height="66"
    						HorizontalAlignment="Center"
    						Background="Indigo">
    					<TextBlock FontFamily="Segoe UI Light"
    							FontSize="56"
    							FontWeight="Light"
    							Foreground="White"
    							HorizontalAlignment="Center"
    							VerticalAlignment="Center"
    							Margin="0,4,0,-4"
    							Text="Scoreboard"></TextBlock>
    				</Border>
    			</Grid>
    		</Viewbox>
    
    		<!-- Vertical scrolling list only used when snapped -->
    		<ListView x:Name="SnappedView"
    				AutomationProperties.AutomationId="SnappedListView"
    				AutomationProperties.Name="Grouped Items"
    				Grid.Row="1"
    				Visibility="Collapsed"
    				Margin="0,-10,0,0"
    				Padding="10,0,0,60"
    				ItemsSource="{Binding Source={StaticResource snappedItemsViewSource}}"
    				ItemTemplate="{StaticResource SnappedScoreboardItemTemplate}"
    				SelectionMode="None"
    				IsSwipeEnabled="false"
    				IsItemClickEnabled="True">
    
    			<ListView.GroupStyle>
    				<GroupStyle>
    					<GroupStyle.HeaderTemplate>
    						<DataTemplate>
    							<Grid Margin="2,20,2,10">
    								<Border Background="Indigo"
    										Width="300"
    										Height="50">
    									<StackPanel Orientation="Horizontal">
    										<Grid Width="40"
    											Height="40" 
    											Margin="4">
    											<Border Background="#0276FD"></Border>
    											<Border>
    												<Border.Background>
    													<LinearGradientBrush EndPoint="1.436,1.406"
    																		StartPoint="0.552,0.456">
    														<GradientStop Color="#00FFFFFF"
    																	Offset="0.081" />
    														<GradientStop Color="White"
    																	Offset="0.912" />
    														<GradientStop Color="#00FFFFFF"
    																	Offset="0.011" />
    													</LinearGradientBrush>
    												</Border.Background>
    												<Image Source="{Binding Menu.IconImage}"
    													Stretch="Uniform" />
    											</Border>
    										</Grid>
    										<TextBlock Text="{Binding Menu.Title}"
    												Margin="10,-4,10,10"
    												VerticalAlignment="Center"
    												Style="{StaticResource GroupHeaderTextStyleSnapped}"
    												TextWrapping="Wrap" />
    									</StackPanel>
    								</Border>
    							</Grid>
    						</DataTemplate>
    					</GroupStyle.HeaderTemplate>
    					<GroupStyle.Panel>
    						<ItemsPanelTemplate>
    							<VariableSizedWrapGrid Margin="0,-200,0,200"
    												Orientation="Horizontal"
    												MaximumRowsOrColumns="2"
    												VerticalChildrenAlignment="Center" />
    						</ItemsPanelTemplate>
    					</GroupStyle.Panel>
    				</GroupStyle>
    			</ListView.GroupStyle>
    		</ListView>
    
    		<VisualStateManager.VisualStateGroups>
    
    			<!-- Visual states reflect the application's view state -->
    			<VisualStateGroup x:Name="ApplicationViewStates">
    				<VisualState x:Name="FullScreenLandscape" />
    
    				<VisualState x:Name="Filled" />
    
    				<VisualState x:Name="FullScreenPortrait" />
    
    				<VisualState x:Name="Snapped">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource SnappedBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="250" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="125" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,10,0,-10" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="UnsnappedView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Collapsed" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedView"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Visible" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    	</Grid>
    </common:LayoutAwarePage>
    C#
    /// <summary>
    /// A page that displays a group title, a list of items within the group, and details for
    /// the currently selected item.
    /// </summary>
    public sealed partial class ScoreBoardPage : LayoutAwarePage
    {
    	public ScoreBoardPage()
    	{
    		this.InitializeComponent();
    	}
    
    	#region Page state management
    
    	/// <summary>
    	/// Populates the page with content passed during navigation.  Any saved state is also
    	/// provided when recreating a page from a prior session.
    	/// </summary>
    	/// <param name="navigationParameter">The parameter value passed to
    	/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
    	/// </param>
    	/// <param name="pageState">A dictionary of state preserved by this page during an earlier
    	/// session.  This will be null the first time a page is visited.</param>
    	protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		// TODO: Assign a bindable group to this.DefaultViewModel["Group"]
    		// TODO: Assign a collection of bindable items to this.DefaultViewModel["Items"]
    		IEnumerable<MenuItem> dataSource = (gameState != null) ? gameState.MenuItems : GameMenuSource.GetMenuItems();
    
    		var menuItems = from item in dataSource
    						where (item.Info.UniqueId != "PAT_11")
    						group item by item into g
    						select new GroupData { Menu = g.Key, SubMenuItems = g.Key.SubMenuItems };
    		this.DefaultViewModel["MenuItems"] = menuItems.ToList();
    
    		if (pageState == null)
    		{
    			// When this is a new page, select the first item automatically unless logical page
    			// navigation is being used (see the logical page navigation #region below.)
    			if (!this.UsingLogicalPageNavigation() && this.itemsViewSource.View != null)
    			{
    				this.itemsViewSource.View.MoveCurrentToFirst();
    			}
    		}
    		else
    		{
    			// Restore the previously saved state associated with this page
    			if (pageState.ContainsKey("SelectedItem") && this.itemsViewSource.View != null)
    			{
    				// TODO: Invoke this.itemsViewSource.View.MoveCurrentTo() with the selected
    				//       item as specified by the value of pageState["SelectedItem"]
    				this.itemsViewSource.View.MoveCurrentTo(pageState["SelectedItem"]);
    			}
    		}
    	}
    
    	/// <summary>
    	/// Preserves state associated with this page in case the application is suspended or the
    	/// page is discarded from the navigation cache.  Values must conform to the serialization
    	/// requirements of <see cref="SuspensionManager.SessionState"/>.
    	/// </summary>
    	/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
    	protected override void SaveState(Dictionary<String, Object> pageState)
    	{
    		if (this.itemsViewSource.View != null)
    		{
    			pageState["SelectedItem"] = this.itemsViewSource.View.CurrentItem;
    			// TODO: Derive a serializable navigation parameter and assign it to
    			//       pageState["SelectedItem"]
    		}
    	}
    
    	#endregion
    
    	#region Logical page navigation
    
    	// Visual state management typically reflects the four application view states directly
    	// (full screen landscape and portrait plus snapped and filled views.)  The split page is
    	// designed so that the snapped and portrait view states each have two distinct sub-states:
    	// either the item list or the details are displayed, but not both at the same time.
    	//
    	// This is all implemented with a single physical page that can represent two logical
    	// pages.  The code below achieves this goal without making the user aware of the
    	// distinction.
    
    	/// <summary>
    	/// Invoked to determine whether the page should act as one logical page or two.
    	/// </summary>
    	/// <param name="viewState">The view state for which the question is being posed, or null
    	/// for the current view state.  This parameter is optional with null as the default
    	/// value.</param>
    	/// <returns>True when the view state in question is portrait or snapped, false
    	/// otherwise.</returns>
    	private bool UsingLogicalPageNavigation(ApplicationViewState? viewState = null)
    	{
    		//if (viewState == null) viewState = ApplicationView.Value;
    		//return viewState == ApplicationViewState.FullScreenPortrait ||
    		//    viewState == ApplicationViewState.Snapped;
    		return false;
    	}
    
    	/// <summary>
    	/// Invoked when an item within the list is selected.
    	/// </summary>
    	/// <param name="sender">The GridView (or ListView when the application is Snapped)
    	/// displaying the selected item.</param>
    	/// <param name="e">Event data that describes how the selection was changed.</param>
    	void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    	{
    		// Invalidate the view state when logical page navigation is in effect, as a change
    		// in selection may cause a corresponding change in the current logical page.  When
    		// an item is selected this has the effect of changing from displaying the item list
    		// to showing the selected item's details.  When the selection is cleared this has the
    		// opposite effect.
    		if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
    	}
    
    	/// <summary>
    	/// Invoked when the page's back button is pressed.
    	/// </summary>
    	/// <param name="sender">The back button instance.</param>
    	/// <param name="e">Event data that describes how the back button was clicked.</param>
    	protected override void GoBack(object sender, RoutedEventArgs e)
    	{
    		if (this.UsingLogicalPageNavigation() && LandscapeListView.SelectedItem != null)
    		{
    			// When logical page navigation is in effect and there's a selected item that
    			// item's details are currently displayed.  Clearing the selection will return to
    			// the item list.  From the user's point of view this is a logical backward
    			// navigation.
    			this.LandscapeListView.SelectedItem = null;
    		}
    		else
    		{
    			// When logical page navigation is not in effect, or when there is no selected
    			// item, use the default back button behavior.
    			base.GoBack(sender, e);
    		}
    	}
    
    	/// <summary>
    	/// Invoked to determine the name of the visual state that corresponds to an application
    	/// view state.
    	/// </summary>
    	/// <param name="viewState">The view state for which the question is being posed.</param>
    	/// <returns>The name of the desired visual state.  This is the same as the name of the
    	/// view state except when there is a selected item in portrait and snapped views where
    	/// this additional logical page is represented by adding a suffix of _Detail.</returns>
    	protected override string DetermineVisualState(ApplicationViewState viewState)
    	{
    		// Update the back button's enabled state when the view state changes
    		var logicalPageBack = this.UsingLogicalPageNavigation(viewState) && this.LandscapeListView.SelectedItem != null;
    		var physicalPageBack = this.Frame != null && this.Frame.CanGoBack;
    		this.DefaultViewModel["CanGoBack"] = logicalPageBack || physicalPageBack;
    
    		// Determine visual states for landscape layouts based not on the view state, but
    		// on the width of the window.  This page has one layout that is appropriate for
    		// 1366 virtual pixels or wider, and another for narrower displays or when a snapped
    		// application reduces the horizontal space available to less than 1366.
    		if (viewState == ApplicationViewState.Filled ||
    			viewState == ApplicationViewState.FullScreenLandscape)
    		{
    			var windowWidth = Window.Current.Bounds.Width;
    			if (windowWidth >= 1366) return "FullScreenLandscape";
    			return "Filled";
    		}
    
    		// When in portrait or snapped start with the default visual state name, then add a
    		// suffix when viewing details instead of the list
    		var defaultStateName = base.DetermineVisualState(viewState);
    		//return logicalPageBack ? defaultStateName + "_Detail" : defaultStateName;
    		return defaultStateName;
    	}
    
    	#endregion
    }

    MyFloTilesPage

    Image 25

    This page allows the user to create custom FloTiles puzzles either by using the Webcam or by selecting an image from the MyPictures folder.

    Code:

    XML
    <common:LayoutAwarePage x:Name="pageRoot"
    						x:Class="FloTiles.Views.MyFloTilesPage"
    						DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    						xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    						xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    						xmlns:local="using:FloTiles.Views"
    						xmlns:common="using:FloTiles.Common"
    						xmlns:converters="using:FloTiles.Converters"
    						xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    						xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    						mc:Ignorable="d">
    
    	<Page.Resources>
    		<Style x:Key="MetroButtonStyle"
    			TargetType="Button">
    			<Setter Property="FontFamily"
    					Value="Segoe UI Symbol" />
    			<Setter Property="FontWeight"
    					Value="SemiBold" />
    			<Setter Property="FontSize"
    					Value="64" />
    			<Setter Property="HorizontalAlignment"
    					Value="Stretch" />
    			<Setter Property="VerticalAlignment"
    					Value="Stretch" />
    			<Setter Property="Template">
    				<Setter.Value>
    					<ControlTemplate TargetType="Button">
    						<Grid x:Name="ButtonContent">
    							<VisualStateManager.VisualStateGroups>
    								<VisualStateGroup x:Name="ApplicationViewStates">
    									<VisualState x:Name="FullScreenLandscape" />
    									<VisualState x:Name="Filled" />
    									<VisualState x:Name="FullScreenPortrait" />
    									<VisualState x:Name="Snapped" />
    								</VisualStateGroup>
    								<VisualStateGroup x:Name="CommonStates">
    									<VisualState x:Name="Normal" />
    									<VisualState x:Name="PointerOver">
    										<Storyboard>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonOutline"
    																		Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="#44FFFFFF" />
    											</ObjectAnimationUsingKeyFrames>
    										</Storyboard>
    									</VisualState>
    									<VisualState x:Name="Pressed">
    										<Storyboard>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonOutline"
    																		Storyboard.TargetProperty="(Ellipse.Stroke).(SolidColorBrush.Color)">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="White" />
    											</ObjectAnimationUsingKeyFrames>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonOutline"
    																		Storyboard.TargetProperty="(Ellipse.Fill).(SolidColorBrush.Color)">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="White" />
    											</ObjectAnimationUsingKeyFrames>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Glyph"
    																		Storyboard.TargetProperty="Foreground">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="Black" />
    											</ObjectAnimationUsingKeyFrames>
    										</Storyboard>
    									</VisualState>
    									<VisualState x:Name="Disabled">
    										<Storyboard>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonOutline"
    																		Storyboard.TargetProperty="(Ellipse.Stroke).(SolidColorBrush.Color)">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="#323232" />
    											</ObjectAnimationUsingKeyFrames>
    											<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Glyph"
    																		Storyboard.TargetProperty="Foreground">
    												<DiscreteObjectKeyFrame KeyTime="0"
    																		Value="#323232" />
    											</ObjectAnimationUsingKeyFrames>
    										</Storyboard>
    									</VisualState>
    								</VisualStateGroup>
    								<VisualStateGroup x:Name="FocusStates">
    									<VisualState x:Name="Focused" />
    									<VisualState x:Name="Unfocused" />
    									<VisualState x:Name="PointerFocused" />
    								</VisualStateGroup>
    							</VisualStateManager.VisualStateGroups>
    							<Ellipse x:Name="ButtonOutline"
    									Width="{TemplateBinding Width}"
    									Height="{TemplateBinding Height}"
    									Fill="Transparent"
    									HorizontalAlignment="Center"
    									Stroke="White"
    									StrokeThickness="2"
    									VerticalAlignment="Center" />
    							<TextBlock x:Name="Glyph"
    									Text="{TemplateBinding Content}"
    									FontFamily="{TemplateBinding FontFamily}"
    									FontSize="{TemplateBinding FontSize}"
    									Foreground="White"
    									HorizontalAlignment="Center"
    									VerticalAlignment="Center"
    									Opacity="1" />
    						</Grid>
    					</ControlTemplate>
    				</Setter.Value>
    			</Setter>
    		</Style>
    
    		<!-- Collection of items displayed by this page -->
    		<CollectionViewSource x:Name="menuItemsSource"
    							Source="{Binding MenuItems}"
    							IsSourceGrouped="False" />
    		<CollectionViewSource x:Name="levelSource"
    							Source="{Binding Levels}"
    							IsSourceGrouped="False" />
    		<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityHelper"></converters:BooleanToVisibilityConverter>
    	</Page.Resources>
    
    	<!--
    		This grid acts as a root panel for the page that defines two rows:
    		* Row 0 contains the back button and page title
    		* Row 1 contains the rest of the page layout
    	-->
    	<Grid Style="{StaticResource LayoutRootStyle}">
    		<Grid.RowDefinitions>
    			<RowDefinition Height="140" />
    			<RowDefinition Height="*" />
    		</Grid.RowDefinitions>
    
    		<Grid.Background>
    			<ImageBrush ImageSource="../Assets/Images/Background/BG08.jpg"
    						Stretch="UniformToFill"
    						AlignmentX="Left"
    						AlignmentY="Top"></ImageBrush>
    		</Grid.Background>
    
    		<!-- Back button and Title Logo -->
    		<Grid>
    			<Grid.ColumnDefinitions>
    				<ColumnDefinition Width="Auto" />
    				<ColumnDefinition Width="*" />
    			</Grid.ColumnDefinitions>
    			<Button x:Name="backButton"
    					Click="GoBack"
    					IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
    					Style="{StaticResource BackButtonStyle}" />
    			<Image x:Name="TitleLogo"
    				Grid.Column="1"
    				Source="../Assets/Icons/FloTilesHeader.png"
    				Stretch="Uniform"
    				Width="280"
    				Height="140"
    				Margin="-20,0,20,0"
    				IsHitTestVisible="False"
    				HorizontalAlignment="Left" />
    		</Grid>
    
    		<Grid x:Name="InnerGrid"
    			Grid.Row="1"
    			Width="1200"
    			Height="650"
    			Margin="0,-20,0,10"
    			Background="#EE111111">
    			<Grid.RowDefinitions>
    				<RowDefinition Height="300"></RowDefinition>
    				<RowDefinition Height="200"></RowDefinition>
    				<RowDefinition Height="150"></RowDefinition>
    			</Grid.RowDefinitions>
    			<TextBlock x:Name="Header"
    					Grid.Row="0"
    					Text="Create FloTile"
    					FontSize="56"
    					FontFamily="Segoe UI Light"
    					FontWeight="Light"
    					Foreground="#9A9A9A"
    					HorizontalAlignment="Left"
    					VerticalAlignment="Top"
    					Margin="10,5"></TextBlock>
    			<Button x:Name="SelectImageBtn"
    					Style="{StaticResource TextPrimaryButtonStyle}"
    					Width="150"
    					Height="190"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Click="OnSelectImage"
    					PointerEntered="OnPointerEntered"
    					PointerExited="OnPointerExited">
    				<Grid>
    					<Border Background="#0276FD"
    							Width="150"
    							Height="150"
    							VerticalAlignment="Top"></Border>
    					<Border Width="150"
    							Height="150"
    							VerticalAlignment="Top">
    						<Border.Background>
    							<LinearGradientBrush EndPoint="1.436,1.406"
    												StartPoint="0.552,0.456">
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.081" />
    								<GradientStop Color="White"
    											Offset="0.912" />
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.011" />
    							</LinearGradientBrush>
    						</Border.Background>
    					</Border>
    					<Image Source="../Assets/Icons/Levels/MyPictures.png"
    						Stretch="Uniform"
    						Width="150"
    						Height="150"
    						VerticalAlignment="Top" />
    					<Border Height="40"
    							VerticalAlignment="Bottom"
    							Background="#646464"></Border>
    					<TextBlock Text="Select Image"
    							Margin="0,-10,0,10"
    							VerticalAlignment="Bottom"
    							HorizontalAlignment="Center"
    							FontSize="22"
    							Style="{StaticResource GroupHeaderTextStyle}" />
    				</Grid>
    			</Button>
    
    			<Button x:Name="CapturePhotoBtn"
    					Style="{StaticResource TextPrimaryButtonStyle}"
    					Width="150"
    					Height="190"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Click="OnCapturePhoto"
    					PointerEntered="OnPointerEntered"
    					PointerExited="OnPointerExited"
    					Visibility="{Binding IsCameraView, Converter={StaticResource BoolToVisibilityHelper}}">
    				<Grid>
    					<Border Background="#0276FD"
    							Width="150"
    							Height="150"
    							VerticalAlignment="Top"></Border>
    					<Border Width="150"
    							Height="150"
    							VerticalAlignment="Top">
    						<Border.Background>
    							<LinearGradientBrush EndPoint="1.436,1.406"
    												StartPoint="0.552,0.456">
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.081" />
    								<GradientStop Color="White"
    											Offset="0.912" />
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.011" />
    							</LinearGradientBrush>
    						</Border.Background>
    					</Border>
    					<Image Source="../Assets/Icons/Levels/Camera.png"
    						Stretch="Uniform"
    						Width="150"
    						Height="150"
    						VerticalAlignment="Top" />
    					<Border Height="40"
    							VerticalAlignment="Bottom"
    							Background="#646464"></Border>
    					<TextBlock Text="Capture Photo"
    							Margin="0,-10,0,10"
    							VerticalAlignment="Bottom"
    							HorizontalAlignment="Center"
    							FontSize="22"
    							Style="{StaticResource GroupHeaderTextStyle}" />
    				</Grid>
    			</Button>
    
    			<Image x:Name="ImgPreview"
    				Width="400"
    				Height="280"
    				Stretch="Fill"
    				Source="{Binding SelectedImage}"
    				HorizontalAlignment="Right"
    				VerticalAlignment="Center"
    				Margin="10,0"></Image>
    
    			<Grid Grid.Row="1">
    				<Grid.RowDefinitions>
    					<RowDefinition></RowDefinition>
    					<RowDefinition></RowDefinition>
    				</Grid.RowDefinitions>
    				<Grid.ColumnDefinitions>
    					<ColumnDefinition Width="0.38*"></ColumnDefinition>
    					<ColumnDefinition Width="10"></ColumnDefinition>
    					<ColumnDefinition Width="0.62*"></ColumnDefinition>
    				</Grid.ColumnDefinitions>
    				<TextBlock Text="Challenge Type"
    						FontSize="22"
    						FontFamily="Segoe UI Light"
    						FontWeight="Light"
    						HorizontalAlignment="Right"
    						VerticalAlignment="Center"
    						Margin="10,0"></TextBlock>
    				<ComboBox x:Name="ChallengeCB"
    						Grid.Row="0"
    						Grid.Column="2"
    						Width="300"
    						Height="34"
    						HorizontalAlignment="Left"
    						VerticalAlignment="Center"
    						ItemsSource="{Binding Source={StaticResource menuItemsSource}}"
    						SelectionChanged="OnChallengeSelected">
    					<ComboBox.ItemTemplate>
    						<DataTemplate>
    							<Grid>
    								<Grid.ColumnDefinitions>
    									<ColumnDefinition Width="220"></ColumnDefinition>
    									<ColumnDefinition Width="30"></ColumnDefinition>
    								</Grid.ColumnDefinitions>
    								<TextBlock Text="{Binding Menu.Title}"
    										HorizontalAlignment="Left"
    										VerticalAlignment="Center"></TextBlock>
    								<Image Grid.Column="1"
    									Width="30"
    									Height="30"
    									HorizontalAlignment="Right"
    									VerticalAlignment="Center"
    									Source="../Assets/Icons/LevelStatus/QLock.png"
    									Stretch="Uniform"
    									ToolTipService.ToolTip="Level is Locked!"
    									Visibility="{Binding Menu.IsLocked, Converter={StaticResource BoolToVisibilityHelper}}"></Image>
    							</Grid>
    						</DataTemplate>
    					</ComboBox.ItemTemplate>
    				</ComboBox>
    				<TextBlock Grid.Row="1"
    						Text="Challenge Level"
    						FontSize="22"
    						FontFamily="Segoe UI Light"
    						FontWeight="Light"
    						HorizontalAlignment="Right"
    						VerticalAlignment="Center"
    						Margin="10,0"></TextBlock>
    				<ComboBox x:Name="LevelCB"
    						Grid.Row="1"
    						Grid.Column="2"
    						Width="300"
    						Height="34"
    						ItemsSource="{Binding Source={StaticResource levelSource}}"
    						HorizontalAlignment="Left"
    						VerticalAlignment="Center"
    						SelectionChanged="OnLevelSelected"></ComboBox>
    			</Grid>
    
    			<Button Grid.Row="2"
    					Width="120"
    					Height="120"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Margin="10"
    					FontSize="56"
    					Content="&#xE0AE;"
    					IsEnabled="{Binding CanCreateFloTile}"
    					Style="{StaticResource MetroButtonStyle}"
    					Click="OnCreateFloTile"
    					ToolTipService.ToolTip="Create FloTile">
    			</Button>
    		</Grid>
    
    		<!-- Grid for Snapped State -->
    		<Grid x:Name="SnappedGrid"
    			Grid.Row="1"
    			Width="330"
    			Height="660"
    			Margin="0,0,0,0"
    			Background="#EE111111"
    			Visibility="Collapsed">
    			<Grid.RowDefinitions>
    				<RowDefinition Height="240"></RowDefinition>
    				<RowDefinition Height="170"></RowDefinition>
    				<RowDefinition Height="75"></RowDefinition>
    				<RowDefinition Height="75"></RowDefinition>
    				<RowDefinition Height="90"></RowDefinition>
    			</Grid.RowDefinitions>
    
    			<Image x:Name="SnappedImgPreview"
    				Width="320"
    				Height="220"
    				Stretch="Fill"
    				Source="{Binding SelectedImage}"
    				HorizontalAlignment="Center"
    				VerticalAlignment="Center"
    				Margin="0"></Image>
    
    			<Button x:Name="SnappedSelectImageBtn"
    					Style="{StaticResource TextPrimaryButtonStyle}"
    					Grid.Row="1"
    					Width="120"
    					Height="152"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Click="OnSelectImage"
    					PointerEntered="OnPointerEntered"
    					PointerExited="OnPointerExited">
    				<Grid>
    					<Border Background="#0276FD"
    							Width="120"
    							Height="120"
    							VerticalAlignment="Top"></Border>
    					<Border Width="120"
    							Height="120"
    							VerticalAlignment="Top">
    						<Border.Background>
    							<LinearGradientBrush EndPoint="1.436,1.406"
    												StartPoint="0.552,0.456">
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.081" />
    								<GradientStop Color="White"
    											Offset="0.912" />
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.011" />
    							</LinearGradientBrush>
    						</Border.Background>
    					</Border>
    					<Image Source="../Assets/Icons/Levels/MyPictures.png"
    						Stretch="Uniform"
    						Width="120"
    						Height="120"
    						VerticalAlignment="Top" />
    					<Border Height="32"
    							VerticalAlignment="Bottom"
    							Background="#646464"></Border>
    					<TextBlock Text="Select Image"
    							Margin="0,-10,0,10"
    							VerticalAlignment="Bottom"
    							HorizontalAlignment="Center"
    							FontSize="16"
    							Style="{StaticResource GroupHeaderTextStyle}" />
    				</Grid>
    			</Button>
    
    			<Button x:Name="SnappedCapturePhotoBtn"
    					Style="{StaticResource TextPrimaryButtonStyle}"
    					Grid.Row="1"
    					Width="120"
    					Height="152"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					Click="OnCapturePhoto"
    					PointerEntered="OnPointerEntered"
    					PointerExited="OnPointerExited"
    					Visibility="{Binding IsCameraView, Converter={StaticResource BoolToVisibilityHelper}}">
    				<Grid>
    					<Border Background="#0276FD"
    							Width="120"
    							Height="120"
    							VerticalAlignment="Top"></Border>
    					<Border Width="120"
    							Height="120"
    							VerticalAlignment="Top">
    						<Border.Background>
    							<LinearGradientBrush EndPoint="1.436,1.406"
    												StartPoint="0.552,0.456">
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.081" />
    								<GradientStop Color="White"
    											Offset="0.912" />
    								<GradientStop Color="#00FFFFFF"
    											Offset="0.011" />
    							</LinearGradientBrush>
    						</Border.Background>
    					</Border>
    					<Image Source="../Assets/Icons/Levels/Camera.png"
    						Stretch="Uniform"
    						Width="120"
    						Height="120"
    						VerticalAlignment="Top" />
    					<Border Height="32"
    							VerticalAlignment="Bottom"
    							Background="#646464"></Border>
    					<TextBlock Text="Capture Photo"
    							Margin="0,-10,0,10"
    							VerticalAlignment="Bottom"
    							HorizontalAlignment="Center"
    							FontSize="16"
    							Style="{StaticResource GroupHeaderTextStyle}" />
    				</Grid>
    			</Button>
    
    			<TextBlock Text="Challenge Type"
    					Grid.Row="2"
    					FontSize="16"
    					FontFamily="Segoe UI Light"
    					FontWeight="Light"
    					HorizontalAlignment="Left"
    					VerticalAlignment="Top"
    					Margin="5,0"></TextBlock>
    			<ComboBox x:Name="SnappedChallengeCB"
    					Grid.Row="2"
    					Width="300"
    					Height="34"
    					Margin="-5,2,5,-2"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					ItemsSource="{Binding Source={StaticResource menuItemsSource}}"
    					SelectionChanged="OnSnappedChallengeSelected">
    				<ComboBox.ItemTemplate>
    					<DataTemplate>
    						<Grid>
    							<Grid.ColumnDefinitions>
    								<ColumnDefinition Width="220"></ColumnDefinition>
    								<ColumnDefinition Width="30"></ColumnDefinition>
    							</Grid.ColumnDefinitions>
    							<TextBlock Text="{Binding Menu.Title}"
    									HorizontalAlignment="Left"
    									VerticalAlignment="Center"></TextBlock>
    							<Image Grid.Column="1"
    								Width="30"
    								Height="30"
    								HorizontalAlignment="Right"
    								VerticalAlignment="Center"
    								Source="../Assets/Icons/LevelStatus/QLock.png"
    								Stretch="Uniform"
    								ToolTipService.ToolTip="Level is Locked!"
    								Visibility="{Binding Menu.IsLocked, Converter={StaticResource BoolToVisibilityHelper}}"></Image>
    						</Grid>
    					</DataTemplate>
    				</ComboBox.ItemTemplate>
    			</ComboBox>
    			<TextBlock Grid.Row="3"
    					Text="Challenge Level"
    					FontSize="16"
    					FontFamily="Segoe UI Light"
    					FontWeight="Light"
    					HorizontalAlignment="Left"
    					VerticalAlignment="Top"
    					Margin="5,0"></TextBlock>
    			<ComboBox x:Name="SnappedLevelCB"
    					Grid.Row="3"
    					Width="300"
    					Height="34"
    					Margin="-5,2,5,-2"
    					ItemsSource="{Binding Source={StaticResource levelSource}}"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Center"
    					SelectionChanged="OnSnappedLevelSelected"></ComboBox>
    
    			<Button Grid.Row="4"
    					Width="60"
    					Height="60"
    					HorizontalAlignment="Center"
    					VerticalAlignment="Top"
    					Margin="0,-5,0,5"
    					FontSize="36"
    					Content="&#xE0AE;"
    					IsEnabled="{Binding CanCreateFloTile}"
    					Style="{StaticResource MetroButtonStyle}"
    					Click="OnCreateFloTile"
    					ToolTipService.ToolTip="Create FloTile">
    			</Button>
    		</Grid>
    
    		<VisualStateManager.VisualStateGroups>
    
    			<!-- Visual states reflect the application's view state -->
    			<VisualStateGroup x:Name="ApplicationViewStates">
    				<VisualState x:Name="FullScreenLandscape" />
    				<VisualState x:Name="Filled">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="InnerGrid"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="950" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ImgPreview"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="350" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ImgPreview"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="245" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    
    				<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
    				<VisualState x:Name="FullScreenPortrait">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource PortraitBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="InnerGrid"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="750" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Header"
    													Storyboard.TargetProperty="FontSize">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="36" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ImgPreview"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="280" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ImgPreview"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="196" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ImgPreview"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="10,-10,10,10" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    
    				<!-- The back button and title have different styles when snapped -->
    				<VisualState x:Name="Snapped">
    					<Storyboard>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
    													Storyboard.TargetProperty="Style">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="{StaticResource SnappedBackButtonStyle}" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Width">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="250" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Height">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="125" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TitleLogo"
    													Storyboard.TargetProperty="Margin">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="0,10,0,-10" />
    						</ObjectAnimationUsingKeyFrames>
    
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="InnerGrid"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Collapsed" />
    						</ObjectAnimationUsingKeyFrames>
    						<ObjectAnimationUsingKeyFrames Storyboard.TargetName="SnappedGrid"
    													Storyboard.TargetProperty="Visibility">
    							<DiscreteObjectKeyFrame KeyTime="0"
    													Value="Visible" />
    						</ObjectAnimationUsingKeyFrames>
    					</Storyboard>
    				</VisualState>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    	</Grid>
    </common:LayoutAwarePage>
    C#
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class MyFloTilesPage : FloTiles.Common.LayoutAwarePage
    {
    	#region Fields
    
    	GroupData _selectedChallenge = null;
    	string _selectedLevel = string.Empty;
    
    	#endregion
    
    	public MyFloTilesPage()
    	{
    		this.InitializeComponent();
    	}
    
    	/// <summary>
    	/// Populates the page with content passed during navigation.  Any saved state is also
    	/// provided when recreating a page from a prior session.
    	/// </summary>
    	/// <param name="navigationParameter">The parameter value passed to
    	/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
    	/// </param>
    	/// <param name="pageState">A dictionary of state preserved by this page during an earlier
    	/// session.  This will be null the first time a page is visited.</param>
    	async protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
    	{
    		string uniqueId = navigationParameter as string;
    
    		DefaultViewModel["IsCameraView"] = uniqueId.Equals("MFT_CAM");
    
    		IEnumerable<MenuItem> dataSource = (gameState != null) ? gameState.MenuItems : GameMenuSource.GetMenuItems();
    
    		var menuItems = from item in dataSource
    						where (item.Info.UniqueId != "PAT_11")
    						group item by item into g
    						select new GroupData { Menu = g.Key, SubMenuItems = g.Key.SubMenuItems };
    		this.DefaultViewModel["MenuItems"] = menuItems.ToList();
    
    		this.DefaultViewModel["Levels"] = new List<string> { 
    												"Level 1",
    												"Level 2",
    												"Level 3",
    												"Level 4",
    												"Level 5",
    												"Level 6",
    												"Level 7",
    												"Level 8",
    												};
    
    		if (pageState != null)
    		{
    			string imgName = pageState["SelectedImageName"] as string;
    
    			StorageFolder imageBackupFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ImageBackup", CreationCollisionOption.OpenIfExists);
    			StorageFile file = await imageBackupFolder.GetFileAsync(imgName);
    
    			if (file != null)
    			{
    				using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
    				{
    					BitmapImage bitmap = new BitmapImage();
    					stream.Seek(0);
    					bitmap.SetSource(stream);
    
    					if (bitmap != null)
    					{
    						DefaultViewModel["SelectedImage"] = bitmap;
    						DefaultViewModel["SelectedImageName"] = file.Name;
    					}
    				}
    			}
    
    			SnappedChallengeCB.SelectedIndex = ChallengeCB.SelectedIndex = (int)pageState["ChallengeType"];
    			SnappedLevelCB.SelectedIndex = LevelCB.SelectedIndex = (int)pageState["ChallengeLevel"];
    			DefaultViewModel["IsCameraView"] = (bool)pageState["IsCameraView"];
    			_selectedChallenge = (GroupData)ChallengeCB.SelectedItem;
    			_selectedLevel = (string)LevelCB.SelectedItem;
    			CheckCanCreateFloTile();
    		}
    	}
    
    	/// <summary>
    	/// Preserves state associated with this page in case the application is suspended or the
    	/// page is discarded from the navigation cache.  Values must conform to the serialization
    	/// requirements of <see cref="SuspensionManager.SessionState"/>.
    	/// </summary>
    	/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
    	protected override void SaveState(Dictionary<String, Object> pageState)
    	{
    		pageState["IsCameraView"] = DefaultViewModel["IsCameraView"];
    		pageState["SelectedImageName"] = DefaultViewModel.ContainsKey("SelectedImageName") ? DefaultViewModel["SelectedImageName"] : null;
    		// CHECK                                                                                                                                                                            
    		pageState["ChallengeType"] = ChallengeCB.SelectedIndex;
    		pageState["ChallengeLevel"] = LevelCB.SelectedIndex;
    	}
    
    	async private void OnSelectImage(object sender, RoutedEventArgs e)
    	{
    		if (ApplicationView.Value == ApplicationViewState.Snapped)
    		{
    			bool isUnsnapped = ApplicationView.TryUnsnap();
    			if (!isUnsnapped)
    			{
    				MessageDialog msgDlg = new MessageDialog("Please unsnap the application to select the image!", "FloTiles");
    				msgDlg.Commands.Add(new UICommand("Ok"));
    				msgDlg.CancelCommandIndex = 0;
    				await msgDlg.ShowAsync();
    				return;
    			}
    		}
    
    		FileOpenPicker filePicker = new FileOpenPicker()
    		{
    			FileTypeFilter = { ".jpg", ".png" },
    			CommitButtonText = "Select",
    			SuggestedStartLocation = PickerLocationId.PicturesLibrary,
    			ViewMode = PickerViewMode.Thumbnail
    		};
    
    		StorageFile file = await filePicker.PickSingleFileAsync();
    
    		if (file == null)
    			return;
    
    		await Task.Run(async () =>
    		{
    			StorageFolder imageBackupFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ImageBackup", CreationCollisionOption.OpenIfExists);
    			foreach (var fileBak in await imageBackupFolder.GetFilesAsync())
    			{
    				await fileBak.DeleteAsync();
    			}
    
    			file = await file.CopyAsync(imageBackupFolder, String.Format("FloTileImage{0}", file.FileType), NameCollisionOption.ReplaceExisting);
    		});
    
    		if (file == null)
    			return;
    
    		using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
    		{
    			BitmapImage bitmap = new BitmapImage();
    			stream.Seek(0);
    			bitmap.SetSource(stream);
    
    			if (bitmap != null)
    			{
    				DefaultViewModel["SelectedImage"] = bitmap;
    				DefaultViewModel["SelectedImageName"] = file.Name;
    				CheckCanCreateFloTile();
    			}
    		}
    	}
    
    	private void OnPointerEntered(object sender, PointerRoutedEventArgs e)
    	{
    		Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(Windows.UI.Core.CoreCursorType.Hand, 1);
    	}
    
    	private void OnPointerExited(object sender, PointerRoutedEventArgs e)
    	{
    		Window.Current.CoreWindow.PointerCursor = new Windows.UI.Core.CoreCursor(Windows.UI.Core.CoreCursorType.Arrow, 0);
    	}
    
    	private void OnChallengeSelected(object sender, SelectionChangedEventArgs e)
    	{
    		_selectedChallenge = (GroupData)ChallengeCB.SelectedItem;
    		CheckCanCreateFloTile();
    	}
    
    	private void OnSnappedChallengeSelected(object sender, SelectionChangedEventArgs e)
    	{
    		_selectedChallenge = (GroupData)SnappedChallengeCB.SelectedItem;
    		CheckCanCreateFloTile();
    	}
    
    	private void CheckCanCreateFloTile()
    	{
    		DefaultViewModel["CanCreateFloTile"] = DefaultViewModel.ContainsKey("SelectedImage") &&
    											(DefaultViewModel["SelectedImage"] != null) &&
    											(_selectedChallenge != null) &&
    											(!_selectedChallenge.Menu.IsLocked) &&
    											(!String.IsNullOrWhiteSpace(_selectedLevel));
    	}
    
    	private void OnLevelSelected(object sender, SelectionChangedEventArgs e)
    	{
    		_selectedLevel = (string)LevelCB.SelectedItem;
    		CheckCanCreateFloTile();
    	}
    
    	private void OnSnappedLevelSelected(object sender, SelectionChangedEventArgs e)
    	{
    		_selectedLevel = (string)SnappedLevelCB.SelectedItem;
    		CheckCanCreateFloTile();
    	}
    
    	private void OnCreateFloTile(object sender, RoutedEventArgs e)
    	{
    		string pattern = _selectedChallenge.Menu.Info.Pattern.ToString();
    
    		string level = _selectedLevel.Replace(" ", string.Empty);
    
    		string imgPath = DefaultViewModel["SelectedImageName"] as string;
    
    		string naviParam = (bool)DefaultViewModel["IsCameraView"] ? string.Format("MFT_CAM|{0}|{1}|{2}", pattern, level, imgPath) :
    																	string.Format("MFT_ALB|{0}|{1}|{2}", pattern, level, imgPath);
    
    		this.Frame.Navigate(typeof(GamePage), naviParam);
    	}
    
    	async private void OnCapturePhoto(object sender, RoutedEventArgs e)
    	{
    		if (ApplicationView.Value == ApplicationViewState.Snapped)
    		{
    			bool isUnsnapped = ApplicationView.TryUnsnap();
    			if (!isUnsnapped)
    			{
    				MessageDialog msgDlg = new MessageDialog("Please unsnap the application to select the image!", "FloTiles");
    				msgDlg.Commands.Add(new UICommand("Ok"));
    				msgDlg.CancelCommandIndex = 0;
    				await msgDlg.ShowAsync();
    				return;
    			}
    		}
    
    		StorageFile file = null;
    
    		var camera = new CameraCaptureUI();
    		file = await camera.CaptureFileAsync(CameraCaptureUIMode.Photo);
    
    		if (file == null)
    			return;
    
    		await Task.Run(async () =>
    		{
    			StorageFolder imageBackupFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("ImageBackup", CreationCollisionOption.OpenIfExists);
    			foreach (var fileBak in await imageBackupFolder.GetFilesAsync())
    			{
    				await fileBak.DeleteAsync();
    			}
    
    			file = await file.CopyAsync(imageBackupFolder, String.Format("FloTileImage{0}", file.FileType), NameCollisionOption.ReplaceExisting);
    		});
    
    		if (file == null)
    			return;
    
    		using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read))
    		{
    			BitmapImage bitmap = new BitmapImage();
    			stream.Seek(0);
    			bitmap.SetSource(stream);
    
    			if (bitmap != null)
    			{
    				DefaultViewModel["SelectedImage"] = bitmap;
    				DefaultViewModel["SelectedImageName"] = file.Name;
    				CheckCanCreateFloTile();
    			}
    		}
    	}
    }

    FloTiles Logo

    Before I end this article, the FloTiles Logo deserves a mention. A great logo for an app is like the icing (with cherries) on the cake! It makes the app even better and helps a person to remember the app.

    Initially, I wanted to create a logo that would play with the letters F and T. Another design I had was some kind of mashup with tiles of different sizes as in the game you have an image which is split into smaller tiles. I wanted the logo to symbolize the soul of the FloTiles game. It took me two weeks to create the logo. In the first week, I spent several hours doing rough sketches and trying out different variations. By the end of the first week, I had arrived at the final rough sketch. It took me another week to convert the rough sketch to digital form, add colors to it and finally polish it.

    Image 26

    The final logo portrays the fluidity of the FloTiles game in action. If you look carefully you can see four tiles, each of a different color. Now in the FloTiles game, whenever you select a tile and move it, its size scales up a bit and its opacity also reduces. The same is depicted in the logo. It appears as if the tile which the blue color is selected and is being moved. Also if you look closely, behind the blue tile, there is a combination of the letters F and T symbolizing the letters of FloTiles

    Image 27

    Now when I retrospect on those two weeks, I feel satisfied that the logo has turned out well and the whole effort was worth it.

    EndPoint

    This is the longest article I have written yet. I hope you liked it. Please do visit Windows Store and install FloTiles (Download link) and let me know of your comments/suggestions. Don't forget to rate the app too.

    History

    • December 1, 2012: FloTiles article updated.
    • November 29,2012: FloTiles v1.02 released in Windows Store.
    • November 28, 2012: FloTiles v1.01 released in Windows Store.
    • November 21, 2012: FloTiles v1.00 released in Windows Store.
    • October 17, 2012: FloTiles concept released.

    License

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


    Written By
    Software Developer
    United States United States
    An individual with more than a decade of experience in desktop computing and mobile app development primarily on the Microsoft platform. He loves programming in C#, WPF & XAML related technologies.
    Current interests include web application development, developing rich user experiences across various platforms and exploring his creative side.

    Ratish's personal blog: wpfspark.wordpress.com

    Comments and Discussions

     
    QuestionCool app Pin
    AndySolo17-Oct-17 4:34
    AndySolo17-Oct-17 4:34 
    GeneralMy vote of 5 Pin
    sandeepmsandy14-Dec-12 0:37
    sandeepmsandy14-Dec-12 0:37 
    GeneralRe: My vote of 5 Pin
    Ratish Philip14-Dec-12 0:39
    Ratish Philip14-Dec-12 0:39 
    GeneralMy vote of 5 Pin
    Vamshi Mukka10-Dec-12 20:52
    Vamshi Mukka10-Dec-12 20:52 
    GeneralRe: My vote of 5 Pin
    Ratish Philip10-Dec-12 23:49
    Ratish Philip10-Dec-12 23:49 
    GeneralRe: My vote of 5 Pin
    Carsten V2.03-Oct-13 9:02
    Carsten V2.03-Oct-13 9:02 
    GeneralRe: My vote of 5 Pin
    Ratish Philip4-Oct-13 5:58
    Ratish Philip4-Oct-13 5:58 
    GeneralMy vote of 5 Pin
    fredatcodeproject16-Oct-12 11:44
    professionalfredatcodeproject16-Oct-12 11:44 
    GeneralRe: My vote of 5 Pin
    Ratish Philip16-Oct-12 17:45
    Ratish Philip16-Oct-12 17:45 
    Thanks a lot!
    cheers,
    Ratish Philip
    Blog: http://wpfspark.wordpress.com/

    GeneralMy vote of 5 Pin
    Florian Rappl16-Oct-12 11:43
    professionalFlorian Rappl16-Oct-12 11:43 
    GeneralRe: My vote of 5 Pin
    Ratish Philip16-Oct-12 17:45
    Ratish Philip16-Oct-12 17:45 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.