【问题标题】:Persistent graphics WinForms持久图形 WinForms
【发布时间】:2013-02-20 16:59:26
【问题描述】:

我有一个 WinForms 应用程序,我必须在控件之间画一些线。这些行需要持久化,所以我覆盖了OnPaint() 事件的形式。

问题是,重新绘制的线条不是很流畅。

我正在按如下方式创建图形:

Graphics g;
g = this.CreateGraphics();
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
g.FillRectangle(Brushes.White, this.ClientRectangle);

然后画线如下:

public void lineDraw(Control L1, Control L2) {            
    using (Pen pen = new Pen(Color.Black, 4)) {
        pen.StartCap = System.Drawing.Drawing2D.LineCap.Flat;
        pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
        int x1, x2, y1, y2;
        //choose x/y coordinates
        g.DrawLine(pen, x1, y1, x2, y2);
    }
}

我可以设置任何属性来提高绘制图形的平滑度吗?

【问题讨论】:

    标签: c# winforms graphics persistent


    【解决方案1】:

    目标

    图像显示在控件(或窗体)上。

    失效

    任何时候控件(或窗体)被调整大小、最小化/最大化、部分遮挡或移动,它必须(部分)重绘。当这种情况发生时,必须重绘的控件部分被称为无效

    当控件无效时,会执行如下操作:

    1. 调用OnPaintBackground:这会用背景色填充无效区域。
    2. 调用OnPaint:这会在背景上绘制文本和图形。

    为什么OnPaint会导致闪烁

    您已经覆盖了控件的OnPaint method。每次重绘控件时,您都会看到控件的闪烁,其中仅绘制了其背景颜色。那是在调用OnPaintBackground 之后和调用OnPaint 之前。

    解决方案

    • 如果你有一个静态图像(即它永远不会改变):

      1. Load 事件中:创建一个新的Bitmap object
      2. 用背景色填充并在其上绘制线条和形状。
      3. 将此Bitmap 对象分配给控件的BackgroundImage property
    • 如果您有一个在控件调整大小时必须调整大小的静态图像:

      1. 覆盖OnResize method 并在其中创建新的Bitmap。使用控件的ClientSize property 作为Bitmap 的大小。
      2. 用背景色填充并在其上绘制线条和形状。
      3. 将此Bitmap 对象分配给控件的BackgroundImage 属性。
    • 如果您有动画图像:

      1. Load 事件中,将控件的DoubleBuffered property 设置为true。设置此项可防止您看到的闪烁,因为它使用不可见的缓冲区来绘制控件。
      2. 覆盖控件的OnPaint method。获取控件的Graphics context,直接在控件上绘制线条和形状。
      3. 创建并启用一个新的Timer object,并在其回调方法中调用控件的Invalidate method,后跟Update method (as shown here)。例如,将计时器设置为每 40 毫秒触发一次。

    【讨论】:

    • 我从来不知道它的存在! +1
    • 它不是块状的,但每次调用OnPaint() 事件时都会闪烁很多。例如,当我将其他控件移到线条上时。事实上这是有道理的,因为它必须在每次调用时重新绘制所有的行(可以是很多行,但对于少数行来说,方面是相同的)。 SmoothingMode.HighQuality 没有解决问题。 SmoothingMode.HighSpeed.
    • @Virtlink - 您可能还想建议使用计时器来延迟重绘,这样它就不会超过刷新率。
    • 没有一个解决方案具有良好的整体性能。当前的解决方案是将Graphics.DrawLine() 更改为LineShape 对象。
    • @guanabara:我无法想象我的解决方案 #1 的性能会很差。需要明确的是,它不使用OnPaint,它只在表单大小改变时设置一次图像。
    【解决方案2】:

    你可能不应该在这里使用CreateGraphics,更重要的是,不要使用局部变量来存储图形对象。使用从绘制事件中获取的图形对象,并根据需要使之失效。

    protected override void OnPaint(PaintEventArgs e) {
      e.Graphics.Clear(Color.White);
      e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    
      using (Pen pen = new Pen(Color.Black, 4)) {
        pen.StartCap = Drawing2D.LineCap.Flat;
        pen.EndCap = Drawing2D.LineCap.ArrowAnchor;
        int x1, x2, y1, y2;
        //choose x/y coordinates
        e.Graphics.DrawLine(pen, x1, y1, x2, y2);
      }
    
      base.OnPaint(e);
    }
    

    【讨论】:

      【解决方案3】:

      这是我的方式,它对我有用

      //FormMain.cs
      private const float DisplayRatio = 6;
      
      private Bitmap _bmpDisp; //use an in-memory bitmap to Persistent graphics
      private Graphics _grpDisp4Ctl;
      private Graphics _grpDisp4Bmp;
      private Point _ptOldDsp;
      
      
      private void FormMain_Shown(object sender, EventArgs e)
      {
          _grpDisp4Ctl = CreateGraphics();
          _grpDisp4Ctl.SetHighQulity();
      
          _bmpDisp = new Bitmap(ClientSize.Width, ClientSize.Height);
          _grpDisp4Bmp = Graphics.FromImage(_bmpDisp);
          _grpDisp4Bmp.SetHighQulity();
      
          _ptOldDsp = new Point(
              (int)((MousePosition.X - SystemInformation.VirtualScreen.Left) / DisplayRatio),
              (int)((MousePosition.Y - SystemInformation.VirtualScreen.Top) / DisplayRatio)
          );
      }
      
      private void UpdateDisplay(MouseHookEvent mhep) //your implement
      {        
          var ptNew = mhep.Position;
          ptNew.Offset(new Point(-SystemInformation.VirtualScreen.Left, -SystemInformation.VirtualScreen.Top));
          ptNew.X = (int)(ptNew.X / DisplayRatio);
          ptNew.Y = (int)(ptNew.Y / DisplayRatio);
      
          _grpDisp4Ctl.DrawLine(Pens.White, _ptOldDsp, ptNew); //draw smooth lines to mem and ui
          _grpDisp4Bmp.DrawLine(Pens.White, _ptOldDsp, ptNew);
      
          _ptOldDsp = ptNew;
      
      }
      
      private void FormMain_Paint(object sender, PaintEventArgs e)
      {
          // like vb6's auto redraw :)
          e.Graphics.DrawImage(_bmpDisp, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
      }
      
      //common.cs
      internal static void SetHighQulity(this Graphics g)
      {
          g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
          g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
          g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
          g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
          g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
      }
      

      【讨论】:

        【解决方案4】:

        我知道这是一篇较旧的帖子,但您也可以尝试将表单的 DoubleBuffered 属性设置为 TRUE,阅读以下内容:

        "缓冲图形可以减少或消除由以下原因引起的闪烁 逐步重绘显示表面的各个部分。缓冲的 图形要求首先将更新的图形数据写入 缓冲。然后将图形缓冲区中的数据快速写入 显示表面记忆。显示的切换比较快 图形内存通常会减少闪烁,否则 发生。”

        【讨论】:

          猜你喜欢
          • 2018-05-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-01-03
          • 2011-12-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多