Tuesday, November 11, 2014

Colour Memory Game: A silverlight application based on MVVM pattern



Summary: This is an exercise to gain understanding of MVVM pattern through an example sample. The sample application called ‘Colour Memory Game’ will be an Silverlight Application based on MVVM pattern using C#.In this post, we will go steps by steps from Requirement gathering, designing approach, developing  with sample codes to a workable implementation hosting over drop box cloud platform as above.
Requirement Specification: The goal of this sample is to construct a game called ”Colour Memory”. The game board consists of a 4x4 grid, all in all 16 slots. All slots consist of cards facedown. The player is to flip two of these upwards each round, trying to find equals. If the two cards are equal, the player receives one point, and the cards are removed from the game board. Otherwise, the player loses one point and the cards are turned facedown again. This continues until all pairs have been found. After the game is finished, the user would be optionally required to input his/her name and email. User's details and the scores would then be submitted to the database and the user would get notified with the high scores and his position in score rankings.
Prerequisites
• The application should work on Internet Explorer 9 and later versions, other browsers Chrome, Firefox should also support.
• The application should be developed entirely in Silverlight & C#
• HighScore server is supposed to be developed in WCF and MYSQL
• MVVM Pattern should be applied
• The game should be controllable by the arrow keys (to navigate) and press space/enter (to select) (except the operations inside the input field)
• The game should follow the design template illustrated below
• Suggested tools (but not required): o Microsoft Visual Studio 2013 o Microsoft Expression Blend

image
Design Approach: Since we plan to adopt MVVM approach, we should be in a position to develop our GUI, Game Algorithm and DB operations independently not necessarily by the same team. And accordingly our design approaches for independent piece of work should follow. Automated Unit Test scripts can be written independently for each modules- ViewModel and Model.  So Lets initiate our design approaches as follows.
I. GUI Design :  The main GUI template will consists of a game board (may be a grid of cards), a reset button, a label for game information like user directives and scores, ranks/position and Logo.
image
II. Algorithm Design: We will have set of 16 (4 X4) cards, each card can have status as (UnSelected, Selected or Matched). Visual appearance will indicate the status of the card. Card position over the grid layout will be identified with its row/column position. On clicking the ‘Reset Game’ button the cards will be reshuffled. So we will have to develop logic to generate 16 random numbers corresponding to each card.  Based on this, a ideal class diagram would probably be as
image 
III. Database Design: We need to record the high score achieved by player. For that we need the user’s details (name, emailid, password) and score details (emailid, score).
image
Development Approach:  Development of GUI (View), Game Algorithm (Model), and Database can be done independently and simultaneously if required adhering to common integration goal of hook and play eventually.
I. GUI development :  GUI interfaces (Views) will consist of few pure XAML with skeleton code behind, data will be bound to the public properties of the ViewModel classes and UI interaction will be facilitated through the command binding feature of Silverlight.
-ColourMemoGameView.xaml : This is the application’s main UI consists of general layout for card board grid, Game Reset Button and Game/Score information.
image
XAML
  1. <UserControl x:Class="dSucs.ColourMemoGame.View.ColourMemoGameView"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.    xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
  7.    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
  8.             xmlns:local="clr-namespace:dSucs.ColourMemoGame.View"
  9.             xmlns:cmg="clr-namespace:dSucs.ColourMemoGame"
  10.    mc:Ignorable="d"
  11.    d:DesignHeight="576" d:DesignWidth="720" Tag="GameView00" >
  12.     <UserControl.Resources>
  13.         <cmg:ViewTemplateChooser x:Key="TemplateChooser" />
  14.         <Storyboard x:Name="sbResetGame">
  15.             <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationZ)" Storyboard.TargetName="cardBoardContent">
  16.                 <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="90"/>
  17.                 <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="180"/>
  18.                 <EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="90"/>
  19.                 <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="0"/>
  20.             </DoubleAnimationUsingKeyFrames>
  21.             <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="cardBoardContent">
  22.                 <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  23.                     <DiscreteObjectKeyFrame.Value>
  24.                         <Visibility>Visible</Visibility>
  25.                     </DiscreteObjectKeyFrame.Value>
  26.                 </DiscreteObjectKeyFrame>
  27.                 <DiscreteObjectKeyFrame KeyTime="0:0:0.6">
  28.                     <DiscreteObjectKeyFrame.Value>
  29.                         <Visibility>Collapsed</Visibility>
  30.                     </DiscreteObjectKeyFrame.Value>
  31.                 </DiscreteObjectKeyFrame>
  32.                 <DiscreteObjectKeyFrame KeyTime="0:0:0.9">
  33.                     <DiscreteObjectKeyFrame.Value>
  34.                         <Visibility>Visible</Visibility>
  35.                     </DiscreteObjectKeyFrame.Value>
  36.                 </DiscreteObjectKeyFrame>
  37.             </ObjectAnimationUsingKeyFrames>
  38.         </Storyboard>
  39.         <Style x:Key="btnZoom" TargetType="Button">
  40.             <Setter Property="Template">
  41.                 <Setter.Value>
  42.                     <ControlTemplate TargetType="Button">
  43.                         <Grid>
  44.                             <ed:Callout AnchorPoint="-0.388,-0.222" CalloutStyle="Oval" Content="" FontSize="14.666999816894531" Stroke="Black" StrokeThickness="0" Margin="0,4,0,-4">
  45.                                 <ed:Callout.Fill>
  46.                                     <RadialGradientBrush RadiusX="0.531" RadiusY="0.643" GradientOrigin="0.887,0.825" Center="0.493,0.52">
  47.                                         <GradientStop Color="#FFEFDFF1" Offset="0.643"/>
  48.                                         <GradientStop Color="#FFD65FD1" Offset="0.891"/>
  49.                                         <GradientStop Color="#FFD65FD1" Offset="0.509"/>
  50.                                     </RadialGradientBrush>
  51.                                 </ed:Callout.Fill>
  52.                             </ed:Callout>
  53.                             <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  54.                         </Grid>
  55.                     </ControlTemplate>
  56.                 </Setter.Value>
  57.             </Setter>
  58.         </Style>
  59.         <Style x:Key="btnScoop" TargetType="Button">
  60.             <Setter Property="Template">
  61.                 <Setter.Value>
  62.                     <ControlTemplate TargetType="Button">
  63.                         <Grid>
  64.                             <ed:Callout AnchorPoint="1.236,1.246" CalloutStyle="Oval" Content="" FontSize="14.666999816894531" Stroke="Black" StrokeThickness="0">
  65.                                 <ed:Callout.Fill>
  66.                                     <RadialGradientBrush RadiusX="0.531" RadiusY="0.643" GradientOrigin="0.887,0.825" Center="0.493,0.52">
  67.                                         <GradientStop Color="#FFEFDFF1" Offset="0.43"/>
  68.                                         <GradientStop Color="#FFD65FD1" Offset="0.891"/>
  69.                                         <GradientStop Color="#FFD65FD1" Offset="0.052"/>
  70.                                     </RadialGradientBrush>
  71.                                 </ed:Callout.Fill>
  72.                             </ed:Callout>
  73.                             <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
  74.                         </Grid>
  75.                     </ControlTemplate>
  76.                 </Setter.Value>
  77.             </Setter>
  78.         </Style>
  79.         <Style x:Key="btnLogin" TargetType="Button">
  80.             <Setter Property="Template">
  81.                 <Setter.Value>
  82.                     <ControlTemplate TargetType="Button">
  83.                         <Grid>
  84.                             <Rectangle Stroke="Black" StrokeThickness="0">
  85.                                 <Rectangle.Fill>
  86.                                     <RadialGradientBrush RadiusX="0.531" RadiusY="0.643" GradientOrigin="0.887,0.825" Center="0.493,0.52">
  87.                                         <GradientStop Color="#FFEFDFF1" Offset="0.743"/>
  88.                                         <GradientStop Color="#FF9AD65F" Offset="0.943"/>
  89.                                         <GradientStop Color="#FF95D65F" Offset="0.057"/>
  90.                                         <GradientStop Color="#FF87C2DD" Offset="0.274"/>
  91.                                     </RadialGradientBrush>
  92.                                 </Rectangle.Fill>
  93.                             </Rectangle>
  94.                             <ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
  95.                         </Grid>
  96.                     </ControlTemplate>
  97.                 </Setter.Value>
  98.             </Setter>
  99.         </Style>
  100.         <Style x:Key="btnInfo" TargetType="Button">
  101.             <Setter Property="Template">
  102.                 <Setter.Value>
  103.                     <ControlTemplate TargetType="Button">
  104.                         <Grid>
  105.                             <Ellipse Stroke="Black" StrokeThickness="0">
  106.                                 <Ellipse.Fill>
  107.                                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  108.                                         <GradientStop Color="#FF4D52CD" Offset="0.009"/>
  109.                                         <GradientStop Color="#FF0C47C5" Offset="0.987"/>
  110.                                         <GradientStop Color="#FFCDECDA" Offset="0.483"/>
  111.                                     </LinearGradientBrush>
  112.                                 </Ellipse.Fill>
  113.                             </Ellipse>
  114.                             <Ellipse Stroke="Black" Margin="10,0,11,0" StrokeThickness="0">
  115.                                 <Ellipse.Fill>
  116.                                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  117.                                         <GradientStop Color="#FF192D85" Offset="0.352"/>
  118.                                         <GradientStop Color="#FF1818A4" Offset="0.691"/>
  119.                                         <GradientStop Color="#FFCDDBEC" Offset="0.483"/>
  120.                                     </LinearGradientBrush>
  121.                                 </Ellipse.Fill>
  122.                             </Ellipse>
  123.                             <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
  124.                         </Grid>
  125.                     </ControlTemplate>
  126.                 </Setter.Value>
  127.             </Setter>
  128.         </Style>
  129.     </UserControl.Resources>
  130.  
  131.     <UserControl.Background>
  132.         <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  133.             <GradientStop Color="Black" Offset="0"/>
  134.             <GradientStop Color="#FFA2CFC9" Offset="0.368"/>
  135.         </LinearGradientBrush>
  136.     </UserControl.Background>
  137.  
  138.  
  139.     <Grid x:Name="LayoutRoot" Margin="1" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Height="576" VerticalAlignment="Center" HorizontalAlignment="Center" Width="745">
  140.         <Grid.Background>
  141.             <RadialGradientBrush>
  142.                 <GradientStop Color="Black" Offset="1"/>
  143.                 <GradientStop Color="#FFA3D4BE" Offset="0.935"/>
  144.             </RadialGradientBrush>
  145.         </Grid.Background>
  146.         <Grid.RenderTransform>
  147.             <CompositeTransform Rotation="0.226"/>
  148.         </Grid.RenderTransform>
  149.         <Grid.ColumnDefinitions>
  150.             <ColumnDefinition Width="401*"/>
  151.             <ColumnDefinition Width="207*"/>
  152.         </Grid.ColumnDefinitions>
  153.  
  154.         <Grid x:Name="GameContainer" Margin="47.049,-13.188,196.472,23.574" MinHeight="450" Height="535" VerticalAlignment="Center" HorizontalAlignment="Left" Width="560" RenderTransformOrigin="0.5,0.5" Grid.ColumnSpan="2">
  155.             <Grid.RenderTransform>
  156.                 <CompositeTransform Rotation="0.502"/>
  157.             </Grid.RenderTransform>
  158.             <Grid.Background>
  159.                 <RadialGradientBrush RadiusX="0.514">
  160.                     <GradientStop Color="#FFCDBBBB" Offset="1"/>
  161.                     <GradientStop Color="#FF7EBBCD"/>
  162.                 </RadialGradientBrush>
  163.             </Grid.Background>
  164.             <ContentControl x:Name="cardBoardContent" Content="{Binding CardBoardVM, Converter={StaticResource TemplateChooser}}" Margin="10,10,49.16,10" Grid.Column="1" Width="470" Height="520" HorizontalAlignment="Center" VerticalAlignment="Center" >
  165.                 <i:Interaction.Triggers>
  166.                     <ei:DataTrigger Binding="{Binding IsResetGame, UpdateSourceTrigger=PropertyChanged}" Value="True">
  167.                         <ei:ControlStoryboardAction Storyboard="{StaticResource sbResetGame}"/>
  168.                     </ei:DataTrigger>
  169.                 </i:Interaction.Triggers>
  170.                 <ContentControl.Projection>
  171.                     <PlaneProjection/>
  172.                 </ContentControl.Projection>
  173.             </ContentControl>
  174.         </Grid>
  175.         <Grid Grid.Column="1" HorizontalAlignment="Right" Height="420.882" Margin="23.694,-3.424,-27.247,0" VerticalAlignment="Top" Width="215" RenderTransformOrigin="0.483,0.541">
  176.             <Grid.RenderTransform>
  177.                 <CompositeTransform Rotation="-13.432" TranslateX="4.113" TranslateY="0.4"/>
  178.             </Grid.RenderTransform>
  179.             <Grid.Background>
  180.                 <RadialGradientBrush>
  181.                     <GradientStop Color="#FFD0DCE6" Offset="0.091"/>
  182.                     <GradientStop Color="#FF73A0C5" Offset="0.426"/>
  183.                 </RadialGradientBrush>
  184.             </Grid.Background>
  185.             <TextBlock x:Name="tbScore" HorizontalAlignment="Center" Margin="-6.376,84.038,12.101,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="209.275" Height="22.937" RenderTransformOrigin="0.5,0.5" Foreground="#FFF9FBF7" FontSize="15" Text="{Binding Path=ScoreInfo, FallbackValue='Score=10, Moves=30'}">
  186.                 <TextBlock.RenderTransform>
  187.                     <CompositeTransform Rotation="14.086"/>
  188.                 </TextBlock.RenderTransform>
  189.             </TextBlock>
  190.             <Image HorizontalAlignment="Left" Height="36.288" Margin="116.417,29.068,0,0" VerticalAlignment="Top" Width="86.419" Source="/SilverLightBlend;component/Images/logo.png" RenderTransformOrigin="0.456,0.892">
  191.                 <Image.RenderTransform>
  192.                     <CompositeTransform Rotation="10.342" TranslateX="-3.334" TranslateY="-1.184"/>
  193.                 </Image.RenderTransform>
  194.             </Image>
  195.             <Button Content="           Reset Game   " HorizontalAlignment="Left" Height="44.289" Margin="-23.195,437.577,0,-60.984" Style="{StaticResource ButtonStyle_ResetGame}" VerticalAlignment="Top" Width="145.926" Command="{Binding CmdResetGame}" TabIndex="17" ToolTipService.ToolTip="Click to reset the currently loaded game"/>
  196.             <TextBox HorizontalAlignment="Left" Height="250.17" Margin="-70.463,135.023,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="204.728" RenderTransformOrigin="0.5,0.5" Background="#FFE6E680" IsReadOnly="True" Text="{Binding Path=MessageInfo, FallbackValue='Click on any card and navigate using arrow keys or mouse, choose any two cards by pressing (Enter Key) or clicking by mouse. If Two cards are matched, a one point will be awared, if matching fails once point will be taken back.'}">
  197.                 <TextBox.RenderTransform>
  198.                     <CompositeTransform Rotation="13.7"/>
  199.                 </TextBox.RenderTransform>
  200.             </TextBox>
  201.  
  202.         </Grid>
  203.         <Image Grid.Column="1" Margin="110.424,0,19.047,520.971" Source="/Images/logo.png" Stretch="Fill"/>
  204.         <TextBlock x:Name="tbTopScore" HorizontalAlignment="Center" Margin="34.643,106,55,0" TextWrapping="Wrap" Width="164" Height="22" RenderTransformOrigin="0.5,0.5" FontSize="12" Text="{Binding Path=HighScoreInfo, FallbackValue='High Score=10, Rank=3'}" Grid.Column="1" VerticalAlignment="Top" Foreground="#FF051E70">
  205.             <TextBlock.RenderTransform>
  206.                 <CompositeTransform Rotation="0.919"/>
  207.             </TextBlock.RenderTransform>
  208.         </TextBlock>
  209.         <Button x:Name="btnInformation" Style="{StaticResource btnInfo}" Grid.Column="1" HorizontalAlignment="Left" Margin="78.724,-0.007,0,0" VerticalAlignment="Top" Width="50" Height="50" BorderThickness="0" Command="{Binding CmdInformation}" ToolTipService.ToolTip="Click for more Information.">
  210.             <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
  211.                 <ContentPresenter Content="Info"></ContentPresenter>
  212.             </StackPanel>
  213.         </Button>
  214.         <TextBlock x:Name="tbHighestEver" HorizontalAlignment="Center" Margin="9.643,125,21,0" TextWrapping="Wrap" Width="223" Height="22" RenderTransformOrigin="0.5,0.5" Foreground="#FF6F0574" Text="{Binding HighestEverScoreInfo, FallbackValue='Highest ever score by some one=10'}" Grid.Column="1" VerticalAlignment="Top" TextDecorations="Underline">
  215.             <TextBlock.RenderTransform>
  216.                 <CompositeTransform Rotation="0.919"/>
  217.             </TextBlock.RenderTransform>
  218.         </TextBlock>
  219.         <Button Command="{Binding CmdFullScreen}" Style="{StaticResource btnZoom}" Grid.Column="1" HorizontalAlignment="Left" Margin="189.643,518,0,0" VerticalAlignment="Top" Width="57" Height="50" ToolTipService.ToolTip="{Binding FsToolTip}">
  220.             <i:Interaction.Triggers>
  221.                 <ei:DataTrigger Binding="{Binding IsFullScreen, UpdateSourceTrigger=PropertyChanged}" Value="true">
  222.                     <ei:ChangePropertyAction PropertyName="Style" Value="{StaticResource btnScoop}" />
  223.                 </ei:DataTrigger>
  224.                 <ei:DataTrigger Binding="{Binding IsFullScreen, UpdateSourceTrigger=PropertyChanged}" Value="false">
  225.                     <ei:ChangePropertyAction PropertyName="Style" Value="{StaticResource btnZoom}" />
  226.                 </ei:DataTrigger>
  227.             </i:Interaction.Triggers>
  228.         </Button>
  229.         <Button Command="{Binding CmdLogin}" Height="33" VerticalAlignment="Top" Background="#FF366791" Foreground="#FF9E2525" Style="{StaticResource btnLogin}" ToolTipService.ToolTip="{Binding SignInMsg, FallbackValue='Click here to Sign Out'}" Margin="9.643,53,19,0" Grid.Column="1" >
  230.             <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
  231.                 <ContentPresenter Content="{Binding Path=WelComeMessage, FallbackValue='Sign In'}" />
  232.             </StackPanel>
  233.         </Button>
  234.     </Grid>
  235. </UserControl>
