构建国际象棋应用程序所需的 WPF 控件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20560519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 10:12:01  来源:igfitidea点击:

WPF controls needed to build chess application

c#wpf

提问by Aadith Ramia

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.

我正在尝试构建一个国际象棋应用程序。我有后端逻辑(几乎)。但我在 UI 方面工作不多。我打算使用 C#,我听说 WPF 是要走的路。

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?

能否请您指点一下如何构建 UI 界面和在其上的各种硬币?我是否必须为硬币建立某种控制?另外,我应该使用什么控件来开发板?

回答by Mark Feldman

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.

我将在这个问题上再试一次,并实际向您展示如何使用 WPF 正确地做到这一点。不过请注意,如果您之前从未做过任何 WPF,那么一开始可能会有点不知所措,但希望它应该能让您了解数据驱动的 WPF 是如何以及一旦掌握了它的强大功能它。

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:

首先,您需要创建一个 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); }
    }
}

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:

从这里开始,几乎所有其他事情都是在 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>

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:

接下来,您需要一种方法来根据您正在渲染的作品选择图像。有很多方法可以做到这一点,但我在这里要做的方法是声明一个图像样式,然后使用触发器根据棋子类型和播放器选择适当的位图。对于这个例子,我将只是热链接到 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>

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(即项目列表),我们将容器设置为画布,我们将它的背景设置为我们的棋盘和对于每件作品,我们将根据 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>

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:

就是这样!我们现在拥有渲染国际象棋棋盘所需的一切。剩下的就是创建一个我们的棋子数组,把它放在一个 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}
        };

And here's the result:

结果如下:

WPF chessboard

WPF棋盘

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.

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

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

如果您需要任何进一步的说明和/或希望看到一个独立的项目,请务必让我知道。

回答by Mark Feldman

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.

如果它是 2D,那么 Canvas 是显而易见的方法,您可以将图像或其他任何您喜欢的东西放在上面,然后将它们移动到任意位置。您还可以将其包装在 Viewbox 中,以便它以及其中的所有内容自动缩放到父窗口。

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.

一般来说,您不需要进行任何用户控件。WPF 对用户控件的重视程度要低得多,一般来说,只有在需要添加需要复制的现有框架未涵盖的非常具体的行为时才需要它们。在国际象棋应用程序中不太可能出现这种情况。

回答by AjS

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.

ComponentOne 有一个名为跳棋的棋盘游戏的 Silverlight 演示。您可以在线查看此演示,并且源代码是免费提供的。当然,它可以对您入门很有帮助。请注意,移植到 WPF 会相当容易。

回答by Shaharyar

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

1.设计画布并决定棋盘的大小或得分区域球员姓名的大小或您希望在游戏过程中显示的任何其他附加信息

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

2.设计硬币的图像,以及板背景图像

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

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

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

4.为动画创建一个类,了解图像在特定动作上的移动方式。

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

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

6.Create another class containing the game logic

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

7.One more class you will need for the player

7.玩家还需要一门课

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

完成它们之后,这完全取决于您如何处理它们的逻辑。

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

这里有一个链接,它在vb.net 上,但您可以了解设计 UI。