You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PictureBox绘制矩形异常:旧矩形残留与越界绘制问题排查

问题修复方案

项目背景

涉及项目:Images-Crop

当前问题

设置clearRectangles = false测试时出现两个异常:

  1. 在PictureBox2的图像区域外尝试画矩形,已有的所有矩形会被删除,不符合clearRectangles=false时保留矩形的预期;
  2. 越界绘制后再回到图像区域画矩形,之前的无效越界矩形也会被显示出来。

需求目标

  • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 13:05:24