ColourMemoGameViewModel.cs
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using System.Windows.Threading;
  12. using System.ComponentModel;
  13. using System.Collections.Generic;
  14. using dSucs.ColourMemoGame.Model;
  15. using System.Collections.ObjectModel;
  16. using System.ServiceModel;
  17. using dSucs.ColourMemoGame.ViewModel.MemoGameService;
  18. using System.Threading.Tasks;
  19.  
  20. namespace dSucs.ColourMemoGame.ViewModel
  21. {
  22.     public class ColourMemoGameViewModel : ViewModelBase
  23.     {
  24.         #region Construction
  25.         public ColourMemoGameViewModel()
  26.         {
  27.             Initialize();
  28.         }
  29.  
  30.         private void Initialize()
  31.         {
  32.             IsResetGame = true;
  33.             _CardBoardVM = new CardBoardViewModel();
  34.             _CardBoardVM.OnUpdateGameInfo += UpdateGameInfo;
  35.             _CardBoardVM.OnSaveScore += UpdateScoreInfo;
  36.             _CardBoardVM.OnOpenLoginWindow += OpenLoginWindow;
  37.             this.OnResetGame += _CardBoardVM.ResetGame;
  38.             IsResetGame = false;
  39.             MessageInfo = Helper.Msg001;
  40.             WelComeMessage = Helper.Msg019;
  41.             ScoreInfo = string.Format(Helper.Msg025, 0, 0);
  42.             Task taskInitService = new Task(new Action(InitMemoGameService));
  43.             taskInitService.Start();
  44.         }
  45.  
  46.         #endregion
  47.  
  48.         #region Public Properties
  49.         public CardBoardViewModel CardBoardVM
  50.         {
  51.             get { return _CardBoardVM; }
  52.             set { SetProperty(ref _CardBoardVM, value, "CardBoardVM"); }
  53.         }
  54.  
  55.         public string WelComeMessage
  56.         {
  57.             get
  58.             {
  59.                 return _WelComeMessage;
  60.             }
  61.             set { SetProperty(ref _WelComeMessage, value, "WelComeMessage"); }
  62.         }
  63.  
  64.         public string ScoreInfo
  65.         {
  66.             get
  67.             {
  68.                 return _ScoreInfo;
  69.             }
  70.             set { SetProperty(ref _ScoreInfo, value, "ScoreInfo"); }
  71.         }
  72.  
  73.         public string HighScoreInfo
  74.         {
  75.             get
  76.             {
  77.                 return _HighScoreInfo;
  78.             }
  79.             set { SetProperty(ref _HighScoreInfo, value, "HighScoreInfo"); }
  80.         }
  81.  
  82.         public string HighestEverScoreInfo
  83.         {
  84.             get
  85.             {
  86.                 return _HighestEverScoreInfo;
  87.             }
  88.             set { SetProperty(ref _HighestEverScoreInfo, value, "HighestEverScoreInfo"); }
  89.         }
  90.  
  91.         public string MessageInfo
  92.         {
  93.             get
  94.             {
  95.                 return _MessageInfo;
  96.             }
  97.             set { SetProperty(ref _MessageInfo, value, "MessageInfo"); }
  98.         }
  99.         public bool IsResetGame
  100.         {
  101.             get
  102.             {
  103.                 return _IsResetGame;
  104.             }
  105.             set { SetProperty(ref _IsResetGame, value, "IsResetGame"); }
  106.         }
  107.         public string EmailID
  108.         {
  109.             get
  110.             {
  111.                 return _EmailID;
  112.             }
  113.             set { SetProperty(ref _EmailID, value, "EmailID"); }
  114.         }
  115.  
  116.         public bool IsFullScreen
  117.         {
  118.             get
  119.             {
  120.                 return _IsFullScreen;
  121.             }
  122.             set { SetProperty(ref _IsFullScreen, value, "IsFullScreen"); }
  123.         }
  124.         public string FsToolTip
  125.         {
  126.             get
  127.             {
  128.                 return _FsToolTip;
  129.             }
  130.             set { SetProperty(ref _FsToolTip, value, "FsToolTip"); }
  131.         }
  132.         public string SignInMsg
  133.         {
  134.             get
  135.             {
  136.                 return _SignInMsg;
  137.             }
  138.             set { SetProperty(ref _SignInMsg, value, "SignInMsg"); }
  139.         }
  140.        
  141.  
  142.        
  143.        #endregion
  144.  
  145.         #region Commands
  146.         public ICommand CmdResetGame
  147.         {
  148.             get
  149.             {
  150.                 if (_CmdResetGame == null)
  151.                     _CmdResetGame = new RelayCommand(
  152.                         p => this.ResetGame(),
  153.                         p => true);
  154.  
  155.                 return _CmdResetGame;
  156.             }
  157.         }
  158.         public ICommand CmdInformation
  159.         {
  160.             get
  161.             {
  162.                 if (_CmdInformation == null)
  163.                     _CmdInformation = new RelayCommand(
  164.                         p => this.ShowInformation(),
  165.                         p => true);
  166.  
  167.                 return _CmdInformation;
  168.             }
  169.         }
  170.  
  171.         public ICommand CmdFullScreen
  172.         {
  173.             get
  174.             {
  175.                 if (_CmdFullScreen == null)
  176.                     _CmdFullScreen = new RelayCommand(
  177.                         p => Application.Current.Host.Content.IsFullScreen = Application.Current.Host.Content.IsFullScreen ? false : true,
  178.                         p => true);
  179.  
  180.                 return _CmdFullScreen;
  181.             }
  182.         }
  183.         public ICommand CmdLogin
  184.         {
  185.             get
  186.             {
  187.                 if (_CmdLogin == null)
  188.                     _CmdLogin = new RelayCommand(
  189.                         p => this.SignIn(),
  190.                         p => true);
  191.  
  192.                 return _CmdLogin;
  193.             }
  194.         }
  195.  
  196.         #endregion
  197.  
  198.         #region UI Logics/Helpers
  199.         public void UpdateGameInfo(object sender, CardEventArgs e)
  200.         {
  201.             GameInfo gameInfo = e.Result as GameInfo;
  202.             ScoreInfo = string.Format(Helper.Msg025, gameInfo.Score, gameInfo.Move);
  203.             MessageInfo = gameInfo.MessageInfo;
  204.             if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetUserInfoAsync(EmailID);
  205.         }
  206.  
  207.         private void UpdateScoreInfo(object sender, CardEventArgs e)
  208.         {
  209.             GameInfo gameInfo = e.Result as GameInfo;
  210.             ScoreInfo = string.Format(Helper.Msg025, gameInfo.Score, gameInfo.Move);
  211.             if (!string.IsNullOrEmpty(EmailID))
  212.                 _WCFClient.GetUserInfoAsync(EmailID);
  213.         }
  214.  
  215.         public void UpdateUserInfo(object sender, CardEventArgs e)
  216.         {
  217.             EmailID = e.Result.ToString();
  218.             _CardBoardVM.EmailID = EmailID;
  219.             if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetUserInfoAsync(EmailID);
  220.         }
  221.         private void UpdateUserInfo(object sender, GetUserInfoCompletedEventArgs e)
  222.         {
  223.             _UserInfo = e.Result;
  224.             if (!string.IsNullOrEmpty(_UserInfo.Name))
  225.             {
  226.                 WelComeMessage = string.Format(Helper.Msg028, _UserInfo.Name);
  227.                 HighScoreInfo = string.Format(Helper.Msg026, _UserInfo.BestScore, _UserInfo.Rank);
  228.                 HighestEverScoreInfo = string.Format(Helper.Msg027, _UserInfo.HighestScore);
  229.                 SignInMsg = Helper.Msg020;
  230.             }
  231.  
  232.         }
  233.         private void ResetGame()
  234.         {
  235.             IsResetGame = true;
  236.             ScoreInfo = string.Format(Helper.Msg025, 0, 0);
  237.             MessageInfo = Helper.Msg001;
  238.             OnResetGame(this, new CardEventArgs(string.Empty));
  239.             IsResetGame = false;
  240.         }
  241.  
  242.         private void ShowInformation()
  243.         {
  244.             InfoViewModel vm = InfoViewModel.GetInstance();
  245.             OnOpenWindow(this, new CardEventArgs(vm));
  246.         }
  247.        
  248.         public void ApplyFullScreen()
  249.         {
  250.             IsFullScreen = Application.Current.Host.Content.IsFullScreen;
  251.             if (IsFullScreen)
  252.             {
  253.                 FsToolTip = Helper.Msg022;
  254.             }
  255.             else
  256.             {
  257.                 FsToolTip = Helper.Msg021;
  258.             }
  259.         }
  260.  
  261.         private void SignIn()
  262.         {
  263.             if(String.IsNullOrEmpty(EmailID))
  264.             {
  265.                 LoginViewModel vm = new LoginViewModel();
  266.                 OnOpenWindow(this, new CardEventArgs(vm));
  267.             }
  268.             else if(MessageBox.Show(Helper.Msg023, Helper.Msg024,MessageBoxButton.OKCancel)==MessageBoxResult.OK)
  269.             {
  270.                 WelComeMessage=Helper.Msg019;
  271.                 EmailID = string.Empty;
  272.                 HighScoreInfo = string.Empty;
  273.                 HighestEverScoreInfo = string.Empty;
  274.                 _CardBoardVM.EmailID = string.Empty;
  275.             }
  276.         }
  277.  
  278.         private void OpenLoginWindow(object sender, CardEventArgs args)
  279.         {
  280.             OnOpenWindow(this, args); //Relay the event so that it can be registered from the Bootstrap
  281.         }
  282.  
  283.         private void InitMemoGameService()
  284.         {
  285.             EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
  286.             BasicHttpBinding binding = new BasicHttpBinding();
  287.             _WCFClient = new MemoGameServiceClient(binding, address);
  288.             _WCFClient.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(UpdateUserInfo);
  289.         }
  290.         #endregion
  291.  
  292.         #region Member Fields & Events
  293.         private string _MessageInfo, _WelComeMessage, _ScoreInfo, _HighScoreInfo, _HighestEverScoreInfo, _EmailID, _FsToolTip = Helper.Msg021, _SignInMsg=Helper.Msg029;
  294.         private bool _IsResetGame = false, _IsFullScreen;
  295.         private CardBoardViewModel _CardBoardVM;
  296.         private RelayCommand _CmdResetGame, _CmdInformation, _CmdFullScreen, _CmdLogin;
  297.         public event CmgEventHandler OnResetGame;
  298.         public event CmgEventHandler OnOpenWindow;
  299.         private MemoGameServiceClient _WCFClient;
  300.         UserInfo _UserInfo;
  301.         #endregion
  302.  
  303.  
  304.     }
  305. }

