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

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.
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.
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
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.
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![]()
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).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.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.
-ColourMemoGameView.xaml : This is the application’s main UI consists of general layout for card board grid, Game Reset Button and Game/Score information.
ColourMemoGameViewModel.cs
- using System;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using System.Windows.Threading;
- using System.ComponentModel;
- using System.Collections.Generic;
- using dSucs.ColourMemoGame.Model;
- using System.Collections.ObjectModel;
- using System.ServiceModel;
- using dSucs.ColourMemoGame.ViewModel.MemoGameService;
- using System.Threading.Tasks;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class ColourMemoGameViewModel : ViewModelBase
- {
- #region Construction
- public ColourMemoGameViewModel()
- {
- Initialize();
- }
- private void Initialize()
- {
- IsResetGame = true;
- _CardBoardVM = new CardBoardViewModel();
- _CardBoardVM.OnUpdateGameInfo += UpdateGameInfo;
- _CardBoardVM.OnSaveScore += UpdateScoreInfo;
- _CardBoardVM.OnOpenLoginWindow += OpenLoginWindow;
- this.OnResetGame += _CardBoardVM.ResetGame;
- IsResetGame = false;
- MessageInfo = Helper.Msg001;
- WelComeMessage = Helper.Msg019;
- ScoreInfo = string.Format(Helper.Msg025, 0, 0);
- Task taskInitService = new Task(new Action(InitMemoGameService));
- taskInitService.Start();
- }
- #endregion
- #region Public Properties
- public CardBoardViewModel CardBoardVM
- {
- get { return _CardBoardVM; }
- set { SetProperty(ref _CardBoardVM, value, "CardBoardVM"); }
- }
- public string WelComeMessage
- {
- get
- {
- return _WelComeMessage;
- }
- set { SetProperty(ref _WelComeMessage, value, "WelComeMessage"); }
- }
- public string ScoreInfo
- {
- get
- {
- return _ScoreInfo;
- }
- set { SetProperty(ref _ScoreInfo, value, "ScoreInfo"); }
- }
- public string HighScoreInfo
- {
- get
- {
- return _HighScoreInfo;
- }
- set { SetProperty(ref _HighScoreInfo, value, "HighScoreInfo"); }
- }
- public string HighestEverScoreInfo
- {
- get
- {
- return _HighestEverScoreInfo;
- }
- set { SetProperty(ref _HighestEverScoreInfo, value, "HighestEverScoreInfo"); }
- }
- public string MessageInfo
- {
- get
- {
- return _MessageInfo;
- }
- set { SetProperty(ref _MessageInfo, value, "MessageInfo"); }
- }
- public bool IsResetGame
- {
- get
- {
- return _IsResetGame;
- }
- set { SetProperty(ref _IsResetGame, value, "IsResetGame"); }
- }
- public string EmailID
- {
- get
- {
- return _EmailID;
- }
- set { SetProperty(ref _EmailID, value, "EmailID"); }
- }
- public bool IsFullScreen
- {
- get
- {
- return _IsFullScreen;
- }
- set { SetProperty(ref _IsFullScreen, value, "IsFullScreen"); }
- }
- public string FsToolTip
- {
- get
- {
- return _FsToolTip;
- }
- set { SetProperty(ref _FsToolTip, value, "FsToolTip"); }
- }
- public string SignInMsg
- {
- get
- {
- return _SignInMsg;
- }
- set { SetProperty(ref _SignInMsg, value, "SignInMsg"); }
- }
- #endregion
- #region Commands
- public ICommand CmdResetGame
- {
- get
- {
- if (_CmdResetGame == null)
- _CmdResetGame = new RelayCommand(
- p => this.ResetGame(),
- p => true);
- return _CmdResetGame;
- }
- }
- public ICommand CmdInformation
- {
- get
- {
- if (_CmdInformation == null)
- _CmdInformation = new RelayCommand(
- p => this.ShowInformation(),
- p => true);
- return _CmdInformation;
- }
- }
- public ICommand CmdFullScreen
- {
- get
- {
- if (_CmdFullScreen == null)
- _CmdFullScreen = new RelayCommand(
- p => Application.Current.Host.Content.IsFullScreen = Application.Current.Host.Content.IsFullScreen ? false : true,
- p => true);
- return _CmdFullScreen;
- }
- }
- public ICommand CmdLogin
- {
- get
- {
- if (_CmdLogin == null)
- _CmdLogin = new RelayCommand(
- p => this.SignIn(),
- p => true);
- return _CmdLogin;
- }
- }
- #endregion
- #region UI Logics/Helpers
- public void UpdateGameInfo(object sender, CardEventArgs e)
- {
- GameInfo gameInfo = e.Result as GameInfo;
- ScoreInfo = string.Format(Helper.Msg025, gameInfo.Score, gameInfo.Move);
- MessageInfo = gameInfo.MessageInfo;
- if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetUserInfoAsync(EmailID);
- }
- private void UpdateScoreInfo(object sender, CardEventArgs e)
- {
- GameInfo gameInfo = e.Result as GameInfo;
- ScoreInfo = string.Format(Helper.Msg025, gameInfo.Score, gameInfo.Move);
- if (!string.IsNullOrEmpty(EmailID))
- _WCFClient.GetUserInfoAsync(EmailID);
- }
- public void UpdateUserInfo(object sender, CardEventArgs e)
- {
- EmailID = e.Result.ToString();
- _CardBoardVM.EmailID = EmailID;
- if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetUserInfoAsync(EmailID);
- }
- private void UpdateUserInfo(object sender, GetUserInfoCompletedEventArgs e)
- {
- _UserInfo = e.Result;
- if (!string.IsNullOrEmpty(_UserInfo.Name))
- {
- WelComeMessage = string.Format(Helper.Msg028, _UserInfo.Name);
- HighScoreInfo = string.Format(Helper.Msg026, _UserInfo.BestScore, _UserInfo.Rank);
- HighestEverScoreInfo = string.Format(Helper.Msg027, _UserInfo.HighestScore);
- SignInMsg = Helper.Msg020;
- }
- }
- private void ResetGame()
- {
- IsResetGame = true;
- ScoreInfo = string.Format(Helper.Msg025, 0, 0);
- MessageInfo = Helper.Msg001;
- OnResetGame(this, new CardEventArgs(string.Empty));
- IsResetGame = false;
- }
- private void ShowInformation()
- {
- InfoViewModel vm = InfoViewModel.GetInstance();
- OnOpenWindow(this, new CardEventArgs(vm));
- }
- public void ApplyFullScreen()
- {
- IsFullScreen = Application.Current.Host.Content.IsFullScreen;
- if (IsFullScreen)
- {
- FsToolTip = Helper.Msg022;
- }
- else
- {
- FsToolTip = Helper.Msg021;
- }
- }
- private void SignIn()
- {
- if(String.IsNullOrEmpty(EmailID))
- {
- LoginViewModel vm = new LoginViewModel();
- OnOpenWindow(this, new CardEventArgs(vm));
- }
- else if(MessageBox.Show(Helper.Msg023, Helper.Msg024,MessageBoxButton.OKCancel)==MessageBoxResult.OK)
- {
- WelComeMessage=Helper.Msg019;
- EmailID = string.Empty;
- HighScoreInfo = string.Empty;
- HighestEverScoreInfo = string.Empty;
- _CardBoardVM.EmailID = string.Empty;
- }
- }
- private void OpenLoginWindow(object sender, CardEventArgs args)
- {
- OnOpenWindow(this, args); //Relay the event so that it can be registered from the Bootstrap
- }
- private void InitMemoGameService()
- {
- EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
- BasicHttpBinding binding = new BasicHttpBinding();
- _WCFClient = new MemoGameServiceClient(binding, address);
- _WCFClient.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(UpdateUserInfo);
- }
- #endregion
- #region Member Fields & Events
- private string _MessageInfo, _WelComeMessage, _ScoreInfo, _HighScoreInfo, _HighestEverScoreInfo, _EmailID, _FsToolTip = Helper.Msg021, _SignInMsg=Helper.Msg029;
- private bool _IsResetGame = false, _IsFullScreen;
- private CardBoardViewModel _CardBoardVM;
- private RelayCommand _CmdResetGame, _CmdInformation, _CmdFullScreen, _CmdLogin;
- public event CmgEventHandler OnResetGame;
- public event CmgEventHandler OnOpenWindow;
- private MemoGameServiceClient _WCFClient;
- UserInfo _UserInfo;
- #endregion
- }
- }
-CardBoardView.xaml : This contains the datagrid of 16 = 4 X 4 cards
-CardView.xaml : This contains the Card
CardBoardViewModel.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.Model;
- using System.Collections.ObjectModel;
- using System.Windows.Threading;
- using dSucs.ColourMemoGame.ViewModel.MemoGameService;
- using System.ServiceModel;
- using System.Threading.Tasks;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class CardBoardViewModel : ViewModelBase
- {
- public CardBoardViewModel()
- {
- _Alg = GameAlgorithm.CreateInstance();
- LstCardVM = new ObservableCollection<Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>>();
- CreateCardVM();
- Task taskInitService = new Task(new Action(InitMemoGameService));
- taskInitService.Start();
- }
- public string EmailID { get; set; }
- public ObservableCollection<Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>> LstCardVM { get; set; }
- public void CreateCardVM()
- {
- int row = 4, col = 4;
- LstCardVM.Clear();
- CardViewModel vm1 = null, vm2 = null, vm3 = null, vm4 = null;
- Card[,] arrCards = _Alg.CreateCardsArray(row, col);
- for (int i = 0; i < row; i++)
- {
- vm1 = new CardViewModel(this, arrCards[i, 0]); vm1.OnSelectCard += this.SelectCard;
- vm2 = new CardViewModel(this, arrCards[i, 1]); vm2.OnSelectCard += this.SelectCard;
- vm3 = new CardViewModel(this, arrCards[i, 2]); vm3.OnSelectCard += this.SelectCard;
- vm4 = new CardViewModel(this, arrCards[i, 3]); vm4.OnSelectCard += this.SelectCard;
- LstCardVM.Add(Tuple.Create(vm1, vm2, vm3, vm4));
- }
- }
- public void SelectCard(object sender, CardEventArgs e)
- {
- CardViewModel cVM = null;
- if (sender is Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>)
- {
- Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel> tuple = sender as Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel>;
- int index = Convert.ToInt16(e.Result);
- switch (index)
- {
- case 0:
- cVM = tuple.Item1;
- break;
- case 1:
- cVM = tuple.Item2;
- break;
- case 2:
- cVM = tuple.Item3;
- break;
- case 3:
- cVM = tuple.Item4;
- break;
- }
- _SelectedCardVM = cVM;
- }
- else if (sender is CardViewModel)
- {
- _SelectedCardVM = (CardViewModel)sender;
- }
- SelectCard();
- }
- public void SelectCard()
- {
- if (!_TimerAllowOK)
- {
- _MessageInfo = Helper.Msg002;
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- return;
- }
- else
- {
- lock (_LstSelectedCardVM)
- {
- if (_SelectedCardVM.Status != CardStatus.Matched)
- {
- if (_SelectedCardVM.Status == CardStatus.UnSelected)
- {
- _SelectedCardVM.Status = CardStatus.Selected;
- _SelectedCardVM.Card.Status = CardStatus.Selected;
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.FlipUp);
- switch (_LstSelectedCardVM.Count)
- {
- case 0:
- _LstSelectedCardVM.Add(_SelectedCardVM);
- _MessageInfo = Helper.Msg003;
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- break;
- case 1:
- if (_LstSelectedCardVM[0].Card.CardID != _SelectedCardVM.Card.CardID)
- {
- _LstSelectedCardVM.Add(_SelectedCardVM);
- }
- break;
- }
- }
- else
- {
- _SelectedCardVM.Status = CardStatus.UnSelected;
- _SelectedCardVM.Card.Status = CardStatus.UnSelected;
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.FlipDown);
- switch (_LstSelectedCardVM.Count)
- {
- case 1:
- _LstSelectedCardVM.RemoveAt(0);
- _MessageInfo = Helper.Msg008;
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- break;
- case 2:
- _LstSelectedCardVM.RemoveAt(1);
- break;
- }
- }
- }
- if (_LstSelectedCardVM.Count == 2)
- {
- _Move++;
- if (_Alg.AreSelectedCardsMatching(_LstSelectedCardVM[0].Card, _LstSelectedCardVM[1].Card))
- {
- _LstSelectedCardVM[0].Status = CardStatus.Matched;
- _LstSelectedCardVM[0].Card.Status = CardStatus.Matched;
- _LstSelectedCardVM[1].Status = CardStatus.Matched;
- _LstSelectedCardVM[1].Card.Status = CardStatus.Matched;
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Match);
- _LstSelectedCardVM.Clear();
- _Score++;
- _WinningCount++;
- if (_WinningCount == 8) //TODO:Game finished, mark the count as 8 for winning
- {
- _MessageInfo = Helper.Msg004;
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Win);
- if (!string.IsNullOrEmpty(EmailID))
- {
- _WCFClient.SaveScoreAsync(EmailID, _Score);
- }
- else
- {
- LoginViewModel vm = new LoginViewModel();
- vm.OnCloseWindow += SaveScore;
- OnOpenLoginWindow(this, new CardEventArgs(vm));
- }
- }
- else
- {
- _MessageInfo = Helper.Msg005;
- }
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- }
- else
- {
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.MisMatch);
- _Score--;
- _MessageInfo = Helper.Msg006;
- _timer.Interval = TimeSpan.FromSeconds(0.6);
- _timer.Tick += FaceDownSelectedCards;
- _TimerAllowOK = false;
- _timer.Start();
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- }
- }
- }
- }
- }
- private void SaveScore(object sender, CardEventArgs e)
- {
- EmailID = e.Result.ToString();
- if(!string.IsNullOrEmpty(EmailID))
- _WCFClient.SaveScoreAsync(EmailID, _Score);
- }
- private void SaveScoreCompleted(object sender, SaveScoreCompletedEventArgs e)
- {
- if (e.Result)
- _MessageInfo = Helper.Msg011;
- else
- _MessageInfo = Helper.Msg012;
- OnSaveScore(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- }
- private void FaceDownSelectedCards(object sender, EventArgs e)
- {
- _timer.Stop();
- _TimerAllowOK = true;
- if (_LstSelectedCardVM.Count == 2)
- {
- _LstSelectedCardVM[0].Status = CardStatus.UnSelected;
- _LstSelectedCardVM[0].Card.Status = CardStatus.UnSelected;
- _LstSelectedCardVM[1].Status = CardStatus.UnSelected;
- _LstSelectedCardVM[1].Card.Status = CardStatus.UnSelected;
- _LstSelectedCardVM.Clear();
- _MessageInfo = Helper.Msg007;
- OnUpdateGameInfo(this, new CardEventArgs(new GameInfo { Score = _Score, Move = _Move, MessageInfo = _MessageInfo }));
- }
- }
- public void ResetGame(object sender, CardEventArgs e)
- {
- _Score = _Move = _WinningCount = 0;
- _MessageInfo = Helper.Msg001;
- if (_LstSelectedCardVM.Count==1)
- {
- _LstSelectedCardVM[0].Status = CardStatus.UnSelected;
- _LstSelectedCardVM[0].Card.Status = CardStatus.UnSelected;
- }
- if (_LstSelectedCardVM.Count == 2)
- {
- _LstSelectedCardVM[1].Status = CardStatus.UnSelected;
- _LstSelectedCardVM[1].Card.Status = CardStatus.UnSelected;
- }
- _LstSelectedCardVM.Clear();
- CreateCardVM();
- foreach (Tuple<CardViewModel, CardViewModel, CardViewModel, CardViewModel> tuple in LstCardVM)
- {
- ResetGame(tuple.Item1, tuple.Item1.Card);
- ResetGame(tuple.Item2, tuple.Item2.Card);
- ResetGame(tuple.Item3, tuple.Item3.Card);
- ResetGame(tuple.Item4, tuple.Item4.Card);
- }
- SoundPlayer.GetInstance().PlaySound(DSSoundEffect.Reset);
- }
- private void ResetGame(CardViewModel vm, Card cd)
- {
- vm.Card = cd;
- vm.FrontImage = cd.FrontImage;
- vm.BackImage = cd.BackImage;
- vm.Status = cd.Status;
- vm.Card.Status = cd.Status;
- }
- private void InitMemoGameService()
- {
- EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
- BasicHttpBinding binding = new BasicHttpBinding();
- _WCFClient = new MemoGameServiceClient(binding, address);
- _WCFClient.SaveScoreCompleted += new EventHandler<SaveScoreCompletedEventArgs>(SaveScoreCompleted);
- }
- public GameAlgorithm _Alg = null;
- private IList<CardViewModel> _LstCardVM = new List<CardViewModel>();
- private IList<CardViewModel> _LstSelectedCardVM = new List<CardViewModel>();
- private CardViewModel _SelectedCardVM = null;
- private bool _TimerAllowOK = true;
- private string _MessageInfo = string.Empty;
- private int _Score, _Move, _WinningCount;
- private System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer();
- public event CmgEventHandler OnOpenLoginWindow, OnUpdateGameInfo, OnSaveScore;
- private MemoGameServiceClient _WCFClient;
- }
- }
-LoginView.xaml : User can register/login through this screen. General validations of fields are done at the client side.
CardViewModel.cs
- using System;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.Model;
- using System.ComponentModel;
- using System.Collections.Generic;
- using System.Windows.Data;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class CardViewModel : ViewModelBase
- {
- public CardViewModel(object parentVM, Card card)
- {
- Card = card;
- if (Card != null)
- {
- FrontImage = Card.FrontImage;
- BackImage = Card.BackImage;
- }
- }
- public Card Card
- {
- get { return _Card; }
- set { SetProperty(ref _Card, value, "Card "); }
- }
- public string FrontImage
- {
- get
- {
- return _FrontImage;
- }
- set
- {
- SetProperty(ref _FrontImage, value, "FrontImage");
- }
- }
- public string BackImage
- {
- get
- {
- return _BackImage;
- }
- set
- {
- SetProperty(ref _BackImage, value, "BackImage");
- }
- }
- public CardStatus Status
- {
- get
- {
- return _Status;
- }
- set
- {
- SetProperty(ref _Status, value, "Status");
- }
- }
- #region Commands
- public ICommand CmdSelectCard
- {
- get
- {
- if (_CmdSelectCard == null)
- _CmdSelectCard = new RelayCommand(
- p => this.SelectCard(p, new CardEventArgs(string.Empty)),
- p => true);
- return _CmdSelectCard;
- }
- }
- #endregion
- #region UI Logics/Helpers
- public void SelectCard(object sender, CardEventArgs e)
- {
- Card = (Card)sender;
- OnSelectCard(this, new CardEventArgs(Card));
- }
- #endregion
- #region Private Members
- private string _FrontImage, _BackImage /*, _CardID*/ ;
- private CardStatus _Status;
- private ICommand _CmdSelectCard;
- private Card _Card;
- public event CmgEventHandler OnSelectCard;
- #endregion
- }
- }
- SoundPlayer.cs : To play the sound effects while selecting/unselecting/matching cards or resetting the game.
LoginViewModel.cs
- using dSucs.ColourMemoGame.ViewModel.MemoGameService;
- using System;
- using System.ComponentModel;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.Model;
- using System.ServiceModel;
- using System.Threading.Tasks;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class LoginViewModel : ViewModelBase, IDataErrorInfo
- {
- #region Construction
- public LoginViewModel()
- {
- Initialize();
- }
- private void Initialize()
- {
- MessageInfo = Helper.Msg009;
- ShowButtonSendPassword = "Collapsed";
- ShowTextBoxName = "Collapsed";
- IsButtonOKEnabled = false;
- _validator = MemoGameValidator.GetInstance();
- Task taskInitService = new Task(new Action(InitMemoGameService));
- taskInitService.Start();
- }
- #endregion
- #region Properties
- public string MessageInfo
- {
- get { return _MessageInfo; }
- set { SetProperty(ref _MessageInfo, value, PropMessageInfo); }
- }
- public string EmailID
- {
- get { return _EmailID; }
- set { SetProperty(ref _EmailID, value, PropEmailID); }
- }
- public string Password
- {
- get { return _Password; }
- set { SetProperty(ref _Password, value, PropPassword); }
- }
- public string Name
- {
- get { return _Name; }
- set { SetProperty(ref _Name, value, PropName); }
- }
- public string Error
- {
- get { return _Error; }
- }
- public string ShowTextBoxName
- {
- get { return _ShowTextBoxName; }
- set { SetProperty(ref _ShowTextBoxName, value, PropShowTextBoxName); }
- }
- public string ShowButtonSendPassword
- {
- get { return _ShowButtonSendPassword; }
- set { SetProperty(ref _ShowButtonSendPassword, value, PropShowButtonSendPassword); }
- }
- public bool IsButtonOKEnabled
- {
- get { return _IsButtonOKEnabled; }
- set { SetProperty(ref _IsButtonOKEnabled, value, PropIsButtonOKEnabled); }
- }
- public string this[string propertyName]
- {
- get
- {
- _Error = null;
- switch (propertyName)
- {
- case "EmailID":
- _Error = _validator.Validate<string>(propertyName, EmailID);
- break;
- case "Password":
- _Error = _validator.Validate<string>(propertyName, Password);
- if (_IsUserRegistered)
- IsButtonOKEnabled = string.IsNullOrEmpty(_Error);
- break;
- case "Name":
- if (!_IsUserRegistered)
- _Error = _validator.Validate<string>(propertyName, Name);
- else
- _Error = null;
- IsButtonOKEnabled = string.IsNullOrEmpty(_Error);
- break;
- }
- return _Error;
- }
- }
- public int Score { get; set; }
- #endregion
- #region Commands
- public ICommand CmdEmailIDLostFocus
- {
- get
- {
- if (_CmdEmailIDLostFocus == null)
- _CmdEmailIDLostFocus = new RelayCommand(
- p => this.OnEmailIDLostFocus(p),
- p => true);
- return _CmdEmailIDLostFocus;
- }
- }
- public ICommand CmdClickOK
- {
- get
- {
- if (_CmdClickOK == null)
- _CmdClickOK = new RelayCommand(
- p => this.OnClickOK(p),
- p => true);
- return _CmdClickOK;
- }
- }
- public ICommand CmdClickCancel
- {
- get
- {
- if (_CmdClickCancel == null)
- _CmdClickCancel = new RelayCommand(
- p => this.OnClickCancel(p),
- p => true);
- return _CmdClickCancel;
- }
- }
- public ICommand CmdClickSendMyPassword
- {
- get
- {
- if (_CmdClickSendMyPassword == null)
- _CmdClickSendMyPassword = new RelayCommand(
- p => this.OnClickSendMyPassword(p),
- p => true);
- return _CmdClickSendMyPassword;
- }
- }
- #endregion
- #region UI Functions and Logic
- private void OnEmailIDLostFocus(object sender)
- {
- EmailID = sender.ToString();
- if (!string.IsNullOrEmpty(EmailID)) _WCFClient.IsUserRegisteredAsync(EmailID);
- }
- private void CheckIsUserRegisterd(object sender, IsUserRegisteredCompletedEventArgs e)
- {
- try
- {
- _IsUserRegistered = e.Result;
- }
- catch(Exception ex)
- {
- MessageInfo = Helper.Msg018;
- }
- ShowTextBoxName = _IsUserRegistered ? "Collapsed" : "Visible";
- }
- private void OnClickOK(object sender)
- {
- if (_IsUserRegistered)
- {
- _WCFClient.CheckLoginAsync(EmailID, Password);
- }
- else
- _WCFClient.RegisterUserAsync(EmailID, Password, Name);
- }
- private void RegisterUser(object sender, RegisterUserCompletedEventArgs e)
- {
- bool execComplete = false;
- try
- {
- execComplete = e.Result;
- }
- catch (Exception ex)
- {
- MessageInfo = Helper.Msg018;
- }
- if (execComplete)
- {
- OnCloseWindow(this, new CardEventArgs(this.EmailID));
- }
- else
- {
- MessageInfo = Helper.Msg015;
- }
- }
- private void CheckLogin(object sender, CheckLoginCompletedEventArgs e)
- {
- try
- {
- _LoginAttemptCount++;
- _CheckLogin = e.Result;
- }
- catch(Exception ex)
- {
- MessageInfo = Helper.Msg018;
- }
- if (_CheckLogin)
- {
- OnCloseWindow(this, new CardEventArgs(EmailID));
- }
- else if (!_CheckLogin && _LoginAttemptCount < 3)
- {
- MessageInfo = string.Format(Helper.Msg017, _LoginAttemptCount);
- }
- else if (_LoginAttemptCount >= 3)
- {
- MessageInfo = Helper.Msg014;
- ShowButtonSendPassword = "Visible";
- }
- }
- private void OnClickSendMyPassword(object sender)
- {
- if (!string.IsNullOrEmpty(EmailID)) _WCFClient.GetMyPasswordAsync(EmailID);
- }
- private void SendMyPassword(object sender, GetMyPasswordCompletedEventArgs e)
- {
- bool execComplete = false;
- try
- {
- execComplete = e.Result;
- }
- catch(Exception ex)
- {
- MessageInfo = Helper.Msg013;
- }
- if (execComplete)
- {
- MessageInfo = string.Format(Helper.Msg016, EmailID);
- _LoginAttemptCount=0;
- ShowButtonSendPassword="Collapsed";
- }
- else
- {
- MessageInfo = Helper.Msg013;
- }
- }
- public void OnClickCancel(object sender)
- {
- OnCloseWindow(this, new CardEventArgs(string.Empty));
- }
- private void InitMemoGameService()
- {
- EndpointAddress address = new EndpointAddress(GlobalVariables.EndPointAddress);
- BasicHttpBinding binding = new BasicHttpBinding();
- //binding.SendTimeout = TimeSpan.FromSeconds(15);
- _WCFClient = new MemoGameServiceClient(binding, address);
- _WCFClient.IsUserRegisteredCompleted += new EventHandler<IsUserRegisteredCompletedEventArgs>(CheckIsUserRegisterd);
- _WCFClient.RegisterUserCompleted += new EventHandler<RegisterUserCompletedEventArgs>(RegisterUser);
- _WCFClient.CheckLoginCompleted += new EventHandler<CheckLoginCompletedEventArgs>(CheckLogin);
- _WCFClient.GetMyPasswordCompleted += new EventHandler<GetMyPasswordCompletedEventArgs>(SendMyPassword);
- }
- #endregion
- #region Private Members
- string _MessageInfo, _EmailID, _Password, _Name, _Error, _ShowTextBoxName, _ShowButtonSendPassword;
- string PropMessageInfo = "MessageInfo", PropEmailID = "EmailID", PropPassword = "Password", PropName = "Name";
- string PropShowTextBoxName = "ShowTextBoxName", PropShowButtonSendPassword = "ShowButtonSendPassword", PropIsButtonOKEnabled = "IsButtonOKEnabled";
- bool _IsUserRegistered = true, _IsButtonOKEnabled=false,_CheckLogin = false;
- int _LoginAttemptCount = 0;
- ICommand _CmdEmailIDLostFocus, _CmdClickOK, _CmdClickCancel, _CmdClickSendMyPassword;
- MemoGameValidator _validator = null;
- MemoGameServiceClient _WCFClient;
- public event CmgEventHandler OnCloseWindow;
- #endregion
- }
- }
MemoGameValidator.cs
- using System;
- using System.Text.RegularExpressions;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class MemoGameValidator
- {
- private MemoGameValidator()
- {
- }
- public static MemoGameValidator GetInstance()
- {
- if (_instance == null) _instance = new MemoGameValidator();
- return _instance;
- }
- public string Validate<T>(string PropertyName, T value)
- {
- string error = null;
- switch (PropertyName)
- {
- case "EmailID":
- if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
- {
- error = "EmailID can not be left blank.";
- }
- else
- {
- 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})(\]?)$";
- if (!Regex.IsMatch(value.ToString(), emailPattern))
- error = "Invalid Email Address.";
- }
- break;
- case "Password":
- if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
- {
- error = "Password needs to be entered.";
- }
- else
- if ((value is string) && (!string.IsNullOrEmpty(value.ToString())))
- {
- string pwdPattern = @"((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[~!@#$%^&*()]).{4,8})";
- if (!Regex.IsMatch(value.ToString(), pwdPattern))
- error = "Must contain at least one digit, one lcase letter, one ucase letter, one special characters having length 4 to 8.";
- }
- break;
- case "Name":
- if (!(value is string) || (string.IsNullOrEmpty(value.ToString())))
- {
- error = "New User? Please provide your name.";
- }
- else if ((value is string) && (!string.IsNullOrEmpty(value.ToString())))
- {
- string namePattern = @"^[a-zA-Z0-9]{3,10}$";
- if (!Regex.IsMatch(value.ToString(), namePattern))
- error = "Name should contain only alpha-numeric characters of length 3 to 10";
- }
- break;
- default:
- break;
- }
- return error;
- }
- private static MemoGameValidator _instance = null;
- }
- }
-ViewModelBase: This is a base class for all the ViewModels which provides a common implementation of INotifyPropertyChanged.
SoundPlayer.cs
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Resources;
- using Microsoft.Xna.Framework.Audio;
- using dSucs.ColourMemoGame.Model;
- namespace dSucs.ColourMemoGame.ViewModel
- {
- public class SoundPlayer
- {
- #region Construction and Initialization
- private SoundPlayer()
- {
- InitializeEffects();
- }
- public static SoundPlayer GetInstance()
- {
- if (_instance == null) _instance = new SoundPlayer();
- return _instance;
- }
- void InitializeEffects()
- {
- this.AllSoundEffects = new Dictionary<string, SoundEffect>();
- this.AllSoundEffectInstances = new Dictionary<string, SoundEffectInstance>();
- foreach (var sound in Sounds)
- {
- this.AllSoundEffects.Add(sound, SoundEffect.FromStream(GetResourceStreamForNote(sound)));
- }
- }
- #endregion
- #region public methods
- public void PlaySound(string sound)
- {
- SoundEffectInstance instance = null;
- if (!this.AllSoundEffectInstances.ContainsKey(sound))
- {
- if (this.AllSoundEffects.ContainsKey(sound))
- {
- instance = this.AllSoundEffects[sound].CreateInstance();
- AllSoundEffectInstances.Add(sound, instance);
- }
- }
- else
- {
- instance = this.AllSoundEffectInstances[sound];
- }
- instance.Play();
- }
- public void StopSound(string sound)
- {
- if (this.AllSoundEffectInstances.ContainsKey(sound))
- {
- this.AllSoundEffectInstances[sound].Dispose();
- this.AllSoundEffectInstances.Remove(sound);
- }
- }
- public void StopAllSound()
- {
- foreach (var sound in Sounds)
- {
- StopSound(sound);
- }
- }
- #endregion
- #region Private Functions
- private Stream GetResourceStreamForNote(string wavFile)
- {
- StreamResourceInfo sri =
- Application.GetResourceStream(
- new Uri(string.Format(Helper.MediaFile, wavFile),
- UriKind.Relative));
- return (sri.Stream);
- }
- #endregion
- #region Private Members
- string[] Sounds =
- {
- DSSoundEffect.FlipUp, DSSoundEffect.FlipDown,
- DSSoundEffect.Match, DSSoundEffect.MisMatch,
- DSSoundEffect.Win, DSSoundEffect.Reset
- };
- Dictionary<string, SoundEffect> AllSoundEffects;
- Dictionary<string, SoundEffectInstance> AllSoundEffectInstances;
- private static SoundPlayer _instance = null;
- #endregion
- }
- public static class DSSoundEffect
- {
- public static string FlipUp = "FlipUp", FlipDown = "FlipDown", Match = "Match", MisMatch = "MisMatch", Win = "Win", Reset = "Reset";
- }
- }
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.
ViewModelBase.cs
- public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
- {
- #region Constructor
- protected ViewModelBase()
- {
- }
- #endregion
- #region INotifyPropertyChanged Members
- protected virtual void OnPropertyChanged(string propertyName)
- {
- PropertyChangedEventHandler handler = PropertyChanged;
- if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
- }
- protected bool SetProperty<T>(ref T field, T value, string propertyName)
- {
- if (EqualityComparer<T>.Default.Equals(field, value)) return false;
- field = value;
- OnPropertyChanged(propertyName);
- return true;
- }
- public event PropertyChangedEventHandler PropertyChanged;
- #endregion
- #region IDisposable Members
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposed)
- return;
- if (disposing)
- {
- // Free any other managed objects here.
- //
- }
- // Free any unmanaged objects here.
- //
- disposed = true;
- }
- #if DEBUG
- ~ViewModelBase()
- {
- Dispose(false);
- }
- #endif
- #endregion
- public virtual string DisplayName { get; protected set; }
- bool disposed = false;
- }
-BootStrap.cs: This is to help the App to start faster.
-Converter.cs: This is to help choose a templated view of CardBoard loosely coupling the View with its corresponding ViewModel.
BootStrap.cs
- using System;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.View;
- using dSucs.ColourMemoGame.ViewModel;
- using dSucs.ColourMemoGame.Controls;
- using System.Threading.Tasks;
- using dSucs.ColourMemoGame.Model;
- namespace dSucs.ColourMemoGame
- {
- public class BootStrap
- {
- private BootStrap()
- {
- Task taskSetEndPointAddress = new Task(new Action(SetEndPointAddress));
- taskSetEndPointAddress.Start();
- }
- /// <summary>
- /// Instantiate the Sinlgton Instane of Boostrap
- /// </summary>
- /// <returns></returns>
- public static BootStrap GetInstance()
- {
- if (_instance == null) _instance = new BootStrap();
- return _instance;
- }
- /// <summary>
- /// Free reources;
- /// </summary>
- public void FreeResouces()
- {
- SoundPlayer.GetInstance().StopAllSound();
- }
- /// <summary>
- /// Instantiate the main view and ViewModel, then set the DataContext
- /// </summary>
- /// <returns></returns>
- public UserControl Init()
- {
- _CmgView = new ColourMemoGameView();
- _CmgVM = new ColourMemoGameViewModel();
- _CmgView.DataContext = _CmgVM;
- App.Current.Host.Content.FullScreenChanged += new EventHandler((s, e) =>
- {
- _CmgVM.ApplyFullScreen();
- });
- _CmgVM.OnOpenWindow += this.OpenWindow;
- return _CmgView;
- }
- public void OpenWindow(object sender, CardEventArgs e)
- {
- if (e.Result is InfoViewModel)
- {
- InfoViewModel vm = e.Result as InfoViewModel;
- _InfoView = new InfoView();
- _InfoView.DataContext = vm;
- _InfoView.Show();
- }
- else if (e.Result is LoginViewModel)
- {
- LoginViewModel vm = e.Result as LoginViewModel;
- vm.OnCloseWindow += this.CloseWindow;
- vm.OnCloseWindow += _CmgVM.UpdateUserInfo;
- _LoginView = new LoginView();
- _LoginView.DataContext = vm;
- _LoginView.Show();
- }
- }
- public void CloseWindow(object sender, CardEventArgs e)
- {
- if (sender is InfoViewModel)
- {
- _InfoView.Close();
- }
- else if (sender is LoginViewModel)
- {
- _LoginView.Close();
- }
- }
- private void SetEndPointAddress()
- {
- string serviceUri = Helper.ServicePathTmp;
- var serverFile = new Uri(string.Format(Helper.ResourceFile, Helper.ServiceFile), UriKind.Relative);
- var rs = Application.GetResourceStream(serverFile);
- if (rs != null)
- {
- System.IO.StreamReader reader = new System.IO.StreamReader(rs.Stream);
- try
- {
- if (reader != null)
- {
- string line = string.Empty;
- string key = Helper.ServiceKey;
- while ((line = reader.ReadLine()) != null)
- {
- if (line.StartsWith(key))
- {
- serviceUri = line.Substring(key.Length + 1);
- }
- }
- }
- }
- catch
- { }
- }
- GlobalVariables.EndPointAddress = serviceUri;
- }
- private static BootStrap _instance = null;
- private UserControl _CmgView;
- private ChildWindow _InfoView, _LoginView;
- private ColourMemoGameViewModel _CmgVM;
- }
- }
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.
Converter.cs
- using System;
- using System.Globalization;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.Model;
- using System.Collections.Generic;
- using dSucs.ColourMemoGame.ViewModel;
- using dSucs.ColourMemoGame.View;
- namespace dSucs.ColourMemoGame
- {
- public class ViewTemplateChooser : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- if (value is CardBoardViewModel)
- {
- CardBoardViewModel vm = (CardBoardViewModel)value;
- CardBoardView vw= new CardBoardView() { DataContext = vm };
- vw.dgCardBoard.OnSelectCard += vm.SelectCard; //To select a card on pressing Enter/Space key
- return vw;
- }
- return value;
- }
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- throw new NotImplementedException();
- }
- }
- }
-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.
-ChildWindowBase.xaml.cs: Close the child window and pressing the Esc Key, In Silverlight this functionality is missing for many other reasons.
CustomDataGrid.cs
- using System;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Ink;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- using dSucs.ColourMemoGame.ViewModel;
- using dSucs.ColourMemoGame.View;
- namespace dSucs.ColourMemoGame.Controls
- {
- public class CustomDataGrid : DataGrid
- {
- /// <summary>
- /// Represents the CurrentCell of the customized Grid
- /// </summary>
- public DataGridCell CurrentCell { get; set; }
- /// <summary>
- /// This function customizes the original keyboard events of the Grid to select Cards the CardBaord
- /// </summary>
- /// <param name="e"></param>
- protected override void OnKeyDown(KeyEventArgs e)
- {
- if (e.Key == Key.Enter || e.Key == Key.Space)
- {
- OnSelectCard(this.SelectedItem, new CardEventArgs(this.CurrentColumn.DisplayIndex)); //Raise Card selection event from the CustomGrid
- e.Handled = true;
- }
- else if (e.Key == Key.Up || e.Key == Key.Down || e.Key == Key.Left || e.Key == Key.Right)
- {
- FrameworkElement el = this.Columns[this.CurrentColumn.DisplayIndex].GetCellContent(this.SelectedItem);
- CurrentCell = GetParent(el, typeof(DataGridCell)) as DataGridCell;
- this.Focus();
- CurrentCell.Focus();
- }
- else if (e.Key == Key.Tab)
- {
- e.Handled = true;
- }
- base.OnKeyDown(e);
- }
- /// <summary>
- /// This will focus the first element in the Grid on loading
- /// </summary>
- /// <param name="e"></param>
- protected override void OnLoadingRow(DataGridRowEventArgs e)
- {
- FocusFirstCell(this, new CardEventArgs(string.Empty));
- base.OnLoadingRow(e);
- }
- /// <summary>
- /// This function will focus the First Cell in the Grid.
- /// This should apparently happen on loading the grid (Game Initiation) or on resetting the Game(clickig the Reset Game button)
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- public void FocusFirstCell(object sender, CardEventArgs e)
- {
- this.Dispatcher.BeginInvoke((Action)(() =>
- {
- FrameworkElement el = null;
- this.SelectedIndex = 0;
- el = this.Columns[0].GetCellContent(this.SelectedItem);
- CurrentCell = GetParent(el, typeof(DataGridCell)) as DataGridCell;
- CurrentCell.Focus();
- this.Focus();
- }));
- }
- /// <summary>
- /// Get the parent FrameworkElement of a given FrameworkElement
- /// </summary>
- /// <param name="child"></param>
- /// <param name="targetType"></param>
- /// <returns></returns>
- private FrameworkElement GetParent(FrameworkElement child, Type targetType)
- {
- object parent = child.Parent;
- if (parent != null)
- {
- if (parent.GetType() == targetType)
- {
- return (FrameworkElement)parent;
- }
- else
- {
- return GetParent((FrameworkElement)parent, targetType);
- }
- }
- return null;
- }
- /// <summary>
- /// This OnSelectCard event is fired on pressing the Enter/Space key to select a card.
- /// </summary>
- public event CmgEventHandler OnSelectCard;
- }
- }
ChildWindowBase.xaml.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Net;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Animation;
- using System.Windows.Shapes;
- namespace dSucs.ColourMemoGame.View
- {
- public partial class ChildWindowBase : ChildWindow
- {
- public ChildWindowBase()
- {
- InitializeComponent();
- this.KeyDown += new KeyEventHandler((s, e) =>
- {
- if (e.Key == Key.Escape) this.Close();
- });
- }
- }
- }
Card.cs
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using System.Text;
- namespace dSucs.ColourMemoGame.Model
- {
- public class Card
- {
- #region Public Properties
- public string CardID
- {
- get { return string.Format("CD{0}{1}", Row, Column); }
- }
- public int Row{ get; set; }
- public int Column { get; set; }
- public string FrontImage { get; set; }
- public string BackImage { get; set; }
- public CardStatus Status { get; set; }
- public override string ToString()
- {
- return string.Format("CD{0}{1}({2})({3})", Row, Column, FrontImage, Status);
- }
- #endregion
- }
- public enum CardStatus
- {
- UnSelected = 0,
- Selected = 1,
- Matched = 2
- }
- }
GameAlgorithm.cs
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Text;
- namespace dSucs.ColourMemoGame.Model
- {
- public class GameAlgorithm
- {
- #region Construction
- private GameAlgorithm()
- {
- }
- public static GameAlgorithm CreateInstance()
- {
- if (_AlgoInstance == null)
- _AlgoInstance = new GameAlgorithm();
- return _AlgoInstance;
- }
- #endregion
- #region Game Initialization
- public Card[,] CreateCardsArray(int totRows, int totCols)
- {
- Card[,] arrCards = new Card[totRows, totCols];
- int totItems = totRows * totCols;
- int minNum = 1;
- int maxNum = (totItems / 2);
- int cntIndex = 0;
- IList<int> lstRandNums = new List<int>();
- while (lstRandNums.Count < totItems)
- {
- IList<int> lstNumbers = GenerateRadomNumbers(minNum, maxNum);
- foreach (int num in lstNumbers)
- {
- lstRandNums.Add(num);
- }
- }
- IList<int> lstCardIndices = GenerateRadomNumbers(0, totItems);
- for (int Row = 0; Row < totRows; Row++)
- {
- for (int Column = 0; Column < totCols; Column++)
- {
- Card cd = new Card()
- {
- Row = Row,
- Column = Column,
- FrontImage = string.Format("/Images/colour{0}.png", lstRandNums[lstCardIndices[cntIndex]]),
- BackImage = string.Format("/Images/{0}", "card_bg.png"),
- Status = CardStatus.UnSelected
- };
- arrCards[Row, Column] = cd;
- cntIndex += 1;
- }
- }
- return arrCards;
- }
- public bool AreSelectedCardsMatching(Card firstCard, Card secondCard)
- {
- return (firstCard.CardID != secondCard.CardID && firstCard.FrontImage == secondCard.FrontImage);
- }
- #endregion
- #region Helper Functions and Logics
- private IList<int> GenerateRadomNumbers(int minNum, int maxNum)
- {
- System.Random rnd = new System.Random();
- var lstRandNums = Enumerable.Range(minNum, maxNum).OrderBy(r => rnd.Next()).ToList();
- return lstRandNums;
- }
- #endregion
- #region Private Members
- private static GameAlgorithm _AlgoInstance;
- #endregion
- }
- }
IMemoGameService.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.Serialization;
- using System.ServiceModel;
- using System.ServiceModel.Web;
- using System.Text;
- namespace dSucs.Services.ColourMemoGame
- {
- // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
- [ServiceContract]
- public interface IMemoGameService
- {
- [OperationContract]
- bool IsUserRegistered(string emailID);
- [OperationContract]
- bool RegisterUser(string emailID, string password, string name);
- [OperationContract]
- bool CheckLogin(string emailID, string password);
- [OperationContract]
- bool GetMyPassword(string emailID);
- [OperationContract]
- bool SaveScore(string emailID, int score);
- [OperationContract]
- UserInfo GetUserInfo(string emailID);
- }
- // Use a data contract as illustrated in the sample below to add composite types to service operations.
- [DataContract]
- public class UserInfo
- {
- [DataMember]
- public string EmailID { get; set; }
- [DataMember]
- public string Name { get; set; }
- [DataMember]
- public int BestScore { get; set; }
- [DataMember]
- public int HighestScore { get; set; }
- [DataMember]
- public int Rank { get; set; }
- }
- }
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.
1 comment:
Could you pls send the full source code of this game
Post a Comment