Skip to main content

A board game in MVVM Part 3b - Commands and Converters

This is a continuation of my experience creating a quick board game in silverlight 5
Original Post
Part 1 - The Model
Part 2 - The AI
Part 3 - The View
Play the Game

Commands allow the user to interact with the game. The basics of the actual gameplay are in 2 commands.

There's obviously several others but these 2 commands constitute the bulk of the actual gameplay. Activating a piece chooses which to move, and clicking a square that is enabled (which we tell the board to handle when a piece is activated) makes the player's move. It's very useful to be able to dictate the type of the CommandParameter and I find it much more useful than some of the other implementations which always pass in "object".

  1.         public DelegateCommand<Piece> ActivatePieceCommand
  2.         {
  3.             get
  4.             {
  5.                 if (this.activatePieceCommand == null)
  6.                 {
  7.                     this.activatePieceCommand = new DelegateCommand<Piece>(ActivatePiece);
  8.                 }
  9.  
  10.                 return this.activatePieceCommand;
  11.             }
  12.         }
  13.  
  14.         public void ActivatePiece(Piece targetPiece)
  15.         {
  16.             if (Game.IsActive)
  17.             {
  18.                 if (targetPiece.IsMoveable && !targetPiece.IsActive)
  19.                 {
  20.                     targetPiece.IsActive = true;
  21.                     Game.Board.ActivePiece = targetPiece;
  22.                 }
  23.                 else if (targetPiece.IsMoveable)
  24.                 {
  25.                     targetPiece.IsActive = false;
  26.                     Game.Board.ActivePiece = null;
  27.                     Game.Board.SetMoveablePieces(Game.CurrentPlayer);
  28.                 }
  29.             }
  30.         }
  31.  
  32.         public DelegateCommand<Square> MakeMoveCommand
  33.         {
  34.             get
  35.             {
  36.                 if (this.makeMoveCommand == null)
  37.                 {
  38.                     this.makeMoveCommand = new DelegateCommand<Square>(MakeMove);
  39.                 }
  40.  
  41.                 return this.makeMoveCommand;
  42.             }
  43.         }
  44.  
  45.         public void MakeMove(Square targetSquare)
  46.         {
  47.             if (Game.IsActive)
  48.             {
  49.                 Game.MovePiece(targetSquare);
  50.             }
  51.         }

This is the basis of the gameplay and isn't too terribly hard to grasp. The key to a lot of the way we have the board set up is appropriately hiding, showing, enabling, and disabling the various squares and pieces.

There are several converters I find useful in pretty much every project I do. Most of them are very self-explanatory such as:

  1. public class BoolToVisibilityConverter : IValueConverter
  2.     {
  3.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  4.         {
  5.             return (bool)value ? Visibility.Visible : Visibility.Collapsed;
  6.         }
  7.  
  8.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  9.         {
  10.             return (Visibility)value == Visibility.Visible;
  11.         }
  12.     }

There are, however, a few that are used that would be much more interesting. If we go back to the view and look at the representation of the player's piece we see this:

  1. <Border Visibility="{Binding Occupant, Converter={StaticResource HideOnNullConverter}}">
  2.     <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Command="{Binding DataContext.ActivatePieceCommand, RelativeSource={RelativeSource AncestorType=local:MainPage}}" CommandParameter="{Binding Occupant}" IsEnabled="{Binding Occupant.IsMoveable}">
  3.         <Button.Template>
  4.             <ControlTemplate>
  5.                 <Ellipse HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="{Binding Occupant, Converter={StaticResource KingStrokeConverter}}" StrokeThickness="{Binding Occupant, Converter={StaticResource KingStrokeConverter}}" Fill="{Binding Occupant, Converter={StaticResource PlayerColorConverter}}">
  6.                     <Ellipse.Effect>
  7.                         <DropShadowEffect Opacity=".5" ShadowDepth="3" />
  8.                     </Ellipse.Effect>
  9.                 </Ellipse>
  10.             </ControlTemplate>
  11.         </Button.Template>
  12.     </Button>
  13. </Border>

The 2 fun ones are the PlayerColorConverter and the KingStrokeConverter. These bind directly to a piece to convert their properties into what is needed. What I don't see often though in converters is the ability to configure the converter.

Lets look at where I've instantiated the converters in xaml.

  1. <library:PlayerColorConverter x:Key="PlayerColorConverter" DefenderColor="LightBlue" KingColor="LightBlue" AttackerColor="LightSalmon" />
  2. <library:KingStrokeConverter x:Key="KingStrokeConverter" StrokeThickness="10">
  3.     <library:KingStrokeConverter.StrokeColor>
  4.         <RadialGradientBrush>
  5.             <GradientStopCollection>
  6.                 <GradientStop Offset="0.7" Color="LightBlue" />
  7.                 <GradientStop Offset="1" Color="Gold" />
  8.             </GradientStopCollection>
  9.         </RadialGradientBrush>
  10.     </library:KingStrokeConverter.StrokeColor>
  11. </library:KingStrokeConverter>

Notice that my converters inherit DependencyObject and actual have their own DependencyProperties. This allows the view to dictate what should show up.