-CardBoardView.xaml  : This contains the datagrid of 16 = 4 X 4 cards
image
XAML
  1. <UserControl x:Class="dSucs.ColourMemoGame.View.CardBoardView"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
  7.    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
  8.    xmlns:local="clr-namespace:dSucs.ColourMemoGame"
  9.    xmlns:lview="clr-namespace:dSucs.ColourMemoGame.View"
  10.                 xmlns:ctrl="clr-namespace:dSucs.ColourMemoGame.Controls"
  11.    mc:Ignorable="d"
  12.    d:DesignHeight="415" d:DesignWidth="500" Width="415" Height="500" HorizontalAlignment="Center" VerticalAlignment="Center">
  13.  
  14.     <UserControl.Background>
  15.         <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  16.             <GradientStop Color="Black" Offset="0"/>
  17.             <GradientStop Color="#FFA2CFC9" Offset="0.368"/>
  18.         </LinearGradientBrush>
  19.     </UserControl.Background>
  20.  
  21.     <Grid x:Name="LayoutRoot" Background="White" Width="420" Height="505" HorizontalAlignment="Center" VerticalAlignment="Center" d:IsLocked="True">
  22.         <Grid.RowDefinitions>
  23.             <RowDefinition></RowDefinition>
  24.         </Grid.RowDefinitions>
  25.         <Grid.ColumnDefinitions>
  26.             <ColumnDefinition></ColumnDefinition>
  27.         </Grid.ColumnDefinitions>
  28.           <ctrl:CustomDataGrid Grid.Column="0" x:Name="dgCardBoard" AlternatingRowBackground="#FFDCE892" RowBackground="#FFDCE892" BorderThickness="3" AutoGenerateColumns="False" ItemsSource="{Binding LstCardVM, UpdateSourceTrigger=PropertyChanged}" HeadersVisibility="None" SelectionMode="Single" HorizontalAlignment="Center" HorizontalScrollBarVisibility="Hidden" RowDetailsVisibilityMode="Visible" VerticalScrollBarVisibility="Hidden" VerticalAlignment="Center" Width="410" Height="491" BorderBrush="#FFBA98D6" GridLinesVisibility="All" RowStyle="{StaticResource CustomDataGridRowStyle}" CellStyle="{StaticResource CustomDatagridCellStyle}" Background="#FFDCE892" d:IsLocked="True" >
  29.             <sdk:DataGrid.Columns>
  30.                 <sdk:DataGridTemplateColumn d:IsLocked="True">
  31.                     <sdk:DataGridTemplateColumn.CellTemplate>
  32.                         <DataTemplate>
  33.                             <Grid Margin="10, 10, 10, 10" Background="White" >
  34.                                 <lview:CardView DataContext="{Binding Item1}"></lview:CardView>
  35.                             </Grid>
  36.                         </DataTemplate>
  37.                     </sdk:DataGridTemplateColumn.CellTemplate>
  38.                 </sdk:DataGridTemplateColumn>
  39.                 <sdk:DataGridTemplateColumn d:IsLocked="True">
  40.                     <sdk:DataGridTemplateColumn.CellTemplate>
  41.                         <DataTemplate>
  42.                             <Grid Margin="10, 10, 10, 10" Background="White">
  43.                                 <lview:CardView DataContext="{Binding Item2}"></lview:CardView>
  44.                             </Grid>
  45.                         </DataTemplate>
  46.                     </sdk:DataGridTemplateColumn.CellTemplate>
  47.                 </sdk:DataGridTemplateColumn>
  48.                 <sdk:DataGridTemplateColumn d:IsLocked="True">
  49.                     <sdk:DataGridTemplateColumn.CellTemplate>
  50.                         <DataTemplate>
  51.                             <Grid Margin="10, 10, 10, 10" Background="White">
  52.                                 <lview:CardView DataContext="{Binding Item3}"></lview:CardView>
  53.                             </Grid>
  54.                         </DataTemplate>
  55.                     </sdk:DataGridTemplateColumn.CellTemplate>
  56.                 </sdk:DataGridTemplateColumn>
  57.                 <sdk:DataGridTemplateColumn d:IsLocked="True">
  58.                     <sdk:DataGridTemplateColumn.CellTemplate>
  59.                         <DataTemplate>
  60.                             <Grid Margin="10, 10, 10, 10" Background="White">
  61.                                 <lview:CardView DataContext="{Binding Item4}"></lview:CardView>
  62.                             </Grid>
  63.                         </DataTemplate>
  64.                     </sdk:DataGridTemplateColumn.CellTemplate>
  65.                 </sdk:DataGridTemplateColumn>
  66.             </sdk:DataGrid.Columns>
  67.         </ctrl:CustomDataGrid>
  68.     </Grid>
  69. </UserControl>
