PictureBox绘制矩形异常:旧矩形残留与越界绘制问题排查
问题修复方案
项目背景
涉及项目:Images-Crop
当前问题
设置clearRectangles = false测试时出现两个异常:
- 在PictureBox2的图像区域外尝试画矩形,已有的所有矩形会被删除,不符合
clearRectangles=false时保留矩形的预期; - 越界绘制后再回到图像区域画矩形,之前的无效越界矩形也会被显示出来。
需求目标
clearRectangles=false:禁止在图像区域外绘制,所有已画矩形永久保留;clearRectangles=true:每次绘制完成后自动删除矩形,越界绘制的无效矩形直接丢弃,后续绘图不显示旧矩形。
代码修改细节
1. MouseDown事件:限制仅在图像区域内启动绘制
只有当点击位置在图像范围内时,才添加新的绘制矩形,从源头避免越界绘制的无效矩形。
private void pictureBox2_MouseDown(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; if (pictureBox2.Image != null && selectedPath != null) { // 检查点击位置是否在图像区域内 if (e.X >= 0 && e.X <= pictureBox2.Image.Width && e.Y >= 0 && e.Y <= pictureBox2.Image.Height) { DrawingRects.Add(new DrawingRectangle() { Location = e.Location, Size = Size.Empty, StartPosition = e.Location, Owner = (Control)sender, DrawingcColor = SelectedColor }); } } }
2. MouseMove事件:仅在图像区域内更新矩形
调整鼠标位置的判断范围为图像尺寸,而非控件尺寸,同时确保x、y变量始终在图像范围内:
private void pictureBox2_MouseMove(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0) { // 限制鼠标坐标在图像范围内 int X = Math.Clamp(e.X, 0, pictureBox2.Image.Width); int Y = Math.Clamp(e.Y, 0, pictureBox2.Image.Height); x = X; y = Y; var dr = DrawingRects[DrawingRects.Count - 1]; // 调整矩形位置,确保起始点和当前点都在图像内 Point newLoc = dr.StartPosition; if (X < dr.StartPosition.X) newLoc.X = X; if (Y < dr.StartPosition.Y) newLoc.Y = Y; dr.Location = newLoc; dr.Size = new Size(Math.Abs(dr.StartPosition.X - X), Math.Abs(dr.StartPosition.Y - Y)); pictureBox2.Invalidate(); } }
3. MouseUp事件:校验矩形有效性,按设置处理清除逻辑
- 检查最后一个矩形是否有效(尺寸大于0且完全在图像内),无效则移除;
- 把
clearRectangles的清除逻辑移到这里,避免Paint事件触发时的异常清除:
private void pictureBox2_MouseUp(object sender, MouseEventArgs e) { if (e.Button != MouseButtons.Left) return; if (DrawingRects.Count > 0 && pictureBox2.Image != null && selectedPath != null) { var dr = DrawingRects.Last(); // 校验矩形是否完全在图像范围内且尺寸有效 bool isRectValid = dr.Rect.Width > 0 && dr.Rect.Height > 0 && dr.Rect.Right <= pictureBox2.Image.Width && dr.Rect.Bottom <= pictureBox2.Image.Height && dr.Rect.X >= 0 && dr.Rect.Y >= 0; if (isRectValid) { rectImage = cropAtRect((Bitmap)pictureBox2.Image, dr.Rect); if (saveRectangles) { rectangleName = GetNextName(Path.Combine(selectedPath, "Rectangle"), ".bmp"); FileList.Add($"{dr.Location}, {dr.Size}", rectangleName); string json = JsonConvert.SerializeObject(FileList, Formatting.Indented); using (StreamWriter sw = new StreamWriter(Path.Combine(selectedPath, "rectangles.txt"), false)) { sw.Write(json); } rectImage.Save(rectangleName); saveRectanglesCounter++; } pixelsCounter = dr.Rect.Width * dr.Rect.Height; pictureBox1.Invalidate(); listBox1.DataSource = FileList.ToList(); listBox1.SelectedIndex = listBox1.Items.Count - 1; } else { // 移除无效的越界矩形 DrawingRects.Remove(dr); pictureBox2.Invalidate(); } // 按设置清除矩形,仅在有效绘制完成后执行 if (clearRectangles && isRectValid) { DrawingRects.Clear(); pictureBox2.Invalidate(); } pictureBox2.Focus(); if (pictureBox1.Image != null) { using (Graphics g = Graphics.FromImage(pictureBox1.Image)) { g.Clear(pictureBox1.BackColor); } } } }
4. PictureBox2 Paint事件:简化绘制逻辑
去掉对x、y的额外判断,直接绘制所有有效的矩形:
private void pictureBox2_Paint(object sender, PaintEventArgs e) { ControlPaint.DrawBorder(e.Graphics, pictureBox2.ClientRectangle, Color.Red, ButtonBorderStyle.Solid); if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0) { DrawShapes(e.Graphics); } }
5. PictureBox1 Paint事件:调整清除逻辑
移除在Paint事件中清除DrawingRects的代码,避免被动触发清除:
private void pictureBox1_Paint(object sender, PaintEventArgs e) { if (drawBorder) { ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, Color.Red, ButtonBorderStyle.Solid); } if (rectImage != null && DrawingRects.Count > 0) { var dr = DrawingRects.Last(); e.Graphics.DrawImage(rectImage, dr.Rect); } }
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