Lets see it in action:

  1. public class PlayerColorConverter : DependencyObject, IValueConverter
  2.     {
  3.         public static readonly DependencyProperty AttackerColorProperty =
  4.                     DependencyProperty.Register("AttackerColor", typeof(SolidColorBrush), typeof(PlayerColorConverter), null);
  5.  
  6.         public static readonly DependencyProperty DefenderColorProperty =
  7.                     DependencyProperty.Register("DefenderColor", typeof(SolidColorBrush), typeof(PlayerColorConverter), null);
  8.  
  9.         public static readonly DependencyProperty KingColorProperty =
  10.                     DependencyProperty.Register("KingColor", typeof(SolidColorBrush), typeof(PlayerColorConverter), null);
  11.  
  12.         public SolidColorBrush AttackerColor
  13.         {
  14.             get { return (SolidColorBrush)GetValue(AttackerColorProperty); }
  15.             set { SetValue(AttackerColorProperty, value); }
  16.         }
  17.  
  18.         public SolidColorBrush DefenderColor
  19.         {
  20.             get { return (SolidColorBrush)GetValue(DefenderColorProperty); }
  21.             set { SetValue(DefenderColorProperty, value); }
  22.         }
  23.  
  24.         public SolidColorBrush KingColor
  25.         {
  26.             get { return (SolidColorBrush)GetValue(KingColorProperty); }
  27.             set { SetValue(KingColorProperty, value); }
  28.         }
  29.  
  30.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  31.         {
  32.             SolidColorBrush retVal = new SolidColorBrush(Colors.Magenta);
  33.             if (value is Piece)
  34.             {
  35.                 Piece piece = value as Piece;
  36.                 if (piece != null)
  37.                 {
  38.                     if (piece.Player == Player.Attacker)
  39.                     {
  40.                         retVal = AttackerColor;
  41.                     }
  42.                     else if (!piece.IsKing)
  43.                     {
  44.                         retVal = DefenderColor;
  45.                     }
  46.                     else
  47.                     {
  48.                         retVal = KingColor;
  49.                     }
  50.                 }
  51.             }
  52.             else if (value != null)
  53.             {
  54.                 string stringVal = value.ToString();
  55.                 if (stringVal == Player.Attacker.ToString())
  56.                 {
  57.                     retVal = AttackerColor;
  58.                 }
  59.                 else if (stringVal == Player.Defender.ToString())
  60.                 {
  61.                     retVal = DefenderColor;
  62.                 }
  63.                 else
  64.                 {
  65.                     System.Diagnostics.Debug.WriteLine("Invalid player value passed to PlayerColorConverter: " + stringVal);
  66.                 }
  67.             }
  68.  
  69.             return retVal;
  70.         }
  71.  
  72.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  73.         {
  74.             throw new NotImplementedException();
  75.         }
  76.     }
  77.  
  78.     public class KingStrokeConverter : DependencyObject, IValueConverter
  79.     {
  80.         public static readonly DependencyProperty StrokeThicknessProperty =
  81.                     DependencyProperty.Register("StrokeThickness", typeof(double), typeof(KingStrokeConverter), null);
  82.  
  83.         public static readonly DependencyProperty StrokeProperty =
  84.                     DependencyProperty.Register("Stroke", typeof(Brush), typeof(KingStrokeConverter), null);
  85.  
  86.         public double StrokeThickness
  87.         {
  88.             get { return (double)GetValue(StrokeThicknessProperty); }
  89.             set { SetValue(StrokeThicknessProperty, value); }
  90.         }
  91.  
  92.         public Brush StrokeColor
  93.         {
  94.             get { return (Brush)GetValue(StrokeProperty); }
  95.             set { SetValue(StrokeProperty, value); }
  96.         }
  97.  
  98.         public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  99.         {
  100.             Piece piece = value as Piece;
  101.             object retVal = null;
  102.             if (targetType == typeof(double))
  103.             {
  104.                 if (piece != null && piece.IsKing)
  105.                 {
  106.                     retVal = StrokeThickness;
  107.                 }
  108.                 else
  109.                 {
  110.                     retVal = 0;
  111.                 }
  112.             }
  113.             else
  114.             {
  115.                 if (piece != null && piece.IsKing)
  116.                 {
  117.                     retVal = StrokeColor;
  118.                 }
  119.                 else
  120.                 {
  121.                     retVal = new SolidColorBrush(Colors.Transparent);
  122.                 }
  123.             }
  124.  
  125.             return retVal;
  126.         }
  127.  
  128.         public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  129.         {
  130.             throw new NotImplementedException();
  131.         }
  132.     }

This allows me to separate the converter's functionality from the configuration which should rightly happen in the View. You'll also notice that I paid attention to the targetType and reacted accordingly in the KingStrokeConverter, allowing it to handle multiple aspects of "Stroke" without creating an entirely separate converter for it.

Comments

MVVM Part 3b

The mvvm game board is very interesting and I had followed every part of it. I think the codes shared by this post would be helpful to the essay paper services. Well I wish you will keep updating the latest versions of the game.

If you need to connect router

If you need to connect router it is necessary to log in with an IP address; some hardware manufacturers also use this IP address to build a network and in managing their wireless networks.192.168.l.l

tariq

Hi there, I found your blog via Google while searching for such kinda informative post and your post looks very interesting for me.

tariq

I must say, I thought this was a pretty interesting read when it comes to this topic. Liked the material. . .

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You can enable syntax highlighting of source code with the following tags: <asp>, <c>, <cpp>, <cs>, <css>, <drupal5>, <drupal6>, <html4strict>, <java>, <javascript>, <jquery>, <php>, <python>, <ruby>, <sql>, <xml>. The supported tag styles are: <foo>, [foo]. PHP source code can also be enclosed in <?php ... ?> or <% ... %>.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.