如何使用C#在跳棋棋盘上绘制圆形棋子并解决绘制不显示问题
问题原因
你绘制的棋子不显示是两个核心问题导致的:
CreateGraphics生成的绘制内容是临时的,只要控件触发重绘(比如窗体加载完成后首次渲染、窗口大小变动、被其他窗口遮挡后恢复)就会被完全清空- 你在将Panel加入窗体控件集合之前就调用了绘制操作,此时控件尚未完成初始化,绘制内容不会生效
修复后的可运行代码
using System; using System.Drawing; using System.Windows.Forms; namespace Checkers { public partial class Form1 : Form { private Panel[,] _chessBoardPanels; // 定义棋子颜色常量 private readonly Brush _blackPieceBrush = Brushes.Black; private readonly Brush _redPieceBrush = Brushes.Red; private readonly Pen _pieceBorderPen = new Pen(Color.White, 2); private const int tileSize = 100; private const int gridSize = 8; public Form1() { InitializeComponent(); // 调整窗体大小适配棋盘 this.ClientSize = new Size(tileSize * gridSize, tileSize * gridSize); this.FormBorderStyle = FormBorderStyle.FixedSingle; this.MaximizeBox = false; } public static void DrawCircle(Graphics g, Pen pen, float centerX, float centerY, float radius) { g.DrawEllipse(pen, centerX - radius, centerY - radius, radius * 2, radius * 2); } public static void FillCircle(Graphics g, Brush b, float centerX, float centerY, float radius) { g.FillEllipse(b, centerX - radius, centerY - radius, radius * 2, radius * 2); } private void Form1_Load(object sender, EventArgs e) { var clr1 = Color.DarkGray; var clr2 = Color.White; _chessBoardPanels = new Panel[gridSize, gridSize]; for (int n = 0; n < gridSize; n++) { for (int m = 0; m < gridSize; m++) { var newPanel = new Panel { Size = new Size(tileSize, tileSize), Location = new Point(tileSize * n, tileSize * m), Tag = new Point(n, m) // 存储当前格子坐标,后续移动棋子用 }; // 绑定Paint事件,所有绘制逻辑放这里 newPanel.Paint += (s, pe) => { Point pos = (Point)((Panel)s).Tag; int x = pos.X; int y = pos.Y; // 跳棋规则:仅在深色格子上放置棋子,前三行放黑棋,后三行放红棋 if ((x + y) % 2 == 1) { float cx = tileSize / 2f; float cy = tileSize / 2f; float radius = cx * 0.4f; // 留边避免贴格子边缘 if (y < 3) { FillCircle(pe.Graphics, _blackPieceBrush, cx, cy, radius); DrawCircle(pe.Graphics, _pieceBorderPen, cx, cy, radius); } else if (y > 4) { FillCircle(pe.Graphics, _redPieceBrush, cx, cy, radius); DrawCircle(pe.Graphics, _pieceBorderPen, cx, cy, radius); } } }; Controls.Add(newPanel); _chessBoardPanels[n, m] = newPanel; // 设置背景色 newPanel.BackColor = (n + m) % 2 == 0 ? clr2 : clr1; } } } } }
面向对象实现思路
新增CheckersPiece类封装棋子属性和行为,降低界面逻辑和业务逻辑的耦合:
public class CheckersPiece { // 棋子所在格子坐标 public int Row { get; set; } public int Column { get; set; } // 棋子颜色 public Color PieceColor { get; set; } // 是否是王棋 public bool IsKing { get; set; } // 移动方法,返回移动是否合法 public bool Move(int targetRow, int targetColumn, Panel[,] board) { // 这里补充移动规则判断逻辑,比如是否走斜向、是否跳吃、是否越界等 if (Math.Abs(targetRow - Row) == 1 && Math.Abs(targetColumn - Column) == 1) { // 普通移动逻辑 Row = targetRow; Column = targetColumn; return true; } // 跳吃逻辑自行补充 return false; } // 渲染方法,将棋子绘制到对应格子 public void Draw(Graphics g, int tileSize) { float cx = tileSize / 2f; float cy = tileSize / 2f; float radius = cx * 0.4f; using (Brush brush = new SolidBrush(PieceColor)) { g.FillEllipse(brush, cx - radius, cy - radius, radius * 2, radius * 2); } using (Pen pen = new Pen(Color.White, 2)) { g.DrawEllipse(pen, cx - radius, cy - radius, radius * 2, radius * 2); } // 王棋额外加标记 if (IsKing) { g.DrawString("王", SystemFonts.DefaultFont, Brushes.Gold, new PointF(cx - 10, cy - 10)); } } }
后续你只需要在Form中维护一个CheckersPiece[,]数组存储所有棋子对象,棋子状态变化后调用对应Panel的Invalidate()方法触发重绘即可。
内容的提问来源于stack exchange,提问作者user16612111
相关产品推荐
相关产品推荐

