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

如何更新Graphics.DrawLine()的点位,实现可移动可缩放的矩形绘制?

解决方案:实现可移动、可调整大小的矩形

首先直接回答你的核心问题:不需要手动每帧清除图形,Windows Forms的绘制机制会自动处理背景清除;更新线条位置的关键是把矩形的状态(中点、宽高)变成可修改的类成员,在状态变化时触发重绘即可。

核心思路

  1. 提取状态为类成员:把硬编码的box_Middle、boxWidth、boxHeight改成Form的私有字段,这样在鼠标操作等场景下可以随时修改这些值。
  2. 触发重绘而非手动清屏:每次修改矩形状态后,调用Invalidate()方法,系统会自动标记窗体需要重绘,并在合适时机触发Paint事件,此时你只需要重新计算所有顶点并绘制线条即可。系统会自动清除旧的绘制内容,不需要手动调用Clear()之类的方法(如果有闪烁问题,可以开启双缓冲优化)。
  3. 添加交互逻辑:通过鼠标事件(MouseDown/MouseMove/MouseUp)实现拖动移动和调整边长的功能。

修改后的完整代码示例

首先在Form类中添加私有字段存储矩形状态和交互标记:

public partial class Form1 : Form
{
    // 矩形状态:中点、宽高
    private Point _boxMiddle = new Point(300, 300);
    private int _boxWidth = 100;
    private int _boxHeight = 200;
    
    // 交互标记:是否正在拖动、调整宽度/高度
    private bool _isDragging = false;
    private bool _isResizingWidth = false;
    private bool _isResizingHeight = false;
    private Point _lastMousePos;

    public Form1()
    {
        InitializeComponent();
        // 开启双缓冲解决闪烁问题
        DoubleBuffered = true;
    }

然后修改Paint事件,使用类成员变量绘制矩形,同时添加可视化的控制点(方便用户操作):

private void Form1_Paint(object sender, PaintEventArgs e)
{
    using (Pen boxPen = new Pen(Color.Red, 3))
    {
        DrawBox(e, boxPen, _boxMiddle, _boxWidth, _boxHeight);
    }

    // 绘制控制点:中点(拖动)、右边中点(调宽度)、下边中点(调高度)
    using (Pen controlPen = new Pen(Color.Blue, 5))
    {
        // 中点控制点
        e.Graphics.DrawEllipse(controlPen, _boxMiddle.X - 3, _boxMiddle.Y - 3, 6, 6);
        // 右边中点控制点
        Point rightMiddle = new Point(_boxMiddle.X + _boxWidth / 2, _boxMiddle.Y);
        e.Graphics.DrawEllipse(controlPen, rightMiddle.X - 3, rightMiddle.Y - 3, 6, 6);
        // 下边中点控制点
        Point bottomMiddle = new Point(_boxMiddle.X, _boxMiddle.Y + _boxHeight / 2);
        e.Graphics.DrawEllipse(controlPen, bottomMiddle.X - 3, bottomMiddle.Y - 3, 6, 6);
    }
}

void DrawBox(PaintEventArgs e, Pen pen, Point middle, int width, int height)
{
    var graphics = e.Graphics;
    graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
    
    // 重新计算四个顶点
    Point topLeft = new Point(middle.X - width / 2, middle.Y - height / 2);
    Point topRight = new Point(topLeft.X + width, topLeft.Y);
    Point bottomLeft = new Point(topLeft.X, topLeft.Y + height);
    Point bottomRight = new Point(topRight.X, bottomLeft.Y);
    
    // 绘制四条边
    graphics.DrawLine(pen, topLeft, topRight);
    graphics.DrawLine(pen, topLeft, bottomLeft);
    graphics.DrawLine(pen, bottomLeft, bottomRight);
    graphics.DrawLine(pen, topRight, bottomRight);
}

最后添加鼠标交互事件,实现拖动和调整大小:

private void Form1_MouseDown(object sender, MouseEventArgs e)
{
    _lastMousePos = e.Location;
    
    // 判断是否点击中点(拖动)
    if (Math.Abs(e.X - _boxMiddle.X) < 5 && Math.Abs(e.Y - _boxMiddle.Y) < 5)
    {
        _isDragging = true;
    }
    // 判断是否点击右边中点(调整宽度)
    else
    {
        Point rightMiddle = new Point(_boxMiddle.X + _boxWidth / 2, _boxMiddle.Y);
        if (Math.Abs(e.X - rightMiddle.X) < 5 && Math.Abs(e.Y - rightMiddle.Y) < 5)
        {
            _isResizingWidth = true;
        }
        // 判断是否点击下边中点(调整高度)
        else
        {
            Point bottomMiddle = new Point(_boxMiddle.X, _boxMiddle.Y + _boxHeight / 2);
            if (Math.Abs(e.X - bottomMiddle.X) < 5 && Math.Abs(e.Y - bottomMiddle.Y) < 5)
            {
                _isResizingHeight = true;
            }
        }
    }
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
    if (_isDragging)
    {
        // 计算鼠标移动距离,更新矩形中点
        int deltaX = e.X - _lastMousePos.X;
        int deltaY = e.Y - _lastMousePos.Y;
        _boxMiddle.X += deltaX;
        _boxMiddle.Y += deltaY;
        _lastMousePos = e.Location;
        Invalidate(); // 触发重绘
    }
    else if (_isResizingWidth)
    {
        // 保持中点不变,调整宽度:鼠标X方向移动的距离*2就是宽度变化量
        int deltaX = e.X - _lastMousePos.X;
        _boxWidth += 2 * deltaX;
        // 防止宽度变为负数
        _boxWidth = Math.Max(10, _boxWidth);
        _lastMousePos = e.Location;
        Invalidate();
    }
    else if (_isResizingHeight)
    {
        // 保持中点不变,调整高度:鼠标Y方向移动的距离*2就是高度变化量
        int deltaY = e.Y - _lastMousePos.Y;
        _boxHeight += 2 * deltaY;
        // 防止高度变为负数
        _boxHeight = Math.Max(10, _boxHeight);
        _lastMousePos = e.Location;
        Invalidate();
    }
}

private void Form1_MouseUp(object sender, MouseEventArgs e)
{
    // 结束所有交互状态
    _isDragging = false;
    _isResizingWidth = false;
    _isResizingHeight = false;
}

关键细节说明

  • 为什么不需要手动清屏?:当你调用Invalidate()时,Windows Forms会标记指定区域为“需要重绘”,在Paint事件触发时,系统会自动清除该区域的背景(除非你设置了ControlStyles.Opaque),然后执行你的绘制代码。
  • 双缓冲的作用:开启DoubleBuffered = true可以避免绘制时的闪烁问题,因为系统会先在内存中完成绘制,再一次性显示到屏幕上。
  • 控制点的设计:添加蓝色的小圆点作为控制点,让用户可以直观地知道哪里可以拖动或调整大小,你也可以根据需求扩展更多控制点(比如四个角)。

内容的提问来源于stack exchange,提问作者Brandon M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:42:01