CardBoardViewModel.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. using System.Windows.Ink;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Animation;
  12. using System.Windows.Shapes;
  13. using dSucs.ColourMemoGame.Model;
  14. using System.Collections.ObjectModel;
  15. using System.Windows.Threading;
  16. using dSucs.ColourMemoGame.ViewModel.MemoGameService;
  17. using System.ServiceModel;
  18. using System.Threading.Tasks;
  19.  
  20. namespace dSucs.ColourMemoGame.ViewModel
  21. {
  22.     public class CardBoardViewModel : ViewModelBase
  23.     {
  24.         public CardBoardViewModel()
  25.         {
  26.             _Alg = GameAlgorithm.CreateInstance();
  27.             LstCardVM = new ObservableCollection<Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>>();
  28.             CreateCardVM();
  29.             Task taskInitService = new Task(new Action(InitMemoGameService));
  30.             taskInitService.Start();
  31.         }
  32.  
  33.         public string EmailID { get; set; }
  34.         public ObservableCollection<Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>> LstCardVM { get; set; }
  35.  
  36.         public void CreateCardVM()
  37.         {
  38.             int row = 4, col = 4;
  39.             LstCardVM.Clear();
  40.             CardViewModel vm1 = null, vm2 = null, vm3 = null, vm4 = null;
  41.             Card[,] arrCards = _Alg.CreateCardsArray(row, col);
  42.             for (int i = 0; i < row; i++)
  43.             {
  44.                 vm1 = new CardViewModel(this, arrCards[i, 0]); vm1.OnSelectCard += this.SelectCard;
  45.                 vm2 = new CardViewModel(this, arrCards[i, 1]); vm2.OnSelectCard += this.SelectCard;
  46.                 vm3 = new CardViewModel(this, arrCards[i, 2]); vm3.OnSelectCard += this.SelectCard;
  47.                 vm4 = new CardViewModel(this, arrCards[i, 3]); vm4.OnSelectCard += this.SelectCard;
  48.                 LstCardVM.Add(Tuple.Create(vm1, vm2, vm3, vm4));
  49.             }
  50.         }
  51.  
  52.         public void SelectCard(object sender, CardEventArgs e)
  53.         {
  54.             CardViewModel cVM = null;
  55.             if (sender is Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>)
  56.             {
  57.                 Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel> tuple = sender as Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>;
  58.                 int index = Convert.ToInt16(e.Result);
  59.                 switch (index)
  60.                 {
  61.                     case 0:
  62.                         cVM = tuple.Item1;
  63.                         break;
  64.                     case 1:
  65.                         cVM = tuple.Item2;
  66.                         break;
  67.                     case 2:
  68.                         cVM = tuple.Item3;
  69.                         break;
  70.                     case 3:
  71.                         cVM = tuple.Item4;
  72.                         break;
  73.                 }
  74.                 _SelectedCardVM = cVM;
  75.             }
  76.             else if (sender is CardViewModel)
  77.             {
  78.                 _SelectedCardVM = (CardViewModel)sender;
  79.             }
  80.  
  81.             SelectCard();
  82.         }
  83.  
  84.         public void SelectCard()
  85.         {
  86.             if (!_TimerAllowOK)
  87.             {
  88.                 _MessageInfo = Helper.Msg002;
  89.                 OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  90.                 return;
  91.             }
  92.             else
  93.             {
  94.                 lock (_LstSelectedCardVM)
  95.                 {
  96.                     if (_SelectedCardVM.Status != CardStatus.Matched)
  97.                     {
  98.                         if (_SelectedCardVM.Status == CardStatus.UnSelected)
  99.                         {
  100.                             _SelectedCardVM.Status = CardStatus.Selected;
  101.                             _SelectedCardVM.Card.Status = CardStatus.Selected;
  102.                             SoundPlayer.GetInstance().PlaySound(DSSoundEffect.FlipUp);
  103.                             switch (_LstSelectedCardVM.Count)
  104.                             {
  105.                                 case 0:
  106.                                     _LstSelectedCardVM.Add(_SelectedCardVM);
  107.                                     _MessageInfo = Helper.Msg003;
  108.                                    OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  109.                                     break;
  110.                                 case 1:
  111.                                     if (_LstSelectedCardVM[0].Card.CardID != _SelectedCardVM.Card.CardID)
  112.                                     {
  113.                                         _LstSelectedCardVM.Add(_SelectedCardVM);
  114.                                     }
  115.                                     break;
  116.                             }
  117.                         }
  118.                         else
  119.                         {
  120.                             _SelectedCardVM.Status = CardStatus.UnSelected;
  121.                             _SelectedCardVM.Card.Status = CardStatus.UnSelected;
  122.                             SoundPlayer.GetInstance().PlaySound(DSSoundEffect.FlipDown);
  123.                             switch (_LstSelectedCardVM.Count)
  124.                             {
  125.                                 case 1:
  126.                                     _LstSelectedCardVM.RemoveAt(0);
  127.                                     _MessageInfo = Helper.Msg008;
  128.                                     OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  129.                                     break;
  130.                                 case 2:
  131.                                     _LstSelectedCardVM.RemoveAt(1);
  132.                                     break;
  133.                             }
  134.                         }
  135.                     }
  136.  
  137.                     if (_LstSelectedCardVM.Count == 2)
  138.                     {
  139.                         _Move++;
  140.                         if (_Alg.AreSelectedCardsMatching(_LstSelectedCardVM[0].Card, _LstSelectedCardVM[1].Card))
  141.                         {
  142.                             _LstSelectedCardVM[0].Status = CardStatus.Matched;
  143.                             _LstSelectedCardVM[0].Card.Status = CardStatus.Matched;
  144.                             _LstSelectedCardVM[1].Status = CardStatus.Matched;
  145.                             _LstSelectedCardVM[1].Card.Status = CardStatus.Matched;
  146.                             SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Match);
  147.                             _LstSelectedCardVM.Clear();
  148.                             _Score++;
  149.                             _WinningCount++;
  150.                             if (_WinningCount == 8) //TODO:Game finished, mark the count as 8 for winning
  151.                             {
  152.                                 _MessageInfo = Helper.Msg004;
  153.                                 SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Win);
  154.  
  155.                                 if (!string.IsNullOrEmpty(EmailID))
  156.                                 {
  157.                                     _WCFClient.SaveScoreAsync(EmailID, _Score);
  158.                                 }
  159.                                 else
  160.                                 {
  161.                                     LoginViewModel vm = new LoginViewModel();
  162.                                     vm.OnCloseWindow += SaveScore;
  163.                                     OnOpenLoginWindow(this, new CardEventArgs(vm));
  164.                                 }
  165.                             }
  166.                             else
  167.                             {
  168.                                 _MessageInfo = Helper.Msg005;
  169.                             }
  170.                             OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  171.                         }
  172.                         else
  173.                         {
  174.                             SoundPlayer.GetInstance().PlaySound(DSSoundEffect.MisMatch);
  175.                             _Score--;
  176.                             _MessageInfo = Helper.Msg006;
  177.                             _timer.Interval = TimeSpan.FromSeconds(0.6);
  178.                             _timer.Tick += FaceDownSelectedCards;
  179.                             _TimerAllowOK = false;
  180.                             _timer.Start();
  181.                             OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  182.                         }
  183.                     }
  184.                 }
  185.             }
  186.         }
  187.  
  188.         private void SaveScore(object sender, CardEventArgs e)
  189.         {
  190.             EmailID = e.Result.ToString();
  191.             if(!string.IsNullOrEmpty(EmailID))
  192.                 _WCFClient.SaveScoreAsync(EmailID, _Score);
  193.         }
  194.       
  195.         private void SaveScoreCompleted(object sender, SaveScoreCompletedEventArgs e)
  196.         {
  197.             if (e.Result)
  198.                 _MessageInfo = Helper.Msg011;
  199.             else
  200.                 _MessageInfo = Helper.Msg012;
  201.             OnSaveScore(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  202.         }
  203.  
  204.         private void FaceDownSelectedCards(object sender, EventArgs e)
  205.         {
  206.             _timer.Stop();
  207.             _TimerAllowOK = true;
  208.             if (_LstSelectedCardVM.Count == 2)
  209.             {
  210.                 _LstSelectedCardVM[0].Status = CardStatus.UnSelected;
  211.                 _LstSelectedCardVM[0].Card.Status = CardStatus.UnSelected;
  212.                 _LstSelectedCardVM[1].Status = CardStatus.UnSelected;
  213.                 _LstSelectedCardVM[1].Card.Status = CardStatus.UnSelected;
  214.                 _LstSelectedCardVM.Clear();
  215.                 _MessageInfo = Helper.Msg007;
  216.                 OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
  217.             }
  218.         }
  219.  
  220.         public void ResetGame(object sender, CardEventArgs e)
  221.         {
  222.             _Score = _Move = _WinningCount = 0;
  223.             _MessageInfo = Helper.Msg001;
  224.             if (_LstSelectedCardVM.Count==1)
  225.             {
  226.             _LstSelectedCardVM[0].Status = CardStatus.UnSelected;
  227.             _LstSelectedCardVM[0].Card.Status = CardStatus.UnSelected;
  228.             }
  229.             if (_LstSelectedCardVM.Count == 2)
  230.             {
  231.                 _LstSelectedCardVM[1].Status = CardStatus.UnSelected;
  232.                 _LstSelectedCardVM[1].Card.Status = CardStatus.UnSelected;
  233.             }
  234.             _LstSelectedCardVM.Clear();
  235.  
  236.             CreateCardVM();
  237.  
  238.             foreach (Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel> tuple in LstCardVM)
  239.             {
  240.                 ResetGame(tuple.Item1, tuple.Item1.Card);
  241.                 ResetGame(tuple.Item2, tuple.Item2.Card);
  242.                 ResetGame(tuple.Item3, tuple.Item3.Card);
  243.                 ResetGame(tuple.Item4, tuple.Item4.Card);
  244.             }
  245.             SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Reset);
  246.         }
  247.         private void ResetGame(CardViewModel vm, Card cd)
  248.         {
  249.             vm.Card = cd;
  250.             vm.FrontImage = cd.FrontImage;
  251.             vm.BackImage = cd.BackImage;
  252.             vm.Status = cd.Status;
  253.             vm.Card.Status = cd.Status;
  254.         }
  255.  
  256.         private void InitMemoGameService()
  257.         {
  258.             EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
  259.             BasicHttpBinding binding = new BasicHttpBinding();
  260.             _WCFClient = new MemoGameServiceClient(binding, address);
  261.             _WCFClient.SaveScoreCompleted += new EventHandler<SaveScoreCompletedEventArgs>(SaveScoreCompleted);
  262.         }
  263.  
  264.        
  265.         public GameAlgorithm _Alg = null;
  266.         private IList<CardViewModel> _LstCardVM = new List<CardViewModel>();
  267.         private IList<CardViewModel> _LstSelectedCardVM = new List<CardViewModel>();
  268.         private CardViewModel _SelectedCardVM = null;
  269.         private bool _TimerAllowOK = true;
  270.         private string _MessageInfo = string.Empty;
  271.         private int _Score, _Move, _WinningCount;
  272.         private System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer();
  273.         public event CmgEventHandler OnOpenLoginWindow, OnUpdateGameInfo, OnSaveScore;
  274.         private MemoGameServiceClient _WCFClient;
  275.     }
  276. }
