C# GDI+ 在窗体与 Chart 控件上实现鼠标绘制矩形

夏知夏
夏知夏
发布时间:2026-10-14 | 2人浏览过
原创

本文讲解如何在 C# WinForms 应用程序中利用 GDI+ 实现实时鼠标绘图。通过捕获 MouseDown、MouseMove 和 MouseUp 事件,演示了两种典型场景:一是在主窗体背景上绘制动态矩形,二是在 System.Windows.Forms.DataVisualization.Charting.Chart 控件的数据绘图区覆盖绘制矩形。重点解析了 Graphics 对象的获取时机、坐标转换逻辑以及在 Chart 控件上绘图时需调用 Refresh() 而非 Clear() 的原因及具体代码实现。

结果或目标

实现一个基于 C# Windows Forms 的交互功能:用户按住鼠标左键并拖动时,系统会在屏幕上(窗体空白处或图表区域)实时跟随鼠标显示一个红色矩形框;松开鼠标后,矩形框停止跟随,状态归位。该功能常用于可视化选区、裁剪预览或图表测量。

准备条件

  1. 开发环境:Visual Studio,项目类型为 Windows Forms App (.NET Framework 或 .NET Core / .NET 5+)。
  2. 依赖命名空间:System.Drawing(核心绘图对象)、System.Windows.Forms(UI控件)、System.Windows.Forms.DataVisualization(Chart 控件,若在窗体中引入)。
  3. 关键控件:默认 Form 窗体及 Chart 控件实例(需手动添加引用并拖入设计器)。

真实操作顺序

第1步:初始化全局变量与图形上下文

目的:提前准备好用于存储绘图坐标的状态变量,并在窗口加载前获取可用于绘制的 Graphics 句柄。

入口:窗体构造函数 (InitializeComponent之后)。

动作与参数:

  • 定义全局布尔值 begin 标记是否正在绘制。
  • 定义两个 Point 结构体 firstPoint(起点)和 secondPoint(终点)。
  • 调用 this.CreateGraphics() 获取窗体本身的图形上下文 g。
  • 调用 this.chart1.CreateGraphics() 获取 Chart 控件的图形上下文 g2。

完成状态:类内包含可供事件处理器共享的图形对象与状态标识。

public partial class DataForm : Form
{
    // 状态控制
    public bool begin = false;
    public Point firstPoint = new Point(0, 0);
    public Point secondPoint = new Point(0, 0);
    
    // 图形上下文
    private Graphics g;      // 对应窗体
    private Graphics g2;     // 对应 chart 控件

    public DataForm()
    {
        InitializeComponent();
        // 必须在初始化组件后获取 Graphics 对象
        g = this.CreateGraphics();
        // 确保 chart1 已在设计器中放置
        if (this.chart1 != null)
        {
            g2 = this.chart1.CreateGraphics();
        }
    }
}

第2步:实现窗体上的实时绘制(MouseDown/Move/Up)

目的:处理用户在窗体非 Chart 区域的点击与拖动,绘制覆盖层矩形。

入口:绑定到窗体的 MouseDown, MouseMove, MouseUp 事件。

动作与逻辑:

  1. data_MouseDown:将 begin 设为 true,记录当前点击位置为 firstPoint。
  2. data_MouseMove:
    - 若 begin 为假则忽略。
    - 使用 g.Clear(this.BackColor) 清除之前的画面(重绘)。这一步模拟了“刷新”。
    - 更新 secondPoint 为当前鼠标位置。
    - 计算矩形的左上角 (minX, minY) 和右下角 (maxX, maxY) 坐标,使用 Math.Min/Max 兼容任意方向的拖动。
    - 调用 g.DrawRectangle(new Pen(Color.Red), ...) 画出红色矩形。
  3. data_MouseUp:将 begin 设为 false,结束绘制循环。
// 窗体事件处理示例
private void DataForm_MouseDown(object sender, MouseEventArgs e)
{
    begin = true;
    firstPoint = new Point(e.X, e.Y);
}

