如何在PictureBox中通过左键点击选中Flower对象
实现PictureBox中Flower对象的点击选中功能
要搞定PictureBox里点击选中Flower的功能,核心就是给每个Flower加点碰撞检测逻辑,然后在鼠标点击时遍历所有花,判断点击点是否落在花的范围内。
步骤1:给Flower类添加碰撞检测方法
在Flower类里新增IsPointInside(Point clickPoint)方法,根据花瓣形状(圆形/多边形)判断点击点是否在花的区域内:
public bool IsPointInside(Point clickPoint) { // 先检测中心圆(最上层,优先判断) float innerRadius = _outerRadius * _innerPart; float dx = clickPoint.X - _centreX; float dy = clickPoint.Y - _centreY; if (dx * dx + dy * dy <= innerRadius * innerRadius) { return true; } // 检测花瓣区域 if (_petalsRound) { // 圆形花瓣:将点击点反向旋转到初始花瓣的坐标系 float anglePerPetal = 360.0f / _numberOfPetals; Matrix inverseMatrix = new Matrix(); // 计算点击点相对中心的角度 float clickAngle = (float)Math.Atan2(dy, dx) * (180 / Math.PI); if (clickAngle < 0) clickAngle += 360; // 找到对应花瓣,计算反向旋转角度 int petalIndex = (int)(clickAngle / anglePerPetal); float rotateBackAngle = -petalIndex * anglePerPetal; inverseMatrix.RotateAt(rotateBackAngle, new PointF(_centreX, _centreY)); PointF[] transformedPoint = new PointF[] { new PointF(clickPoint.X, clickPoint.Y) }; inverseMatrix.TransformPoints(transformedPoint); // 初始花瓣的椭圆区域 float x0 = _centreX - _petalWidth * _outerRadius; float y0 = _centreY - _outerRadius; float width = 2.0f * _petalWidth * _outerRadius; float height = _outerRadius; // 判断转换后的点是否在椭圆内 PointF pt = transformedPoint[0]; float ellipseDx = (pt.X - (x0 + width / 2)) / (width / 2); float ellipseDy = (pt.Y - (y0 + height / 2)) / (height / 2); return ellipseDx * ellipseDx + ellipseDy * ellipseDy <= 1; } else { // 多边形花瓣:将点击点反向旋转到初始三角形花瓣的坐标系 float anglePerPetal = 360.0f / _numberOfPetals; Matrix inverseMatrix = new Matrix(); float clickAngle = (float)Math.Atan2(dy, dx) * (180 / Math.PI); if (clickAngle < 0) clickAngle += 360; int petalIndex = (int)(clickAngle / anglePerPetal); float rotateBackAngle = -petalIndex * anglePerPetal; inverseMatrix.RotateAt(rotateBackAngle, new PointF(_centreX, _centreY)); PointF[] transformedPoint = new PointF[] { new PointF(clickPoint.X, clickPoint.Y) }; inverseMatrix.TransformPoints(transformedPoint); // 初始花瓣的三角形顶点 PointF centre = new PointF(_centreX, _centreY); float x0 = _centreX - _petalWidth * _outerRadius; float y0 = _centreY - _outerRadius; float width = 2.0f * _petalWidth * _outerRadius; PointF[] polygon = new PointF[] { centre, new PointF(x0 + width, y0), new PointF(x0, y0) }; // 判断点是否在三角形内 return PointInPolygon(transformedPoint[0], polygon); } } // 辅助方法:点是否在多边形内(此处适配三角形) private bool PointInPolygon(PointF point, PointF[] polygon) { bool inside = false; int n = polygon.Length; for (int i = 0, j = n - 1; i < n; j = i++) { if (((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y)) && (point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) + polygon[i].X)) { inside = !inside; } } return inside; }
步骤2:修改MouseClick事件处理逻辑
在PictureBox的MouseClick事件里,倒序遍历_gardenList(后绘制的花在最上层,优先选中),调用每个Flower的IsPointInside方法找到被点击的花:
// 定义变量存储选中的花 private Flower _selectedFlower = null; private void pictureBox1_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { // 倒序遍历,确保选中最上层的花 for (int i = _gardenList.Count - 1; i >= 0; i--) { if (_gardenList[i].IsPointInside(e.Location)) { _selectedFlower = _gardenList[i]; // 触发重绘,可用于显示选中状态 pictureBox1.Invalidate(); return; } } // 未点击到任何花,清空选中状态 _selectedFlower = null; pictureBox1.Invalidate(); } }
步骤3:可选:添加选中高亮效果
在Flower的Draw方法里,添加选中状态的绘制逻辑,比如给选中的花加红色边框:
public void Draw(Graphics paper) { // 原有的绘制花瓣、中心圆的代码... // 如果当前花被选中,绘制高亮边框 if (this == _selectedFlower) { using (Pen highlightPen = new Pen(Color.Red, 2)) { // 绘制包围整个花的圆形边框 paper.DrawEllipse(highlightPen, _centreX - _outerRadius, _centreY - _outerRadius, _outerRadius * 2, _outerRadius * 2); } } }
如果不想让Flower依赖外部的_selectedFlower变量,可以给Flower类添加bool IsSelected属性,选中时设置该属性为true,其他花设为false,Draw方法里根据这个属性判断是否高亮。
内容的提问来源于stack exchange,提问作者Lincoln Simms
相关产品推荐
相关产品推荐