-CardView.xaml : This contains the Card
image
XAML
  1. <UserControl
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5.    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6.    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
  7.             xmlns:local="clr-namespace:dSucs.ColourMemoGame.View"
  8.             xmlns:lc="clr-namespace:dSucs.ColourMemoGame"
  9.    xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="dSucs.ColourMemoGame.View.CardView"
  10.    mc:Ignorable="d"
  11.    d:DesignHeight="100" d:DesignWidth="80">
  12.     <UserControl.Resources>
  13.         <ResourceDictionary>
  14.             <Storyboard x:Name="sbFlipUp">
  15.                 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="FrontImage">
  16.                     <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="90"/>
  17.                     <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="180"/>
  18.                 </DoubleAnimationUsingKeyFrames>
  19.                 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="BackImage">
  20.                     <EasingDoubleKeyFrame KeyTime="0" Value="-180"/>
  21.                     <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-90"/>
  22.                     <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="0"/>
  23.                 </DoubleAnimationUsingKeyFrames>
  24.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="FrontImage">
  25.                     <DiscreteObjectKeyFrame KeyTime="0">
  26.                         <DiscreteObjectKeyFrame.Value>
  27.                             <Visibility>Visible</Visibility>
  28.                         </DiscreteObjectKeyFrame.Value>
  29.                     </DiscreteObjectKeyFrame>
  30.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  31.                         <DiscreteObjectKeyFrame.Value>
  32.                             <Visibility>Collapsed</Visibility>
  33.                         </DiscreteObjectKeyFrame.Value>
  34.                     </DiscreteObjectKeyFrame>
  35.                 </ObjectAnimationUsingKeyFrames>
  36.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="BackImage">
  37.                     <DiscreteObjectKeyFrame KeyTime="0">
  38.                         <DiscreteObjectKeyFrame.Value>
  39.                             <Visibility>Visible</Visibility>
  40.                         </DiscreteObjectKeyFrame.Value>
  41.                     </DiscreteObjectKeyFrame>
  42.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  43.                         <DiscreteObjectKeyFrame.Value>
  44.                             <Visibility>Visible</Visibility>
  45.                         </DiscreteObjectKeyFrame.Value>
  46.                     </DiscreteObjectKeyFrame>
  47.                 </ObjectAnimationUsingKeyFrames>
  48.             </Storyboard>
  49.             <Storyboard x:Name="sbFlipDown">
  50.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="FrontImage">
  51.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  52.                         <DiscreteObjectKeyFrame.Value>
  53.                             <Visibility>Visible</Visibility>
  54.                         </DiscreteObjectKeyFrame.Value>
  55.                     </DiscreteObjectKeyFrame>
  56.                 </ObjectAnimationUsingKeyFrames>
  57.                 <DoubleAnimation Duration="0:0:0.6" To="0" Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="FrontImage" d:IsOptimized="True"/>
  58.                 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="BackImage">
  59.                     <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="-90"/>
  60.                     <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="-180"/>
  61.                 </DoubleAnimationUsingKeyFrames>
  62.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="BackImage">
  63.                     <DiscreteObjectKeyFrame KeyTime="0">
  64.                         <DiscreteObjectKeyFrame.Value>
  65.                             <Visibility>Visible</Visibility>
  66.                         </DiscreteObjectKeyFrame.Value>
  67.                     </DiscreteObjectKeyFrame>
  68.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  69.                         <DiscreteObjectKeyFrame.Value>
  70.                             <Visibility>Collapsed</Visibility>
  71.                         </DiscreteObjectKeyFrame.Value>
  72.                     </DiscreteObjectKeyFrame>
  73.                 </ObjectAnimationUsingKeyFrames>
  74.             </Storyboard>
  75.             <Storyboard x:Name="sbRemove">
  76.                 <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationZ)" Storyboard.TargetName="FrontImage">
  77.                     <EasingDoubleKeyFrame KeyTime="0:0:0.0" Value="0"/>
  78.                     <EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="90"/>
  79.                     <EasingDoubleKeyFrame KeyTime="0:0:0.6" Value="180"/>
  80.                     <EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="270"/>
  81.                     <EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="360"/>
  82.                     <EasingDoubleKeyFrame KeyTime="0:0:1.5" Value="0"/>
  83.                 </DoubleAnimationUsingKeyFrames>
  84.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="FrontImage">
  85.                     <DiscreteObjectKeyFrame KeyTime="0">
  86.                         <DiscreteObjectKeyFrame.Value>
  87.                             <Visibility>Visible</Visibility>
  88.                         </DiscreteObjectKeyFrame.Value>
  89.                     </DiscreteObjectKeyFrame>
  90.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  91.                         <DiscreteObjectKeyFrame.Value>
  92.                             <Visibility>Collapsed</Visibility>
  93.                         </DiscreteObjectKeyFrame.Value>
  94.                     </DiscreteObjectKeyFrame>
  95.                 </ObjectAnimationUsingKeyFrames>
  96.                 <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="BackImage">
  97.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.3">
  98.                         <DiscreteObjectKeyFrame.Value>
  99.                             <Visibility>Collapsed</Visibility>
  100.                         </DiscreteObjectKeyFrame.Value>
  101.                     </DiscreteObjectKeyFrame>
  102.                     <DiscreteObjectKeyFrame KeyTime="0:0:0.6">
  103.                         <DiscreteObjectKeyFrame.Value>
  104.                             <Visibility>Collapsed</Visibility>
  105.                         </DiscreteObjectKeyFrame.Value>
  106.                     </DiscreteObjectKeyFrame>
  107.                 </ObjectAnimationUsingKeyFrames>
  108.             </Storyboard>
  109.         </ResourceDictionary>
  110.     </UserControl.Resources>
  111.     <Grid x:Name="LayoutRoot" Height="100" Width="80">
  112.         <Button x:Name="btnCard" Margin="0,0,0,0" Height="100" Width="80" VerticalAlignment="Top" Command="{Binding CmdSelectCard, UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding Card, UpdateSourceTrigger=PropertyChanged}">
  113.             <Grid Margin="0,0,0,0" >
  114.                 <Image x:Name="FrontImage" Margin="0,0,0,0" Source="{Binding FrontImage, FallbackValue=/Images/colour3.png, UpdateSourceTrigger=PropertyChanged}" Stretch="Fill" VerticalAlignment="Top" HorizontalAlignment="Left" Height="100" Width="80" Canvas.Left="0" Canvas.Top="0" >
  115.                     <i:Interaction.Triggers>
  116.                         <ei:DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="1">
  117.                             <ei:ControlStoryboardAction Storyboard="{StaticResource sbFlipDown}" />
  118.                         </ei:DataTrigger>
  119.                         <ei:DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="2">
  120.                             <ei:ControlStoryboardAction Storyboard="{StaticResource sbRemove}" />
  121.                         </ei:DataTrigger>
  122.                     </i:Interaction.Triggers>
  123.                     <Image.Projection>
  124.                         <PlaneProjection/>
  125.                     </Image.Projection>
  126.                 </Image>
  127.                 <Image x:Name="BackImage" Margin="0,0,0,0" Source="{Binding BackImage, FallbackValue=/Images/card_bg.png, UpdateSourceTrigger=PropertyChanged}" Stretch="Fill" HorizontalAlignment="Left" Height="100" Width="80" VerticalAlignment="Top" Canvas.Left="0" Canvas.Top="0">
  128.                     <i:Interaction.Triggers>
  129.                         <ei:DataTrigger Binding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" Value="0">
  130.                             <ei:ControlStoryboardAction Storyboard="{StaticResource sbFlipUp}" />
  131.                         </ei:DataTrigger>
  132.                     </i:Interaction.Triggers>
  133.                     <Image.Projection>
  134.                         <PlaneProjection/>
  135.                     </Image.Projection>
  136.                 </Image>
  137.             </Grid>
  138.         </Button>
  139.     </Grid>
  140. </UserControl>
CardViewModel.cs
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using dSucs.ColourMemoGame.Model;
  12. using System.ComponentModel;
  13. using System.Collections.Generic;
  14. using System.Windows.Data;
  15.  
  16. namespace dSucs.ColourMemoGame.ViewModel
  17. {
  18.  
  19.     public class CardViewModel : ViewModelBase
  20.     {
  21.         public CardViewModel(object parentVM, Card card)
  22.         {
  23.             Card = card;
  24.             if (Card != null)
  25.             {
  26.                 FrontImage = Card.FrontImage;
  27.                 BackImage = Card.BackImage;
  28.             }
  29.         }
  30.       
  31.         public Card Card
  32.         {
  33.             get { return _Card; }
  34.             set { SetProperty(ref _Card, value, "Card "); }
  35.         }
  36.       
  37.         public string FrontImage
  38.         {
  39.             get
  40.             {
  41.                 return _FrontImage;
  42.             }
  43.             set
  44.             {
  45.                 SetProperty(ref _FrontImage, value, "FrontImage");
  46.             }
  47.         }
  48.         public string BackImage
  49.         {
  50.             get
  51.             {
  52.                 return _BackImage;
  53.             }
  54.             set
  55.             {
  56.                 SetProperty(ref _BackImage, value, "BackImage");
  57.             }
  58.         }
  59.         public CardStatus Status
  60.         {
  61.             get
  62.             {
  63.                 return _Status;
  64.             }
  65.             set
  66.             {
  67.                 SetProperty(ref _Status, value, "Status");
  68.             }
  69.         }
  70.        
  71.         #region Commands
  72.         public ICommand CmdSelectCard
  73.         {
  74.             get
  75.             {
  76.                 if (_CmdSelectCard == null)
  77.                     _CmdSelectCard = new RelayCommand(
  78.                         p => this.SelectCard(p, new CardEventArgs(string.Empty)),
  79.                         p => true);
  80.  
  81.                 return _CmdSelectCard;
  82.             }
  83.         }
  84.         #endregion
  85.  
  86.  
  87.  
  88.         #region UI Logics/Helpers
  89.         public void SelectCard(object sender, CardEventArgs e)
  90.         {
  91.             Card = (Card)sender;
  92.             OnSelectCard(this, new CardEventArgs(Card));
  93.         }
  94.         #endregion
  95.  
  96.         #region Private Members
  97.         private string _FrontImage, _BackImage /*, _CardID*/ ;
  98.         private CardStatus _Status;
  99.         private ICommand _CmdSelectCard;
  100.         private Card _Card;
  101.         public event CmgEventHandler OnSelectCard;
  102.         #endregion
  103.     }
  104. }
-LoginView.xaml : User can register/login through this screen. General validations of fields are done at the client side.
image
LoginView.xaml
  1. <v:ChildWindowBase
  2.    xmlns:v="clr-namespace:dSucs.ColourMemoGame.View"
  3.           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4.           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5.           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
  6.           xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" x:Class="dSucs.ColourMemoGame.View.LoginView"
  7.           Width="386.502" Height="246.355"
  8.           Title="Colour Memory Game : Login/Register">
  9.     <Grid x:Name="LayoutRoot" Margin="2">
  10.         <Grid.Background>
  11.             <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  12.                 <GradientStop Color="Black" Offset="0.004"/>
  13.                 <GradientStop Color="#FFC99D9D" Offset="0.578"/>
  14.                 <GradientStop Color="#FF210505" Offset="0.957"/>
  15.             </LinearGradientBrush>
  16.         </Grid.Background>
  17.         <Grid.RowDefinitions>
  18.             <RowDefinition />
  19.             <RowDefinition Height="Auto" />
  20.         </Grid.RowDefinitions>
  21.         <Image Margin="322,0,0,180" Source="/Images/logo.png" Stretch="Fill" RenderTransformOrigin="0.483,0.76"/>
  22.         <TextBlock HorizontalAlignment="Left" Margin="4,27,0,0" TextWrapping="Wrap" Text="{Binding MessageInfo, FallbackValue='Please login/register to know how well you have played  against your peers. Your Rank and Highest score will be shown to you.'}" VerticalAlignment="Top" Height="52" Width="361" Foreground="#FFEEEE0F"/>
  23.         <sdk:Label HorizontalAlignment="Left" Margin="13,88,0,0" VerticalAlignment="Top" Height="25" Width="84" Content="*Email ID:"/>
  24.         <TextBox x:Name="txtEmailID" HorizontalAlignment="Left" Height="23" Margin="78,84,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="259" Text = "{Binding EmailID, Mode=Twoway, ValidatesOnDataErrors=true,UpdateSourceTrigger=PropertyChanged}" TabIndex="1">
  25.             <i:Interaction.Triggers>
  26.                 <i:EventTrigger EventName="LostFocus">
  27.                     <i:InvokeCommandAction Command="{Binding CmdEmailIDLostFocus}" CommandParameter="{Binding Text, ElementName=txtEmailID}" />
  28.                 </i:EventTrigger>
  29.             </i:Interaction.Triggers>
  30.         </TextBox>
  31.         <sdk:Label HorizontalAlignment="Left" Margin="36,135,0,0" VerticalAlignment="Top" Height="25" Width="84" Content="*Name:" Visibility="{Binding ShowTextBoxName, UpdateSourceTrigger=PropertyChanged}"/>
  32.         <TextBox x:Name="txtName" HorizontalAlignment="Left" Height="23" Margin="78,134,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="259" Text = "{Binding Name, Mode=Twoway, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}" TabIndex="3" Visibility="{Binding ShowTextBoxName, UpdateSourceTrigger=PropertyChanged}"/>
  33.         <sdk:Label HorizontalAlignment="Left" Margin="9,112,0,0" VerticalAlignment="Top" Height="25" Width="84" Content="*Password:"/>
  34.         <PasswordBox x:Name="pbPassword" HorizontalAlignment="Left" Margin="78,108,0,0" VerticalAlignment="Top" Width="154" TabIndex="2" Password="{Binding Password, Mode=Twoway, ValidatesOnDataErrors=true, UpdateSourceTrigger=PropertyChanged}"/>
  35.         <Button x:Name="CancelButton" Content="Cancel" Command="{Binding CmdClickCancel}" CommandParameter="{Binding ElementName=WindowScore}" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,172,28,12" Background="#FF4576A0" TabIndex="6"/>
  36.         <Button Content="OK" HorizontalAlignment="Left" Margin="182,172,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CmdClickOK}" IsEnabled="{Binding IsButtonOKEnabled}" TabIndex="5"/>
  37.         <Button Content="Send my Password" HorizontalAlignment="Left" Margin="62,172,0,0" VerticalAlignment="Top" Width="113" Command="{Binding CmdClickSendMyPassword}" Visibility="{Binding ShowButtonSendPassword}" TabIndex="4"/>
  38.     </Grid>
  39. </v:ChildWindowBase>
