在开发扑克相关的应用程序时,经常需要展示特定手牌的信息。为了满足这一需求,最近编写了一个可以在标准的13x13卡牌图表中展示手牌信息的控件。这个控件是用C#编写的.NETWindows Forms控件,使用起来非常简单,将在下面详细介绍如何使用它。
在德州扑克中,不同的起始手牌属性可以通过一个13x13的网格清晰且紧凑地展示出来,正如这个控件所实现的那样。顶部的图像显示了设计时的空白图表;底部的图像显示了每个口袋对被染成金色,并显示了'X'。大多数扑克玩家都熟悉这些网格;对于特定用法的例子,可以参考这里的人头均衡推/弃牌图表。
如果不将控件添加到工具箱中,向表单(或其他容器)添加图表是一个简单的三步过程!
接下来,将以下代码添加到表单的.Designer.cs文件中:
private PokerHandChartControl.PokerHandChart chart = null;
this.SuspendLayout();
// 1st and last line may already be there,
// so just place next 4 lines between this.chart = new PokerHandChartControl.PokerHandChart();
this.chart.Location = new System.Drawing.Point(10, 120);
this.chart.Size = new System.Drawing.Size(200, 200);
// (200, 100) wouldn't matter
this.Controls.Add(this.chart);
this.ResumeLayout(false);
请注意,控件的大小尺寸并不重要,因为控件会自动将自己变成一个正方形 - 现在可以在表单设计器中移动/调整控件的大小了。此外,添加此代码不会干扰将来在表单上放置其他“标准”控件。输入的代码会在调整控件大小时发生变化(并被添加),这只是为了让控件在表单上实例化。控件绘制了一个空的13x13网格,每行/列都标记了一张卡牌……一旦完成,切换到刚刚添加了PokerHandChart的表单的设计标签,会看到一个空的图表。现在可以移动和调整图表控件的大小,并注意到它始终保持正方形。
使用图表非常简单。网格中的每个单元格都暴露了属性,允许控制单元格的显示。每个单元格都有前景和背景颜色,以及'DisplayValue'(一个简单的字符串 - 但它应该适合单元格内!)还可以更改字体(注意,这个版本中只有名称,没有大小,也许以后会有)。此外,每个单元格都有一个'Selected'标签,用于在应用程序中选择手牌(例如,一个范围) - 至少标记用户或控件选择的手牌。列出卡牌的标题也有可配置的颜色和字体(同样,没有字体大小)。
单元格的字体大小对于标题行/列和每个单元格是一个固定的比例,因此会随着图表的整体大小进行缩放。每个单元格值在图表中水平和垂直居中显示。
通过一个GridCell类公开这些属性。一个觉得让这个图表控件特别容易使用的特性是索引器风格的访问器(只读)对于每个单元格。有三种不同的索引器方法可用。
最简单的是使用包含手牌的字符串,例如,要获取代表AKs的单元格,可以使用this["AKs"] - 还有什么比这更容易的!好吧,通常想要访问许多单元格,通常在循环中,这时第二个索引器非常有用。它接受三个参数,即两张牌(作为字符)和一个布尔值,用于指示是否为同花。所以再次,要访问AKs,会使用this['A', 'K', true]。显然,布尔值对于口袋对是被忽略的。同样,编码this['K', 'A', true]将返回与之前相同的GridCell对象,所以类'规范化'了手牌。所以对于特别本地访问许多单元格,一个this访问器结合一个循环(或两个)是最简单的方式。事实上,每个口袋对和同花手牌单元格都可以在几行代码中访问(数组/枚举'ranks'对此有帮助)。
foreach (char r1 in Hand.ranks)
foreach (char r2 in Hands.ranks)
if (Hands.indexFor(r1) >= Hands.indexFor(r2)) {
// So AA, AK but not KA,
// even though that would work it would
// visit most cells more than once, wasteful...
GridCell c = chart[r1, r2, suited];
c.DisplayValue = "11.5";
c.Foreground = Brushes.Blue;
c.CellFont = "Arial";
}
// Hands.indexFor(card) is a method to allow comparison of cards by rank.
最后,单元格可以通过标准数值坐标访问,AA是(0,0),AKs是(1, 0),等等。这是为了(半)完整性;还没有用过它。每个单元格都是在类内部自动创建和维护的。所有开发者需要做的是提供显示数据,如颜色,以及要放置在单元格中的一些文本或数字。
控件还导出了一个点击风格的事件,当用户点击其中一个单元格时触发。实际的GridCell对象被传递给任何订阅的事件侦听器。
chart.HandClickedEvent += HandClicked;
protected void HandClicked(object sender, GridCell hit_cell) { ... }
因此,控件既可以用于显示静态数据,也可以用于允许用户选择和选择手牌/范围。还有一些枚举的例子,例如所有口袋对和所有宽街,使得选择这些组变得非常容易:
foreach (string s in HandEnumerations.PocketPairs)
GridCell c = chart[s];
// Do something to each of these cells...