private void DataForm_MouseMove(object sender, MouseEventArgs e)
{
    if (begin)
    {
        // 1. 清除并重画窗体背景
        g.Clear(this.BackColor);
        
        // 2. 更新终点
        secondPoint = new Point(e.X, e.Y);
        
        // 3. 规范化坐标
        int minX = Math.Min(firstPoint.X, secondPoint.X);
        int minY = Math.Min(firstPoint.Y, secondPoint.Y);
        int width = Math.Abs(secondPoint.X - firstPoint.X);
        int height = Math.Abs(secondPoint.Y - firstPoint.Y);

        // 4. 绘制矩形
        g.DrawRectangle(new Pen(Color.Red, 2), minX, minY, width, height);
    }
}

private void DataForm_MouseUp(object sender, MouseEventArgs e)
{
    begin = false;
}

第3步:实现 Chart 控件上的特殊绘制

目的:在图表内部进行视觉标注。**注意**:Chart 控件的渲染机制与普通窗体不同,直接 Clear 会破坏图表数据区的完整性。

入口:绑定到 chart1 控件的事件。

关键差异:

  • 不能用 g2.Clear(),否则会擦除整个图表的背景和数据序列。
  • 应使用 this.Refresh() 强制控件重绘(或使用双缓冲技术以避免闪烁)。

动作与逻辑:

  1. chart1_MouseDown:开启绘制状态,记录起点。
  2. chart1_MouseMove:
    - 调用 this.Refresh() 触发重绘以消除前一个帧的痕迹。
    - 重新计算坐标,调用 g2.DrawRectangle 绘制新的矩形。
  3. chart1_MouseUp:关闭状态。
// Chart 控件事件处理示例
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
    begin = true;
    firstPoint = new Point(e.X, e.Y); // 注意:e.X/e.Y 是相对于 ClientArea 的坐标
}

private void chart1_MouseMove(object sender, MouseEventArgs e)
{
    if (begin)
    {
        // 必须使用 Refresh 而不是 g2.Clear,以免破坏图表内部结构
        this.Refresh();
        
        secondPoint = new Point(e.X, e.Y);
        
        int minX = Math.Min(firstPoint.X, secondPoint.X);
        int minY = Math.Min(firstPoint.Y, secondPoint.Y);
        int width = Math.Abs(secondPoint.X - firstPoint.X);
        int height = Math.Abs(secondPoint.Y - firstPoint.Y);

        g2.DrawRectangle(new Pen(Color.Red, 2), minX, minY, width, height);
    }
}

private void chart1_MouseUp(object sender, MouseEventArgs e)
{
    begin = false;
}

完成判断

编译运行程序后:
1. 在窗体任意空白处按下鼠标拖动,应能看到红色方框实时跟随鼠标移动且背景正常刷白。
2. 在 Chart 控件区域内按下鼠标拖动,红色方框同样跟随移动,且下方的图表曲线和数据标签不消失、不被擦除。

原文有依据的排错

  • 闪烁问题:上述基础实现使用了简单的 Clear/Draw 或 Refresh,在高 DPI 或复杂图表下可能出现闪烁。进阶方案需启用窗体双缓冲 (DoubleBuffered = true) 或使用 Invalidate() 替换 Refresh。
  • 坐标偏移:如果在容器内(Panel等)绘图,e.X 可能需要进行偏移调整,但在纯窗体或 Chart 客户区通常直接使用。

简要总结

利用 C# GDI+ 进行实时绘图的核心在于掌握 Graphics 对象的生命周期管理以及响应鼠标事件的坐标计算逻辑。对于普通控件,简单的清屏重绘即可满足需求;而对于 Chart 等具有内部渲染机制的复合控件,务必避免直接调用 Clear 破坏其子元素,应采用控件级别的刷新策略。以上就是 C# GDI+ 在窗体和 Chart 控件上进行鼠标绘图操作的详细内容,更多关于 C# GDI+ 绘图技巧的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表