LoginViewModel.cs
  1. using dSucs.ColourMemoGame.ViewModel.MemoGameService;
  2. using System;
  3. using System.ComponentModel;
  4. using System.Net;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. using System.Windows.Ink;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Animation;
  12. using System.Windows.Shapes;
  13. using dSucs.ColourMemoGame.Model;
  14. using System.ServiceModel;
  15. using System.Threading.Tasks;
  16.  
  17. namespace dSucs.ColourMemoGame.ViewModel
  18. {
  19.     public class LoginViewModel : ViewModelBase, IDataErrorInfo
  20.     {
  21.         #region Construction
  22.         public LoginViewModel()
  23.         {
  24.             Initialize();
  25.         }
  26.  
  27.         private void Initialize()
  28.         {
  29.             MessageInfo = Helper.Msg009;
  30.             ShowButtonSendPassword = "Collapsed";
  31.             ShowTextBoxName = "Collapsed";
  32.             IsButtonOKEnabled = false;
  33.             _validator = MemoGameValidator.GetInstance();
  34.             Task taskInitService = new Task(new Action(InitMemoGameService));
  35.             taskInitService.Start();
  36.         }
  37.         #endregion
  38.  
  39.         #region Properties
  40.         public string MessageInfo
  41.         {
  42.             get { return _MessageInfo; }
  43.             set { SetProperty(ref _MessageInfo, value, PropMessageInfo); }
  44.         }
  45.         public string EmailID
  46.         {
  47.             get { return _EmailID; }
  48.             set { SetProperty(ref _EmailID, value, PropEmailID); }
  49.         }
  50.         public string Password
  51.         {
  52.             get { return _Password; }
  53.             set { SetProperty(ref _Password, value, PropPassword); }
  54.         }
  55.         public string Name
  56.         {
  57.             get { return _Name; }
  58.             set { SetProperty(ref _Name, value, PropName); }
  59.         }
  60.         public string Error
  61.         {
  62.             get { return _Error; }
  63.         }
  64.         public string ShowTextBoxName
  65.         {
  66.             get { return _ShowTextBoxName; }
  67.             set { SetProperty(ref _ShowTextBoxName, value, PropShowTextBoxName); }
  68.         }
  69.         public string ShowButtonSendPassword
  70.         {
  71.             get { return _ShowButtonSendPassword; }
  72.             set { SetProperty(ref _ShowButtonSendPassword, value, PropShowButtonSendPassword); }
  73.         }
  74.         public bool IsButtonOKEnabled
  75.         {
  76.             get { return _IsButtonOKEnabled; }
  77.             set { SetProperty(ref _IsButtonOKEnabled, value, PropIsButtonOKEnabled); }
  78.         }
  79.         public string this[string propertyName]
  80.         {
  81.             get
  82.             {
  83.                 _Error = null;
  84.                 switch (propertyName)
  85.                 {
  86.                     case "EmailID":
  87.                         _Error = _validator.Validate<string>(propertyName, EmailID);
  88.                         break;
  89.                     case "Password":
  90.                         _Error = _validator.Validate<string>(propertyName, Password);
  91.                         if (_IsUserRegistered)
  92.                             IsButtonOKEnabled = string.IsNullOrEmpty(_Error);
  93.                         break;
  94.                     case "Name":
  95.                         if (!_IsUserRegistered)
  96.                             _Error = _validator.Validate<string>(propertyName, Name);
  97.                         else
  98.                             _Error = null;
  99.                         IsButtonOKEnabled = string.IsNullOrEmpty(_Error);
  100.                         break;
  101.                 }
  102.                 return _Error;
  103.             }
  104.         }
  105.         public int Score { get; set; }
  106.         #endregion
  107.  
  108.         #region Commands
  109.         public ICommand CmdEmailIDLostFocus
  110.         {
  111.             get
  112.             {
  113.                 if (_CmdEmailIDLostFocus == null)
  114.                     _CmdEmailIDLostFocus = new RelayCommand(
  115.                         p => this.OnEmailIDLostFocus(p),
  116.                         p => true);
  117.  
  118.                 return _CmdEmailIDLostFocus;
  119.             }
  120.         }
  121.         public ICommand CmdClickOK
  122.         {
  123.             get
  124.             {
  125.                 if (_CmdClickOK == null)
  126.                     _CmdClickOK = new RelayCommand(
  127.                         p => this.OnClickOK(p),
  128.                         p => true);
  129.  
  130.                 return _CmdClickOK;
  131.             }
  132.         }
  133.         public ICommand CmdClickCancel
  134.         {
  135.             get
  136.             {
  137.                 if (_CmdClickCancel == null)
  138.                     _CmdClickCancel = new RelayCommand(
  139.                         p => this.OnClickCancel(p),
  140.                         p => true);
  141.  
  142.                 return _CmdClickCancel;
  143.             }
  144.         }
  145.         public ICommand CmdClickSendMyPassword
  146.         {
  147.             get
  148.             {
  149.                 if (_CmdClickSendMyPassword == null)
  150.                     _CmdClickSendMyPassword = new RelayCommand(
  151.                         p => this.OnClickSendMyPassword(p),
  152.                         p => true);
  153.  
  154.                 return _CmdClickSendMyPassword;
  155.             }
  156.         }
  157.         #endregion
  158.  
  159.         #region UI Functions and Logic
  160.         private void OnEmailIDLostFocus(object sender)
  161.         {
  162.             EmailID = sender.ToString();
  163.             if (!string.IsNullOrEmpty(EmailID)) _WCFClient.IsUserRegisteredAsync(EmailID);
  164.         }
  165.         private void CheckIsUserRegisterd(object sender, IsUserRegisteredCompletedEventArgs e)
  166.         {
  167.             try
  168.             {
  169.                 _IsUserRegistered = e.Result;
  170.             }
  171.             catch(Exception ex)
  172.             {
  173.                 MessageInfo = Helper.Msg018;
  174.             }
  175.             ShowTextBoxName = _IsUserRegistered ? "Collapsed" : "Visible";
  176.         }
  177.         private void OnClickOK(object sender)
  178.         {
  179.             if (_IsUserRegistered)
  180.             {
  181.                 _WCFClient.CheckLoginAsync(EmailID, Password);
  182.             }
  183.             else
  184.                 _WCFClient.RegisterUserAsync(EmailID, Password, Name);
  185.         }
  186.         private void RegisterUser(object sender, RegisterUserCompletedEventArgs e)
  187.         {
  188.             bool execComplete = false;
  189.             try
  190.             {
  191.                 execComplete = e.Result;
  192.             }
  193.             catch (Exception ex)
  194.             {
  195.                 MessageInfo = Helper.Msg018;
  196.             }
  197.  
  198.             if (execComplete)
  199.             {
  200.                 OnCloseWindow(this, new CardEventArgs(this.EmailID));
  201.             }
  202.             else
  203.             {
  204.                 MessageInfo = Helper.Msg015;
  205.             }
  206.         }
  207.         private void CheckLogin(object sender, CheckLoginCompletedEventArgs e)
  208.         {
  209.             try
  210.             {
  211.                 _LoginAttemptCount++;
  212.                 _CheckLogin = e.Result;
  213.             }
  214.             catch(Exception ex)
  215.             {
  216.                 MessageInfo = Helper.Msg018;
  217.             }
  218.             if (_CheckLogin)
  219.             {
  220.                 OnCloseWindow(this, new CardEventArgs(EmailID));
  221.             }
  222.             else if (!_CheckLogin && _LoginAttemptCount < 3)
  223.             {
  224.                 MessageInfo = string.Format(Helper.Msg017, _LoginAttemptCount);
  225.             }
  226.             else if (_LoginAttemptCount >= 3)
  227.             {
  228.                 MessageInfo = Helper.Msg014;
  229.                 ShowButtonSendPassword = "Visible";
  230.             }
  231.         }
  232.         private void OnClickSendMyPassword(object sender)
  233.         {
  234.             if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetMyPasswordAsync(EmailID);
  235.         }
  236.         private void SendMyPassword(object sender, GetMyPasswordCompletedEventArgs e)
  237.         {
  238.             bool execComplete = false;
  239.             try
  240.             {
  241.                 execComplete = e.Result;
  242.             }
  243.             catch(Exception ex)
  244.             {
  245.                 MessageInfo = Helper.Msg013;
  246.             }
  247.             if (execComplete)
  248.             {
  249.                 MessageInfo = string.Format(Helper.Msg016, EmailID);
  250.                 _LoginAttemptCount=0;
  251.                 ShowButtonSendPassword="Collapsed";
  252.             }
  253.             else
  254.             {
  255.                 MessageInfo = Helper.Msg013;
  256.             }
  257.         }
  258.         public void OnClickCancel(object sender)
  259.         {
  260.             OnCloseWindow(this, new CardEventArgs(string.Empty));
  261.         }
  262.         private void InitMemoGameService()
  263.         {
  264.             EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
  265.             BasicHttpBinding binding = new BasicHttpBinding();
  266.             //binding.SendTimeout = TimeSpan.FromSeconds(15);
  267.  
  268.             _WCFClient = new MemoGameServiceClient(binding, address);
  269.             _WCFClient.IsUserRegisteredCompleted += new EventHandler<IsUserRegisteredCompletedEventArgs>(CheckIsUserRegisterd);
  270.             _WCFClient.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(RegisterUser);
  271.             _WCFClient.CheckLoginCompleted += new EventHandler<CheckLoginCompletedEventArgs>(CheckLogin);
  272.             _WCFClient.GetMyPasswordCompleted += new EventHandler<GetMyPasswordCompletedEventArgs>(SendMyPassword);
  273.         }
  274.         #endregion
  275.  
  276.         #region Private Members
  277.         string _MessageInfo, _EmailID, _Password, _Name, _Error, _ShowTextBoxName, _ShowButtonSendPassword;
  278.         string PropMessageInfo = "MessageInfo", PropEmailID = "EmailID", PropPassword = "Password", PropName = "Name";
  279.         string PropShowTextBoxName = "ShowTextBoxName", PropShowButtonSendPassword = "ShowButtonSendPassword", PropIsButtonOKEnabled = "IsButtonOKEnabled";
  280.         bool _IsUserRegistered = true, _IsButtonOKEnabled=false,_CheckLogin = false;
  281.         int _LoginAttemptCount = 0;
  282.         ICommand _CmdEmailIDLostFocus, _CmdClickOK, _CmdClickCancel, _CmdClickSendMyPassword;
  283.         MemoGameValidator _validator = null;
  284.         MemoGameServiceClient _WCFClient;
  285.         public event CmgEventHandler OnCloseWindow;
  286.         #endregion
  287.     }
  288. }
MemoGameValidator.cs
  1. using System;
  2. using System.Text.RegularExpressions;
  3.  
  4. namespace dSucs.ColourMemoGame.ViewModel
  5. {
  6.     public class MemoGameValidator
  7.     {
  8.         private MemoGameValidator()
  9.         {
  10.         }
  11.         public static MemoGameValidator GetInstance()
  12.         {
  13.             if (_instance == null) _instance = new MemoGameValidator();
  14.             return _instance;
  15.         }
  16.  
  17.         public string Validate<T>(string PropertyName, T value)
  18.         {
  19.             string error = null;
  20.             switch (PropertyName)
  21.             {
  22.                 case "EmailID":
  23.                     if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
  24.                     {
  25.                         error = "EmailID can not be left blank.";
  26.                     }
  27.                     else
  28.                     {
  29.                         string emailPattern = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
  30.                         if (!Regex.IsMatch(value.ToString(), emailPattern))
  31.                             error = "Invalid Email Address.";
  32.                     }
  33.                     break;
  34.  
  35.                 case "Password":
  36.                     if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
  37.                     {
  38.                         error = "Password needs to be entered.";
  39.                     }
  40.                     else
  41.                     if ((value is string) && (!string.IsNullOrEmpty(value.ToString())))
  42.                     {
  43.                         string pwdPattern = @"((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[~!@#$%^&*()]).{4,8})";
  44.                         if (!Regex.IsMatch(value.ToString(), pwdPattern))
  45.                             error = "Must contain at least one digit, one lcase letter, one ucase letter, one special characters having length 4 to 8.";
  46.                     }
  47.                     break;
  48.  
  49.                 case "Name":
  50.                     if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
  51.                     {
  52.                         error = "New User? Please provide your name.";
  53.                     }
  54.                     else if ((value is string) && (!string.IsNullOrEmpty(value.ToString())))
  55.                     {
  56.                         string namePattern = @"^[a-zA-Z0-9]{3,10}$";
  57.                         if (!Regex.IsMatch(value.ToString(), namePattern))
  58.                             error = "Name should contain only alpha-numeric characters of length 3 to 10";
  59.                     }
  60.                     break;
  61.  
  62.                 default:
  63.                     break;
  64.             }
  65.             return error;
  66.         }
  67.         private static MemoGameValidator _instance = null;
  68.     }
  69. }
