需要使用WPF控件构建国际象棋应用程序。

5

我正在尝试构建一个象棋应用程序。我已经准备好后端逻辑(几乎完成)。但是我在用户界面方面工作得不多。我计划使用C#,我听说WPF是正确的选择。

请问您如何构建UI界面和上面的各种棋子?我需要为棋子构建一些控件吗?另外,我应该使用什么控件来开发棋盘?


1
看一下这个网站:http://wpftutorial.net/Graphics2D.html它是关于WPF控件和使用技巧的非常有用的网站。 - Shaharyar
“coin” 是象棋术语还是你的意思是“棋子”? - CodeCaster
4个回答

34

我将重新尝试这个问题,并向您展示如何使用WPF正确地进行操作。但请注意,如果您以前没有使用过任何WPF,则可能会有点压力,但希望一旦掌握它,就能了解数据驱动的WPF是多么强大。

首先,您需要创建一个WPF项目并运行NuGet软件包管理器来添加MVVM Light软件包(或手动添加,如果您喜欢)。接下来,您需要设置一些枚举来定义您的棋子类型,并设置一个类来表示棋盘上的实际棋子实例:

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); }
    }
}

从这里开始几乎所有东西都是用XAML完成的。首先,您需要为棋盘自身创建一个棋盘刷子,如果您愿意,这可以是位图,但我将继续创建一个几何绘图。此代码需要放置在Window.Resources部分中:

<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>

接下来,您需要一种基于正在渲染的图块选择图像的方法。有许多方法可以做到这一点,但我在这里要做的是声明一个图像样式,然后使用触发器根据图块类型和玩家选择适当的位图。对于这个示例,我将只是热链接到wpclipart网站上的一些剪贴画。这个XAML块很长,但是对于每个图块类型都是在做同样的事情:

<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>

现在是关于棋盘本身的部分。有了上面的代码设置,这一部分非常简短,我们只需渲染一个ItemsControl(即项目列表),将容器设置为画布,将其背景设置为我们的棋盘,并对每个棋子根据Pos属性设置位置。显然,我们还将使用上面设置的ChessPieceStyle Image样式来选择要呈现的正确图像:

<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>

就是这样!我们现在拥有了渲染棋盘所需的一切。剩下的只是创建一个棋子数组,将其放入ObservableCollection(这样GUI在添加和删除棋子时会得到更新)并绑定到我们的棋盘上:

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}
        };

以下是结果:

WPF棋盘

这似乎需要很多工作才能画出一个棋盘,但请记住,这现在是完全数据驱动的界面...如果您添加或删除棋子或更改棋子数组中的任何字段,则这些更改将立即传播到前端。它还非常容易扩展、修改和添加其他功能,例如动画、3D、反射等。但也许最令人印象深刻的是,我根本不需要创建任何自定义用户控件就能做到这一点,WPF数据绑定机制足够强大,可以轻松支持这种东西。

如果您需要进一步的澄清和/或想看到一个独立的项目,请随时让我知道。


哇,Mark!那是一个非常好的答案.. 我正试图跟随你的步骤.. 但我必须承认,您正在与一个 wpf 新手交流... 您能否详细说明在哪里添加所有这些 XML 内容?到目前为止,我已经添加了 MVVM 包,添加 CS 代码应该是没问题的... 我卡在了如何安排这些 XML 文件上。 - Aadith Ramia
2
@Aadith,棋盘和图像样式应该放在你的Windows资源块内(即<Window.Resources> ...此处... </Window.Resources>)。ItemsControl是内容,因此它替换了默认添加的<Grid> </ Grid>标记。也许最简单的方法就是给您指一个可以用作起点的项目 - Mark Feldman
这太棒了..非常感谢马克!你能告诉我添加移动金币功能需要什么吗?每当用户尝试移动金币时,都必须触发事件处理程序..顺便问一下,你会考虑和我合作这个项目吗? - Aadith Ramia
2
太棒了!非常有指导意义。有几个小细节(对于象棋玩家来说可能不是小事哈哈):将暗色和亮色方块反转,并更改国王和皇后的起始方块。 - LJ VanKuiken
这是一个很棒的答案,但我如何在单击方块时更改其颜色?就我所理解的,这里的棋盘只是一个几何图形绘制... - Liroshka
显示剩余2条评论

1
如果是2D,则Canvas是显而易见的选择,您可以在其上放置图像或其他任何喜欢的内容,并将它们移动到任意位置。您还可以将其包装在Viewbox中,使其及其中的所有内容自动缩放到父窗口。
通常情况下,您不需要创建任何用户控件。WPF对用户控件的重视要少得多,在一般情况下,只有在需要添加现有框架未覆盖的非常特定的行为并需要复制时才需要它们。这在象棋应用程序中不太可能发生。

所以,游戏板必须是一个图像,它将被加载到画布中?在这种情况下,所有的硬币也将是单独的图像,必须叠加在游戏板图像上。对吗? - Aadith Ramia
是的,没错。您可以将画布背景设置为棋盘图像,然后棋子将成为画布上的单独图像元素。有很多变体,例如每个棋子都可以是一个按钮,按钮的背景设置为该棋子的位图。并不是说这一定是您要做的方式,WPF在如何处理此类事情方面具有很大的灵活性,但通常您会从画布开始,并从那里继续。 - Mark Feldman

1
ComponentOne有一个名为跳棋的银光演示。您可以在线查看此演示,并且源代码可供免费使用。这对于您入门肯定非常有帮助。请注意,移植到WPF将相当容易。

1

1. 设计画布并决定您将为游戏板,得分区域,玩家名称或任何其他您想在游戏过程中显示的附加信息分配多少空间。

2. 设计硬币图像和游戏板背景图像。

3. 现在设置场景,您应该有一个用于图形的单独类。这样更容易更新和处理它(如何编写取决于您)。

4. 创建一个动画类,以确定图像在特定操作时如何移动。

5. 如果您打算使用声音,请创建一个单独的声音类。

6. 创建另一个包含游戏逻辑的类。

7. 您还需要一个类来处理玩家。

完成以上步骤后,如何处理所有内容就取决于您的逻辑。

一个链接在这里, 它是关于vb.net的,但是你可以了解UI设计的想法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接