我正在尝试构建一个象棋应用程序。我已经准备好后端逻辑(几乎完成)。但是我在用户界面方面工作得不多。我计划使用C#,我听说WPF是正确的选择。
请问您如何构建UI界面和上面的各种棋子?我需要为棋子构建一些控件吗?另外,我应该使用什么控件来开发棋盘?
我将重新尝试这个问题,并向您展示如何使用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}
};
以下是结果:
这似乎需要很多工作才能画出一个棋盘,但请记住,这现在是完全数据驱动的界面...如果您添加或删除棋子或更改棋子数组中的任何字段,则这些更改将立即传播到前端。它还非常容易扩展、修改和添加其他功能,例如动画、3D、反射等。但也许最令人印象深刻的是,我根本不需要创建任何自定义用户控件就能做到这一点,WPF数据绑定机制足够强大,可以轻松支持这种东西。
如果您需要进一步的澄清和/或想看到一个独立的项目,请随时让我知道。
1. 设计画布并决定您将为游戏板,得分区域,玩家名称或任何其他您想在游戏过程中显示的附加信息分配多少空间。
2. 设计硬币图像和游戏板背景图像。
3. 现在设置场景,您应该有一个用于图形的单独类。这样更容易更新和处理它(如何编写取决于您)。
4. 创建一个动画类,以确定图像在特定操作时如何移动。
5. 如果您打算使用声音,请创建一个单独的声音类。
6. 创建另一个包含游戏逻辑的类。
7. 您还需要一个类来处理玩家。
完成以上步骤后,如何处理所有内容就取决于您的逻辑。
一个链接在这里, 它是关于vb.net的,但是你可以了解UI设计的想法。