- SoundPlayer.cs : To play the sound effects while selecting/unselecting/matching cards or resetting the game.
SoundPlayer.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Resources;
  10. using Microsoft.Xna.Framework.Audio;
  11. using dSucs.ColourMemoGame.Model;
  12.  
  13. namespace dSucs.ColourMemoGame.ViewModel
  14. {
  15.     public class SoundPlayer
  16.     {
  17.         #region Construction and Initialization
  18.         private SoundPlayer()
  19.         {
  20.             InitializeEffects();
  21.         }
  22.         public static SoundPlayer GetInstance()
  23.         {
  24.             if (_instance == null) _instance = new SoundPlayer();
  25.             return _instance;
  26.         }
  27.         void InitializeEffects()
  28.         {
  29.             this.AllSoundEffects = new Dictionary<string, SoundEffect>();
  30.             this.AllSoundEffectInstances = new Dictionary<string, SoundEffectInstance>();
  31.  
  32.             foreach (var sound in Sounds)
  33.             {
  34.                 this.AllSoundEffects.Add(sound, SoundEffect.FromStream(GetResourceStreamForNote(sound)));
  35.             }
  36.         }
  37.         #endregion
  38.  
  39.         #region public methods
  40.         public void PlaySound(string sound)
  41.         {
  42.             SoundEffectInstance instance = null;
  43.             if (!this.AllSoundEffectInstances.ContainsKey(sound))
  44.             {
  45.                 if (this.AllSoundEffects.ContainsKey(sound))
  46.                 {
  47.                     instance = this.AllSoundEffects[sound].CreateInstance();
  48.                     AllSoundEffectInstances.Add(sound, instance);
  49.                 }
  50.             }
  51.             else
  52.             {
  53.                 instance = this.AllSoundEffectInstances[sound];
  54.             }
  55.             instance.Play();
  56.         }
  57.         public void StopSound(string sound)
  58.         {
  59.             if (this.AllSoundEffectInstances.ContainsKey(sound))
  60.             {
  61.                 this.AllSoundEffectInstances[sound].Dispose();
  62.                 this.AllSoundEffectInstances.Remove(sound);
  63.             }
  64.         }
  65.         public void StopAllSound()
  66.         {
  67.             foreach (var sound in Sounds)
  68.             {
  69.                 StopSound(sound);
  70.             }
  71.         }
  72.         #endregion
  73.        
  74.         #region Private Functions
  75.         private Stream GetResourceStreamForNote(string wavFile)
  76.         {
  77.             StreamResourceInfo sri =
  78.               Application.GetResourceStream(
  79.                 new Uri(string.Format(Helper.MediaFile, wavFile),
  80.                   UriKind.Relative));
  81.  
  82.             return (sri.Stream);
  83.         }
  84.         #endregion
  85.  
  86.         #region Private Members
  87.         string[] Sounds =
  88.         {
  89.             DSSoundEffect.FlipUp, DSSoundEffect.FlipDown,
  90.             DSSoundEffect.Match, DSSoundEffect.MisMatch,
  91.             DSSoundEffect.Win, DSSoundEffect.Reset
  92.         };
  93.         Dictionary<string, SoundEffect> AllSoundEffects;
  94.         Dictionary<string, SoundEffectInstance> AllSoundEffectInstances;
  95.         private static SoundPlayer _instance = null;
  96.         #endregion
  97.     }
  98.  
  99.     public static class DSSoundEffect
  100.     {
  101.         public static string FlipUp = "FlipUp", FlipDown = "FlipDown", Match = "Match", MisMatch = "MisMatch", Win = "Win", Reset = "Reset";
  102.     }
  103. }
-ViewModelBase: This is a base class for all the ViewModels which provides a common implementation of INotifyPropertyChanged.
ViewModelBase.cs
  1. public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
  2.     {
  3.         #region Constructor
  4.         protected ViewModelBase()
  5.         {
  6.         }
  7.         #endregion
  8.  
  9.         #region INotifyPropertyChanged Members
  10.         protected virtual void OnPropertyChanged(string propertyName)
  11.         {
  12.             PropertyChangedEventHandler handler = PropertyChanged;
  13.             if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  14.         }
  15.         protected bool SetProperty<T>(ref T field, T value, string propertyName)
  16.         {
  17.             if (EqualityComparer<T>.Default.Equals(field, value)) return false;
  18.             field = value;
  19.             OnPropertyChanged(propertyName);
  20.             return true;
  21.         }
  22.  
  23.         public event PropertyChangedEventHandler PropertyChanged;
  24.         #endregion
  25.  
  26.         #region IDisposable Members
  27.         public void Dispose()
  28.         {
  29.             Dispose(true);
  30.             GC.SuppressFinalize(this);
  31.         }
  32.         protected virtual void Dispose(bool disposing)
  33.         {
  34.             if (disposed)
  35.                 return;
  36.  
  37.             if (disposing)
  38.             {
  39.                 // Free any other managed objects here.
  40.                 //
  41.             }
  42.  
  43.             // Free any unmanaged objects here.
  44.             //
  45.             disposed = true;
  46.         }
  47.  
  48. #if DEBUG
  49.         ~ViewModelBase()
  50.         {
  51.             Dispose(false);
  52.         }
  53. #endif
  54.     #endregion
  55.  
  56.         public virtual string DisplayName { get; protected set; }
  57.         bool disposed = false;
  58.     }
Below are a couple of Util classes to the UI which may not be needed if we plan to build up the app based on PRISM /Unity framework which have a rich set of functionalities for bootstraping and event aggregation, but since for the development of this application we are not using these TP frameworks, just relying on plain C#, hence have to write extra few lines for this.
-BootStrap.cs: This is to help the App to start faster.
BootStrap.cs
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using dSucs.ColourMemoGame.View;
  12. using dSucs.ColourMemoGame.ViewModel;
  13. using dSucs.ColourMemoGame.Controls;
  14. using System.Threading.Tasks;
  15. using dSucs.ColourMemoGame.Model;
  16.  
  17. namespace dSucs.ColourMemoGame
  18. {
  19.     public class BootStrap
  20.     {
  21.         private BootStrap()
  22.         {
  23.             Task taskSetEndPointAddress = new Task(new Action(SetEndPointAddress));
  24.             taskSetEndPointAddress.Start();
  25.         }
  26.  
  27.         /// <summary>
  28.         /// Instantiate the Sinlgton Instane of Boostrap
  29.         /// </summary>
  30.         /// <returns></returns>
  31.         public static BootStrap GetInstance()
  32.         {
  33.             if (_instance == null) _instance = new BootStrap();
  34.             return _instance;
  35.         }
  36.        
  37.         /// <summary>
  38.         /// Free reources;
  39.         /// </summary>
  40.         public void FreeResouces()
  41.         {
  42.             SoundPlayer.GetInstance().StopAllSound();
  43.         }
  44.  
  45.         /// <summary>
  46.         /// Instantiate the main view and ViewModel, then set the DataContext
  47.         /// </summary>
  48.         /// <returns></returns>
  49.         public UserControl Init()
  50.         {
  51.             _CmgView = new ColourMemoGameView();
  52.             _CmgVM = new ColourMemoGameViewModel();
  53.             _CmgView.DataContext = _CmgVM;
  54.             App.Current.Host.Content.FullScreenChanged += new EventHandler((s, e) =>
  55.             {
  56.                 _CmgVM.ApplyFullScreen();
  57.             });
  58.             _CmgVM.OnOpenWindow += this.OpenWindow;
  59.             return _CmgView;
  60.         }
  61.         public void OpenWindow(object sender, CardEventArgs e)
  62.         {
  63.             if (e.Result is InfoViewModel)
  64.             {
  65.                 InfoViewModel vm = e.Result as InfoViewModel;
  66.                 _InfoView = new InfoView();
  67.                 _InfoView.DataContext = vm;
  68.                 _InfoView.Show();
  69.             }
  70.             else if (e.Result is LoginViewModel)
  71.             {
  72.                 LoginViewModel vm = e.Result as LoginViewModel;
  73.                 vm.OnCloseWindow += this.CloseWindow;
  74.                 vm.OnCloseWindow += _CmgVM.UpdateUserInfo;
  75.                 _LoginView = new LoginView();
  76.                 _LoginView.DataContext = vm;
  77.                 _LoginView.Show();
  78.             }
  79.         }
  80.  
  81.         public void CloseWindow(object sender, CardEventArgs e)
  82.         {
  83.             if (sender is InfoViewModel)
  84.             {
  85.                 _InfoView.Close();
  86.             }
  87.             else if (sender is LoginViewModel)
  88.             {
  89.                 _LoginView.Close();
  90.             }
  91.         }
  92.  
  93.         private void SetEndPointAddress()
  94.         {
  95.             string serviceUri = Helper.ServicePathTmp;
  96.             var serverFile = new Uri(string.Format(Helper.ResourceFile, Helper.ServiceFile), UriKind.Relative);
  97.             var rs = Application.GetResourceStream(serverFile);
  98.             if (rs != null)
  99.             {
  100.                 System.IO.StreamReader reader = new System.IO.StreamReader(rs.Stream);
  101.                 try
  102.                 {
  103.                     if (reader != null)
  104.                     {
  105.                         string line = string.Empty;
  106.                         string key = Helper.ServiceKey;
  107.                         while ((line = reader.ReadLine()) != null)
  108.                         {
  109.                             if (line.StartsWith(key))
  110.                             {
  111.                                 serviceUri = line.Substring(key.Length + 1);
  112.                             }
  113.                         }
  114.                     }
  115.                 }
  116.                 catch
  117.                 { }
  118.             }
  119.             GlobalVariables.EndPointAddress = serviceUri;
  120.         }
  121.  
  122.         private static BootStrap _instance = null;
  123.         private UserControl _CmgView;
  124.         private ChildWindow _InfoView, _LoginView;
  125.         private ColourMemoGameViewModel _CmgVM;
  126.     }
  127. }
-Converter.cs: This is to help choose a templated view of CardBoard loosely coupling the View with its corresponding ViewModel.
Converter.cs
  1. using System;
  2. using System.Globalization;
  3. using System.Net;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Data;
  7. using System.Windows.Documents;
  8. using System.Windows.Ink;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Animation;
  12. using System.Windows.Shapes;
  13. using dSucs.ColourMemoGame.Model;
  14. using System.Collections.Generic;
  15. using dSucs.ColourMemoGame.ViewModel;
  16. using dSucs.ColourMemoGame.View;
  17.  
  18. namespace dSucs.ColourMemoGame
  19. {
  20.     public class ViewTemplateChooser : IValueConverter
  21.     {
  22.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  23.         {
  24.            if (value is CardBoardViewModel)
  25.             {
  26.                 CardBoardViewModel vm = (CardBoardViewModel)value;
  27.                 CardBoardView vw= new CardBoardView() { DataContext = vm };
  28.                 vw.dgCardBoard.OnSelectCard += vm.SelectCard; //To select a card on pressing Enter/Space key
  29.                
  30.                 return vw;
  31.             }
  32.             return value;
  33.         }
  34.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  35.         {
  36.             throw new NotImplementedException();
  37.         }
  38.     }
  39. }
