Download FloTiles from Windows Store
Table of Contents
FloTiles is an innovative jigsaw puzzle game which brings in a few rules of its own and makes the game unique and more exciting.
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.
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.
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.
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.
The technique to keep the game interesting lies in the arrangement of the tiles which I have termed 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.
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.
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.
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.
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.
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
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
.
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.
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.
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
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
.
The Common module consists of the common definitions, enums, constants, structures etc which is used throughout the app. Here are a few of them:
This enum defines the various kinds of FloPatterns available.
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,
}
This enum defines the various Levels
available.
public enum GameLevelType : int
{
None = 0,
Level0 = 1,
Level1 = 2,
Level2 = 3,
Level3 = 4,
Level4 = 5,
Level5 = 6,
Level6 = 7,
Level7 = 8,
Level8 = 9
}
This enum defines the different states of a Level
within a Challenge
.
public enum LevelStatusType : int
{
None = 0,
Locked = 1,
Playing = 2,
OneStar = 3,
TwoStars = 4,
ThreeStars = 5
}
This enum defines the different states of the Game within a Level
.
public enum GameStatusType : int
{
None = 0,
Initializing = 1,
Loading = 2,
TapToPlay = 3,
Playing = 4,
Paused = 5,
Solved = 6,
GameOver = 7,
PracticeGameOver = 8,
CustomGameOver = 9
}
This enum defines the different types of GameOver states for a Level
.
public enum GameOverType : int
{
None = 0,
LevelComplete = 1,
NextChallengeUnlocked = 2,
LevelCompletePostNCUnlock = 3,
ChallengeComplete = 4
}
This class defines the structure used to identify a tile location within the GameBoard.
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()
{
return (this.Row * 101) + this.Column;
}
}
This module contains the core classes of FloTiles
which are used to create the game based on the Challenge
and the Level
selected.
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.
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
protected override Size MeasureOverride(Size availableSize)
{
Size availableItemSize = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
foreach (UIElement child in Children.Where(c => c != null))
{
child.Measure(availableItemSize);
}
if ((MaxRows > 0) && (MaxCols > 0))
{
Size resultSize = new Size(MaxCols * _itemWidth, MaxRows * _itemHeight);
return resultSize;
}
return availableSize;
}
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>();
CleanUp();
Pattern = pattern;
MaxRows = maxRows;
MaxCols = maxCols;
_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);
child.RenderTransform = _gamePanelHelper.CreateTransform(-_itemWidth, -_itemHeight, NORMAL_SCALE, NORMAL_SCALE);
}
}
if (isRestoring)
{
RestoreSuspendedState(serData);
_boardTiles.AddRange(Children.OfType<GameTile>().OrderBy(x => x.CurrentIndex));
}
else
{
_boardTiles.AddRange(_gamePanelHelper.ShuffleGameTiles(gameTiles.AsEnumerable()));
}
InvalidateMeasure();
IsComposing = true;
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
private void UpdateFluidLayout(bool showEasing = true)
{
if (_boardTiles == null)
return;
int dragTileIndex = -1;
if (_dragTile != null)
dragTileIndex = _boardTiles.IndexOf(_dragTile);
for (int index = 0; index < _boardTiles.Count; index++)
{
UIElement element = _boardTiles[index];
if (element == null)
continue;
if (index == dragTileIndex)
continue;
element.Arrange(new Rect(0, 0, element.DesiredSize.Width,
element.DesiredSize.Height));
Point pos = _gamePanelHelper.GetPointFromIndex(index);
Storyboard transition;
if (element == _lastDragElement)
{
if (!showEasing)
{
transition = _gamePanelHelper.CreateTransition(element, pos, FIRST_TIME_ANIMATION_DURATION, null);
}
else
{
TimeSpan duration = (DragEasing != null) ? DEFAULT_ANIMATION_TIME_WITH_EASING : DEFAULT_ANIMATION_TIME_WITHOUT_EASING;
transition = _gamePanelHelper.CreateTransition(element, pos, duration, DragEasing);
}
transition.Completed += (s, e) =>
{
if (_lastDragElement != null)
{
_lastDragElement.SetValue(Canvas.ZIndexProperty, 0);
_lastDragElement = null;
}
};
}
else {
if (!showEasing)
{
transition = _gamePanelHelper.CreateTransition(element, pos, TimeSpan.FromMilliseconds(FIRST_TIME_ANIMATION_DURATION.Milliseconds + (index * _animDelay)), null);
}
else
{
TimeSpan duration = (ElementEasing != null) ? TimeSpan.FromMilliseconds(DEFAULT_ANIMATION_TIME_WITH_EASING.Milliseconds + (index * _animDelay)) :
TimeSpan.FromMilliseconds(DEFAULT_ANIMATION_TIME_WITHOUT_EASING.Milliseconds + (index * _animDelay));
transition = _gamePanelHelper.CreateTransition(element, pos, duration, ElementEasing);
}
}
transition.Begin();
}
}
private bool UpdateDragElementIndex(int newIndex)
{
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;
}
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
async internal void BeginFluidDragAsync(UIElement child, Point position, Pointer pointer)
{
if ((child == null) || (!IsComposing))
return;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
child.Opacity = DragOpacity;
child.SetValue(Canvas.ZIndexProperty, Z_INDEX_DRAG);
child.CapturePointer(pointer);
_dragTile = child;
_lastDragElement = null;
_dragStartIndex = _boardTiles.IndexOf(child);
_dragEndIndex = -1;
_dragStartPoint = new Point(position.X * DragScale, position.Y * DragScale);
});
}
async internal void FluidDragAsync(UIElement child, Point position, Point positionInParent)
{
if ((child == null) || (!IsComposing))
return;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
if ((_dragTile != null) && (_gamePanelHelper != null))
{
_dragTile.RenderTransform = _gamePanelHelper.CreateTransform(positionInParent.X - _dragStartPoint.X,
positionInParent.Y - _dragStartPoint.Y,
DragScale,
DragScale);
Point currentPt = positionInParent;
int index = _gamePanelHelper.GetIndexFromPoint(currentPt);
if ((index == -1) || (index >= _boardTiles.Count))
{
index = _boardTiles.Count - 1;
}
if (UpdateDragElementIndex(index))
{
InvalidateArrange();
}
}
});
}
async internal void EndFluidDragAsync(UIElement child, Point position, Point positionInParent, Pointer pointer)
{
if ((child == null) || (!IsComposing))
return;
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
if ((_dragTile != null) && (_gamePanelHelper != null))
{
_dragTile.RenderTransform = _gamePanelHelper.CreateTransform(positionInParent.X - _dragStartPoint.X,
positionInParent.Y - _dragStartPoint.Y,
DragScale,
DragScale);
child.Opacity = NORMAL_OPACITY;
child.SetValue(Canvas.ZIndexProperty, Z_INDEX_INTERMEDIATE);
child.ReleasePointerCapture(pointer);
_lastDragElement = _dragTile;
_dragEndIndex = _boardTiles.IndexOf(_dragTile);
_dragTile = null;
if (_dragStartIndex != _dragEndIndex)
{
var eventHandler = this.TileMoved;
if (eventHandler != null)
eventHandler(this, null);
}
await CheckIfGameOverAsync();
}
InvalidateArrange();
});
}
#endregion
}
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.
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
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);
if ((maxRows % 2) == 0)
{
isTop = row < halfRows;
}
else
{
if (row == halfRows)
{
isCenterHeight = true;
isTop = false;
}
else
{
isTop = row < halfRows;
}
}
halfCols = (int)((double)maxCols / (double)2);
if ((maxCols % 2) == 0)
{
isLeft = column < halfCols;
}
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;
}
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);
}
}
internal int GetIndexFromCellIndex(int index)
{
int result = -1;
if (_cellIndex.ContainsKey(index))
{
Cell cell = _cellIndex[index];
result = ((cell.Row * _maxCols) + cell.Column);
}
return result;
}
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;
}
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;
}
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;
}
}
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);
}
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;
}
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;
}
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;
TransformGroup transform = new TransformGroup();
transform.Children.Add(scale);
transform.Children.Add(translation);
return transform;
}
internal Storyboard CreateTransition(UIElement element, Point newLocation, TimeSpan period, EasingFunctionBase easing)
{
Duration duration = new Duration(period);
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)");
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)");
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)");
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;
}
internal IEnumerable<GameTile> ShuffleGameTiles(IEnumerable<GameTile> tiles)
{
int totalNum = _maxRows * _maxCols;
GameTile[] result = new GameTile[totalNum];
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);
enc.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)tileWidth, (uint)tileHeight, 96, 96, frameBytes.ToArray());
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
}
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.
<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>
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
}
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:
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.
[DataContract]
public abstract class MenuItemBase : BindableBase
{
private static Uri _baseUri = new Uri("ms-appx:///");
#region Properties
#region Status
private LevelStatusType _status = LevelStatusType.None;
[DataMember]
public LevelStatusType Status
{
get { return _status; }
set { this.SetProperty(ref this._status, value); }
}
#endregion
#region Info
private GameInfo _info = null;
[DataMember]
public GameInfo Info
{
get { return _info; }
set { this.SetProperty(ref this._info, value); }
}
#endregion
#region Title
private string _title = string.Empty;
[DataMember]
public string Title
{
get { return _title; }
set { this.SetProperty(ref this._title, value); }
}
#endregion
#region Description
private string _description = string.Empty;
[DataMember]
public string Description
{
get { return _description; }
set { this.SetProperty(ref this._description, value); }
}
#endregion
#region IconImagePath
private string _iconImagePath = string.Empty;
[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
}
This class derives from MenuItemBase
and represents the Challenge
in the game.
[DataContract]
public class MenuItem : MenuItemBase
{
#region Properties
#region SubMenuItems
private ObservableCollection<SubMenuItem> _subMenuItems = new ObservableCollection<SubMenuItem>();
[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
}
This class derives from MenuItemBase
and represents a Level
within a Challenge
. A MenuItems
contains a collection of several SubMenuItems
.
[DataContract]
public class SubMenuItem : MenuItemBase
{
#region Properties
#region Menu
#region ItemColor
private SolidColorBrush _itemColor = null;
[DataMember]
public SolidColorBrush ItemColor
{
get { return _itemColor; }
set { this.SetProperty(ref this._itemColor, value); }
}
#endregion
#region ItemSpan
private int _itemSpan = 1;
[DataMember]
public int ItemSpan
{
get { return _itemSpan; }
set { this.SetProperty(ref this._itemSpan, value); }
}
#endregion
[DataMember]
private MenuItem _menu = null;
public MenuItem Menu
{
get { return _menu; }
set { this.SetProperty(ref this._menu, value); }
}
#endregion
#region TotalMoves
private string _totalMoves = Constants.NO_VALUE;
public string TotalMoves
{
get { return _totalMoves; }
set { this.SetProperty(ref this._totalMoves, value); }
}
#endregion
#region TotalDuration
private string _totalDuration = Constants.NO_VALUE;
public string TotalDuration
{
get { return _totalDuration; }
set { this.SetProperty(ref this._totalDuration, value); }
}
#endregion
#region IsLevel
private bool _isLevel = true;
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
}
This class represents the grouping of a MenuItem
and its SubMenuItems
collection.
public class GroupData : BindableBase
{
#region Menu
private MenuItem _menu = null;
public MenuItem Menu
{
get { return _menu; }
set { this.SetProperty(ref this._menu, value); }
}
#endregion
#region SubMenuItems
private ObservableCollection<SubMenuItem> _subMenuItems = null;
public ObservableCollection<SubMenuItem> SubMenuItems
{
get { return _subMenuItems; }
set { this.SetProperty(ref this._subMenuItems, value); }
}
#endregion
}
This class contains the hardcoded list of MenuItems
and SubMenuItems
representing the Challenges
and their respective Levels
in the game.
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;
}
}
This class is represents the serialized state of a MenuItem
.
[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; }
}
This class represents the serialized state of a SubMenuItem
.
[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; }
}
This class represents the serialized state of a GameTile
.
[DataContract]
public class SerializedGameTile
{
[DataMember]
public int HomeIndex { get; set; }
[DataMember]
public int InitialIndex { get; set; }
[DataMember]
public int CurrentIndex { get; set; }
}
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
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.
Code:
<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>
-->
<CollectionViewSource x:Name="groupedItemsViewSource"
Source="{Binding MenuItems}"
IsSourceGrouped="true"
ItemsPath="SubMenuItems" />
</Page.Resources>
-->
<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>
-->
<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>
-->
<SemanticZoom x:Name="SemanticZoomView"
Grid.Row="1">
<SemanticZoom.ZoomedInView>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
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;
}
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)
{
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)
{
var group = ((sender as FrameworkElement).DataContext) as GroupData;
if (group != null)
{
string uniqueId = ((MenuItem)group.Menu).Info.UniqueId;
Type pageType = LevelHelper.GetPageType(uniqueId);
this.Frame.Navigate(pageType, uniqueId);
}
}
}
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.
Code:
<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>
-->
<CollectionViewSource x:Name="itemsViewSource"
Source="{Binding SubMenuItems}" />
</Page.Resources>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape" />
<VisualState x:Name="Filled" />
-->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton"
Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource PortraitBackButtonStyle}" />
</ObjectAnimationUsingKeyFrames>
-->
-->
</Storyboard>
</VisualState>
-->
<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>
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;
}
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState, GameStateViewModel gameState)
{
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);
}
}
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:
<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>
-->
<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>
-->
<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>
-->
<MediaElement x:Name="applauseSound"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="10"
Height="10"
AutoPlay="False"
AudioCategory="GameMedia"
IsLooping="False"></MediaElement>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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=""
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>
-->
<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>
-->
<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>
-->
<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>
-->
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape" />
<VisualState x:Name="Filled" />
-->
<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>
-->
<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>
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
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);
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);
await InitializePatternGridAsync(submenuItem.Info.Pattern, row, col);
if (savedGameStatus != GameStatusType.None)
{
DefaultViewModel["GameStatus"] = savedGameStatus;
if ((savedGameStatus == GameStatusType.Playing) && (!_gameTimer.IsEnabled))
_gameTimer.Start();
}
}
}
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;
string nextLevelId = LevelHelper.GetNextLevel(currentUniqueId);
if (!String.IsNullOrWhiteSpace(nextLevelId))
{
var nextSubMenuItem = gVM.GetSubMenuItem(nextLevelId);
if (nextSubMenuItem != null)
nextSubMenuItem.Status = LevelStatusType.Playing;
}
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
}
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.
Code:
<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>
-->
<CollectionViewSource x:Name="itemsViewSource"
Source="{Binding MenuItems}" />
<CollectionViewSource x:Name="snappedItemsViewSource"
Source="{Binding MenuItems}"
IsSourceGrouped="true"
ItemsPath="SubMenuItems" />
</Page.Resources>
-->
<Grid Style="{StaticResource LayoutRootStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
-->
-->
<Grid.Background>
<ImageBrush ImageSource="../Assets/Images/Background/BG06.jpg"
Stretch="UniformToFill"
AlignmentX="Left"
AlignmentY="Top"></ImageBrush>
</Grid.Background>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
-->
<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>
public sealed partial class ScoreBoardPage : LayoutAwarePage
{
public ScoreBoardPage()
{
this.InitializeComponent();
}
#region Page state management
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
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)
{
if (!this.UsingLogicalPageNavigation() && this.itemsViewSource.View != null)
{
this.itemsViewSource.View.MoveCurrentToFirst();
}
}
else
{
if (pageState.ContainsKey("SelectedItem") && this.itemsViewSource.View != null)
{
this.itemsViewSource.View.MoveCurrentTo(pageState["SelectedItem"]);
}
}
}
protected override void SaveState(Dictionary<String, Object> pageState)
{
if (this.itemsViewSource.View != null)
{
pageState["SelectedItem"] = this.itemsViewSource.View.CurrentItem;
}
}
#endregion
#region Logical page navigation
private bool UsingLogicalPageNavigation(ApplicationViewState? viewState = null)
{
return false;
}
void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.UsingLogicalPageNavigation()) this.InvalidateVisualState();
}
protected override void GoBack(object sender, RoutedEventArgs e)
{
if (this.UsingLogicalPageNavigation() && LandscapeListView.SelectedItem != null)
{
this.LandscapeListView.SelectedItem = null;
}
else
{
base.GoBack(sender, e);
}
}
protected override string DetermineVisualState(ApplicationViewState viewState)
{
var logicalPageBack = this.UsingLogicalPageNavigation(viewState) && this.LandscapeListView.SelectedItem != null;
var physicalPageBack = this.Frame != null && this.Frame.CanGoBack;
this.DefaultViewModel["CanGoBack"] = logicalPageBack || physicalPageBack;
if (viewState == ApplicationViewState.Filled ||
viewState == ApplicationViewState.FullScreenLandscape)
{
var windowWidth = Window.Current.Bounds.Width;
if (windowWidth >= 1366) return "FullScreenLandscape";
return "Filled";
}
var defaultStateName = base.DetermineVisualState(viewState);
return defaultStateName;
}
#endregion
}
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:
<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>
-->
<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>
-->
<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>
-->
<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=""
IsEnabled="{Binding CanCreateFloTile}"
Style="{StaticResource MetroButtonStyle}"
Click="OnCreateFloTile"
ToolTipService.ToolTip="Create FloTile">
</Button>
</Grid>
-->
<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=""
IsEnabled="{Binding CanCreateFloTile}"
Style="{StaticResource MetroButtonStyle}"
Click="OnCreateFloTile"
ToolTipService.ToolTip="Create FloTile">
</Button>
</Grid>
<VisualStateManager.VisualStateGroups>
-->
<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>
-->
<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>
-->
<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>
public sealed partial class MyFloTilesPage : FloTiles.Common.LayoutAwarePage
{
#region Fields
GroupData _selectedChallenge = null;
string _selectedLevel = string.Empty;
#endregion
public MyFloTilesPage()
{
this.InitializeComponent();
}
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();
}
}
protected override void SaveState(Dictionary<String, Object> pageState)
{
pageState["IsCameraView"] = DefaultViewModel["IsCameraView"];
pageState["SelectedImageName"] = DefaultViewModel.ContainsKey("SelectedImageName") ? DefaultViewModel["SelectedImageName"] : null;
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();
}
}
}
}
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.
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
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.
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.
- 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.