5

I am trying to build a chess application. I have the backend logic in place (almost). But I havent worked much on UIs. I plan to use C# and I hear WPF is the way to go.

Could you please give me pointers as to how to build the UI interface and the various coins over it? Will I have to build some kind of controls for the coins? Also, what control should I use to develop the board?

Aadith Ramia
  • 10,005
  • 19
  • 67
  • 86

4 Answers4

34

I'm gonna have another shot at this question and actually show you how to do this properly with WPF. Be warned though, if you've never done any WPF before then this might be a bit overwhelming at first, but hopefully it should give some idea of just how data-driven WPF is and how powerful it can be once you get the hang of it.

First you'll need to create a WPF project and run NuGet package manager to add the MVVM Light package (or add it manually if you prefer). Next you'll want to set up a couple of enums to define your piece type and a class to represent an actual instance of a piece on the board:

public enum PieceType
{
    Pawn,
    Rook,
    Knight,
    Bishop,
    Queen,
    King
}

public enum Player
{
    White,
    Black
}

public class ChessPiece : ViewModelBase
{
    private Point _Pos;
    public Point Pos
    {
        get { return this._Pos; }
        set { this._Pos = value; RaisePropertyChanged(() => this.Pos); }
    }

    private PieceType _Type;
    public PieceType Type
    {
        get { return this._Type; }
        set { this._Type = value; RaisePropertyChanged(() => this.Type); }
    }

    private Player _Player;
    public Player Player
    {
        get { return this._Player; }
        set { this._Player = value; RaisePropertyChanged(() => this.Player); }
    }
}

Almost everything else from here on is done in XAML. First you need to create a checkerboard brush for the board itself, this can be a bitmap if you like but I'll go ahead and create a geometry drawing instead. This code needs to be placed in your Window.Resources section:

<DrawingBrush x:Key="Checkerboard" Stretch="None" TileMode="Tile" Viewport="0,0,2,2" ViewportUnits="Absolute">
        <DrawingBrush.Drawing>
            <DrawingGroup>
                <GeometryDrawing Brush="Tan">
                    <GeometryDrawing.Geometry>
                        <RectangleGeometry Rect="0,0,2,2" />
                    </GeometryDrawing.Geometry>
                </GeometryDrawing>
                <GeometryDrawing Brush="Brown">
                    <GeometryDrawing.Geometry>
                        <GeometryGroup>
                            <RectangleGeometry Rect="0,0,1,1" />
                            <RectangleGeometry Rect="1,1,1,1" />
                        </GeometryGroup>
                    </GeometryDrawing.Geometry>
                </GeometryDrawing>
            </DrawingGroup>
        </DrawingBrush.Drawing>
    </DrawingBrush>

Next up you'll need a way to select an image based on the piece you're rendering. There are many ways to do this but the way I'm going to do it here is to declare an Image style and then use triggers that select the appropriate bitmap based on the piece type and player. For this example I'll just hot-link to some clip-art on the wpclipart site. This block of XAML is long but it's just doing the same thing for each piece type:

<Style x:Key="ChessPieceStyle" TargetType="{x:Type Image}">
        <Style.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Pawn}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>  
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_pawn_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Rook}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_rook_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Knight}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_knight_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Bishop}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_bishop_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Queen}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_queen_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.King}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.White}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_white_king_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Pawn}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_pawn_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Rook}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_rook_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Knight}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_knight_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Bishop}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_bishop_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.Queen}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_queen_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding Type}" Value="{x:Static local:PieceType.King}"/>
                    <Condition Binding="{Binding Player}" Value="{x:Static local:Player.Black}"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="Image.Source" Value="http://www.wpclipart.com/recreation/games/chess/chess_set_1/chess_piece_black_king_T.png" />
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>

And now the board itself. With the above code set up this bit is surprisingly short, we're just going to render an ItemsControl (i.e. a list of items), we'll set the container to be a canvas, we'll set it's background to our checkerboard and for each piece we'll set the position based on the Pos property. Obviously we'll also use the ChessPieceStyle Image style that we set up above to select the correct image to render:

<ItemsControl Name="ChessBoard">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas Width="8" Height="8" Background="{StaticResource Checkerboard}"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid Width="1" Height="1">
                    <Image Width="0.8" Height="0.8" Style="{StaticResource ChessPieceStyle}" />                     
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Left" Value="{Binding Pos.X}" />
                <Setter Property="Canvas.Top" Value="{Binding Pos.Y}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

And that's it! We now have everything we need to render a chess board. All that remains is to create an array of our pieces, put it in an ObservableCollection (so that the GUI gets updates when pieces added and removed) and bind it to our chessboard:

this.ChessBoard.ItemsSource = new ObservableCollection<ChessPiece>
        {
            new ChessPiece{Pos=new Point(0, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(1, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(2, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(3, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(4, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(5, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(6, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(7, 6), Type=PieceType.Pawn, Player=Player.White},
            new ChessPiece{Pos=new Point(0, 7), Type=PieceType.Rook, Player=Player.White},
            new ChessPiece{Pos=new Point(1, 7), Type=PieceType.Knight, Player=Player.White},
            new ChessPiece{Pos=new Point(2, 7), Type=PieceType.Bishop, Player=Player.White},
            new ChessPiece{Pos=new Point(3, 7), Type=PieceType.King, Player=Player.White},
            new ChessPiece{Pos=new Point(4, 7), Type=PieceType.Queen, Player=Player.White},
            new ChessPiece{Pos=new Point(5, 7), Type=PieceType.Bishop, Player=Player.White},
            new ChessPiece{Pos=new Point(6, 7), Type=PieceType.Knight, Player=Player.White},
            new ChessPiece{Pos=new Point(7, 7), Type=PieceType.Rook, Player=Player.White},
            new ChessPiece{Pos=new Point(0, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(1, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(2, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(3, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(4, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(5, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(6, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(7, 1), Type=PieceType.Pawn, Player=Player.Black},
            new ChessPiece{Pos=new Point(0, 0), Type=PieceType.Rook, Player=Player.Black},
            new ChessPiece{Pos=new Point(1, 0), Type=PieceType.Knight, Player=Player.Black},
            new ChessPiece{Pos=new Point(2, 0), Type=PieceType.Bishop, Player=Player.Black},
            new ChessPiece{Pos=new Point(3, 0), Type=PieceType.King, Player=Player.Black},
            new ChessPiece{Pos=new Point(4, 0), Type=PieceType.Queen, Player=Player.Black},
            new ChessPiece{Pos=new Point(5, 0), Type=PieceType.Bishop, Player=Player.Black},
            new ChessPiece{Pos=new Point(6, 0), Type=PieceType.Knight, Player=Player.Black},
            new ChessPiece{Pos=new Point(7, 0), Type=PieceType.Rook, Player=Player.Black}
        };

And here's the result:

WPF chessboard

That might seem like a lot of work just to draw a chessboard but keep in mind that this is now a completely data-driven interface....if you add or remove pieces or change any of the fields in the piece in your piece array then those changes will propagate through to the front-end immediately. It's also very easy to expand, modify and add additional features such as animation, 3D, reflections etc. But perhaps the most impressive thing is that I didn't have to create any custom user controls at all in order to do this, the WPF data binding mechanism is powerful enough to support this kind of stuff out-of-the-box easily.

If you need any further clarifications and/or would like to see a standalone project then by all means let me know.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Wow Mark! now thats some answer..I am trying to follow your steps..but I should confess that you are talking to a total wpf rookie...could you please elaborate as to where to add all that XML content? so far I've added the MVVM package and adding cs code should be fine...I'm stuck with fitting in the XMLs – Aadith Ramia Dec 14 '13 at 17:51
  • 2
    @Aadith the checkerboard and Image style should go inside your Windows Resource block (i.e ...here...). The ItemsControl is content so it replaces the tags that get added by default. Probably easiest if I just point you to a [project that you can use as a starting point](http://www.ppl-pilot.com/files/ChessDemo.zip). – Mark Feldman Dec 14 '13 at 20:59
  • This is great..thanks a lot Mark! Can you please tell me what it takes to add functionality to move the coins? an event handler would have to be triggered ach time a user tries to move a coin..btw, would you consider collaborating with me on the project? – Aadith Ramia Dec 18 '13 at 05:07
  • 2
    Awesome work! Very instructive. A couple of minor details (well maybe not minor for a chess player LOL): Reverse dark and light squares and starting squares for Kings and Queens – LJ VanKuiken Dec 22 '17 at 00:14
  • @Aadith if you are so impressed by this answer why did you never mark it as accepted? I wonder – Mong Zhu Jun 18 '19 at 19:18
  • That's a great answer, but how can I change the color of the square when I click on it? for my understanding, the board here is simply a geometric drawing... – Liroshka Mar 11 '20 at 12:36
  • @Liroshka put your `ItemsControl` on a grid with an 8x8 UniformGrid behind it and populate it with 64 buttons i.e. one for each square. Tie each button to a view model with fields set for which color it is and whether or not it can be selected etc which you update for each turn. Then give those buttons a custom template with DataTriggers etc to change the hover style based on the view model properties. – Mark Feldman Mar 11 '20 at 22:07
1

If it's 2D then Canvas is the obvious way to go, you can place Images on it or anything else you like and move them to arbitrary positions. You can also wrap it up in a Viewbox so that it, and everything in it, scales automatically to the parent window.

In general you shouldn't need to make any user controls. WPF places much less emphasis on user controls, in general you only need them when you need to add very specific behavior not covered by the existing framework that needs to be duplicated. That's unlikely to be the case in a chess application.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • so the board would have to be an image which would be loaded in a canvas? in that cae all the coins would also be separate images that would have to be superimposed on the board image. Is that right? – Aadith Ramia Dec 13 '13 at 06:47
  • Yes, that's right. You can set the Canvas background to the image of the board and then the pieces would be separate Image elements on the Canvas. There are plenty of variants on this, for example each piece could be a Button and the background of the button is set to the bitmap for that piece. Not saying this is exactly how you would do it necessarily, WPF has a lot of flexibility in how you can do stuff like this, but in general you would start with a Canvas and go from there. – Mark Feldman Dec 13 '13 at 07:13
1

ComponentOne has a a Silverlight demo of this board game called checkers. You can view this demo online and the source code is available for free. Definitely, it can be a lot helpful for you to get started. Note that porting to WPF would be rather easy.

AjS
  • 341
  • 2
  • 13
1

1. Design the canvas and decide how much size you will give to the board or how much to the scoring area or players name or any other additional information you want to show during the game.

2. Design coin's images, and board background image

3. Now set up the scene, you should have an individual class for graphics. It would be easier to update it and handle it (Its up to you that how you write it).

4. Create a class for animation, that how an image will move on a particular action.

5. Create an individual class for sounds if you plan to use.

6. Create another class containing the game logic

7. One more class you will need for the player

After doing them, its all up to your logic that how you handle them all.

A link is here, its on vb.net but you can get an idea of designing the UI.

Shaharyar
  • 12,254
  • 4
  • 46
  • 66