Custom Controls: The below couple of custom controls are mean to capture the keyboard events for Arrow Keys, Space/Enter Keys and Esc Keys. On pressing Esc key if any child window will be closed or exit the Full screen.
-CustomDataGrid.cs: This helps in navigating the cards/cells on CardBoard Custom datagrid and a way to capture the event when a card is selected or not.
CustomDataGrid.cs
  1. using System;
  2. using System.Net;
  3. using System.Windows;
  4. using System.Windows.Controls;
  5. using System.Windows.Documents;
  6. using System.Windows.Ink;
  7. using System.Windows.Input;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using System.Windows.Shapes;
  11. using dSucs.ColourMemoGame.ViewModel;
  12. using dSucs.ColourMemoGame.View;
  13.  
  14. namespace dSucs.ColourMemoGame.Controls
  15. {
  16.     public class CustomDataGrid : DataGrid
  17.     {
  18.         /// <summary>
  19.         /// Represents the CurrentCell of the customized Grid
  20.         /// </summary>
  21.         public DataGridCell CurrentCell { get; set; }
  22.  
  23.         /// <summary>
  24.         /// This function customizes the original keyboard events of the Grid to select Cards the CardBaord
  25.         /// </summary>
  26.         /// <param name="e"></param>
  27.         protected override void OnKeyDown(KeyEventArgs e)
  28.         {
  29.             if (e.Key == Key.Enter || e.Key == Key.Space)
  30.             {
  31.                 OnSelectCard(this.SelectedItem, new CardEventArgs(this.CurrentColumn.DisplayIndex)); //Raise Card selection event from the CustomGrid
  32.                 e.Handled = true;
  33.             }
  34.             else if (e.Key == Key.Up || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right)
  35.             {
  36.                 FrameworkElement el = this.Columns[this.CurrentColumn.DisplayIndex].GetCellContent(this.SelectedItem);
  37.                 CurrentCell = GetParent(el, typeof(DataGridCell)) as DataGridCell;
  38.                 this.Focus();
  39.                 CurrentCell.Focus();
  40.             }
  41.             else if (e.Key == Key.Tab)
  42.             {
  43.                 e.Handled = true;
  44.             }
  45.             base.OnKeyDown(e);
  46.         }
  47.  
  48.         /// <summary>
  49.         /// This will focus the first element in the Grid on loading
  50.         /// </summary>
  51.         /// <param name="e"></param>
  52.         protected override void OnLoadingRow(DataGridRowEventArgs e)
  53.         {
  54.             FocusFirstCell(this, new CardEventArgs(string.Empty));
  55.             base.OnLoadingRow(e);
  56.         }
  57.  
  58.         /// <summary>
  59.         /// This function will focus the First Cell in the Grid.
  60.         /// This should apparently happen on loading the grid (Game Initiation) or on resetting the Game(clickig the Reset Game button)
  61.         /// </summary>
  62.         /// <param name="sender"></param>
  63.         /// <param name="e"></param>
  64.         public void FocusFirstCell(object sender, CardEventArgs e)
  65.         {
  66.             this.Dispatcher.BeginInvoke((Action)(() =>
  67.             {
  68.                 FrameworkElement el = null;
  69.                 this.SelectedIndex = 0;
  70.                 el = this.Columns[0].GetCellContent(this.SelectedItem);
  71.                 CurrentCell = GetParent(el, typeof(DataGridCell)) as DataGridCell;
  72.                 CurrentCell.Focus();
  73.                 this.Focus();
  74.             }));
  75.         }
  76.  
  77.        
  78.         /// <summary>
  79.         /// Get the parent FrameworkElement of a given FrameworkElement
  80.         /// </summary>
  81.         /// <param name="child"></param>
  82.         /// <param name="targetType"></param>
  83.         /// <returns></returns>
  84.         private FrameworkElement GetParent(FrameworkElement child, Type targetType)
  85.         {
  86.             object parent = child.Parent;
  87.             if (parent != null)
  88.             {
  89.                 if (parent.GetType() == targetType)
  90.                 {
  91.                     return (FrameworkElement)parent;
  92.                 }
  93.                 else
  94.                 {
  95.                     return GetParent((FrameworkElement)parent, targetType);
  96.                 }
  97.             }
  98.             return null;
  99.         }
  100.         /// <summary>
  101.         /// This OnSelectCard event is fired on pressing the Enter/Space key to select a card.
  102.         /// </summary>
  103.         public event CmgEventHandler OnSelectCard;
  104.     }
  105. }
-ChildWindowBase.xaml.cs: Close the child window and pressing the Esc Key, In Silverlight this functionality is missing for many other reasons.
ChildWindowBase.xaml.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Documents;
  8. using System.Windows.Input;
  9. using System.Windows.Media;
  10. using System.Windows.Media.Animation;
  11. using System.Windows.Shapes;
  12.  
  13. namespace dSucs.ColourMemoGame.View
  14. {
  15.     public partial class ChildWindowBase : ChildWindow
  16.     {
  17.         public ChildWindowBase()
  18.         {
  19.             InitializeComponent();
  20.             this.KeyDown += new KeyEventHandler((s, e) =>
  21.             {
  22.                 if (e.Key == Key.Escape) this.Close();
  23.             });
  24.         }
  25.     }
  26. }
            II. Model development: Our model consist of two simple classes one for the Card properties and the other to generate random card numbers.  Additionally a WCF service to record user scores and user login/registration.
Card.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. namespace dSucs.ColourMemoGame.Model
  8. {
  9.     public class Card
  10.     {
  11.         #region Public Properties
  12.         public string CardID
  13.         {
  14.             get { return string.Format("CD{0}{1}", Row, Column); }
  15.         }
  16.         public int Row{ get; set; }
  17.         public int Column { get; set; }
  18.         public string FrontImage { get; set; }
  19.         public string BackImage { get; set; }
  20.         public CardStatus Status { get; set; }
  21.         public override string ToString()
  22.         {
  23.             return string.Format("CD{0}{1}({2})({3})", Row, Column, FrontImage, Status);
  24.         }
  25.         #endregion
  26.     }
  27.  
  28.     public enum CardStatus
  29.     {
  30.         UnSelected = 0,
  31.         Selected = 1,
  32.         Matched = 2
  33.     }
  34. }
GameAlgorithm.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. namespace dSucs.ColourMemoGame.Model
  8. {
  9.     public class GameAlgorithm
  10.     {
  11.         #region Construction
  12.         private GameAlgorithm()
  13.         {
  14.         }
  15.         public static GameAlgorithm CreateInstance()
  16.         {
  17.             if (_AlgoInstance == null)
  18.                 _AlgoInstance = new GameAlgorithm();
  19.             return _AlgoInstance;
  20.         }
  21.         #endregion
  22.  
  23.         #region Game Initialization
  24.         public Card[,] CreateCardsArray(int totRows, int totCols)
  25.         {
  26.             Card[,] arrCards = new Card[totRows, totCols];
  27.             int totItems = totRows * totCols;
  28.             int minNum = 1;
  29.             int maxNum = (totItems / 2);
  30.             int cntIndex = 0;
  31.             IList<int> lstRandNums = new List<int>();
  32.             while (lstRandNums.Count < totItems)
  33.             {
  34.                 IList<int> lstNumbers = GenerateRadomNumbers(minNum, maxNum);
  35.                 foreach (int num in lstNumbers)
  36.                 {
  37.                     lstRandNums.Add(num);
  38.                 }
  39.             }
  40.             IList<int> lstCardIndices = GenerateRadomNumbers(0, totItems);
  41.             for (int Row = 0; Row < totRows; Row++)
  42.             {
  43.                 for (int Column = 0; Column < totCols; Column++)
  44.                 {
  45.                     Card cd = new Card()
  46.                     {
  47.                         Row = Row,
  48.                         Column = Column,
  49.                         FrontImage = string.Format("/Images/colour{0}.png", lstRandNums[lstCardIndices[cntIndex]]),
  50.                         BackImage = string.Format("/Images/{0}", "card_bg.png"),
  51.                         Status = CardStatus.UnSelected
  52.                     };
  53.                     arrCards[Row, Column] = cd;
  54.                     cntIndex += 1;
  55.                 }
  56.             }
  57.             return arrCards;
  58.         }
  59.         public bool AreSelectedCardsMatching(Card firstCard, Card secondCard)
  60.         {
  61.             return (firstCard.CardID != secondCard.CardID && firstCard.FrontImage == secondCard.FrontImage);
  62.         }
  63.         #endregion
  64.  
  65.  
  66.         #region Helper Functions and Logics
  67.         private IList<int> GenerateRadomNumbers(int minNum, int maxNum)
  68.         {
  69.             System.Random rnd = new System.Random();
  70.             var lstRandNums = Enumerable.Range(minNum, maxNum).OrderBy(r => rnd.Next()).ToList();
  71.             return lstRandNums;
  72.         }
  73.         #endregion
  74.  
  75.  
  76.         #region Private Members
  77.         private static GameAlgorithm _AlgoInstance;
  78.         #endregion
  79.  
  80.     }
  81. }
IMemoGameService.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.ServiceModel.Web;
  7. using System.Text;
  8.  
  9. namespace dSucs.Services.ColourMemoGame
  10. {
  11.     // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
  12.     [ServiceContract]
  13.     public interface IMemoGameService
  14.     {
  15.         [OperationContract]
  16.         bool IsUserRegistered(string emailID);
  17.  
  18.         [OperationContract]
  19.         bool RegisterUser(string emailID, string password, string name);
  20.  
  21.         [OperationContract]
  22.         bool CheckLogin(string emailID, string password);
  23.  
  24.         [OperationContract]
  25.         bool GetMyPassword(string emailID);
  26.  
  27.         [OperationContract]
  28.         bool SaveScore(string emailID, int score);
  29.  
  30.         [OperationContract]
  31.         UserInfo GetUserInfo(string emailID);
  32.     }
  33.  
  34.  
  35.     // Use a data contract as illustrated in the sample below to add composite types to service operations.
  36.     [DataContract]
  37.     public class UserInfo
  38.     {
  39.         [DataMember]
  40.         public string EmailID { get; set; }
  41.  
  42.         [DataMember]
  43.         public string Name { get; set; }
  44.  
  45.         [DataMember]
  46.         public int BestScore { get; set; }
  47.  
  48.         [DataMember]
  49.         public int HighestScore { get; set; }
  50.  
  51.         [DataMember]
  52.         public int Rank { get; set; }
  53.     }
  54. }
III. DB operations: The SQLs used in the WCF db operations as below.
1. IsUserRegistered: {SELECT EXISTS( SELECT 1 FROM memogameuser  WHERE  EmailID='alex@dSucs.com')}
2. RegisterUser: {INSERT INTO MemoGameUser (EmailID, Password, Name) SELECT * FROM (SELECT 'alex@dSucs.com' EmailID, 'fJKAJzNud8LArxPGU48qpw==' Password, 'Alex' Name) AS tmp }
3. CheckLogin: {SELECT EXISTS( SELECT 1 FROM memogameuser  WHERE  EmailID='alex@dSucs.com' AND Password='fJKAJzNud8LArxPGU48qpw==' ) }
4. GetMyPassword:{SELECT emailid,password,name FROM memogameuser  WHERE  EmailID='alex@dSucs.com' }
5. SaveScore: {INSERT INTO MemoGameScore SET EmailID = 'alex@dSucs.com'    , Score = '2' ON DUPLICATE KEY UPDATE    Score = IF (Score < '2', '2', Score) }
6. GetUserInfo : {SELECT U.EmailID, U.Name,TRank.Rank,TRank.Score as BestScore, (SELECT MAX( Score ) FROM memogamescore) As HighestScore  FROM memogameuser U,  (SELECT EmailID, Score, FIND_IN_SET( Score, (  SELECT GROUP_CONCAT( Score  ORDER BY Score DESC )   FROM memogamescore )  ) AS rank   FROM MemoGameScore)  TRank WHERE  U.EmailID=TRank.EmailID and TRank.EmailID='alex@dSucs.com' }


Conclusion: The Silverlight application has been hosted on Dropbox cloud platform, however the WCF service is still on unreliable server. Hence it is expected that one can play the game any time, but the high score, login/registration functionality may not work all the time.   I believe there is quite a large scope for code improvement, hence any suggestions or criticisms are must welcome. For complete source code, please write me back.