导致画面闪烁的关键原因分析:
      一、绘制窗口由于大小位置状态改变进行重绘操作时,绘图窗口内容或大小每改变一次,都要调用Paint事件进行重绘操作,该操作会使画面重新刷新一次以维持窗口正常显示。刷新过程中会导致所有图元重新绘制,而各个图元的重绘操作并不会导致Paint事件发生,因此窗口的每一次刷新只会调用Paint事件一次。窗口刷新一次的过程中,每一个图元的重绘都会立即显示到窗口,因此整个窗口中,只要是图元所在的位置,都在刷新,而刷新的时间是有差别的,闪烁现象自然会出现。所以说,此时导致窗口闪烁现象的关键因素并不在于Paint事件调用的次数多少,而在于各个图元的重绘。
     根据以上分析可知,当图元数目不多时,窗口刷新的位置也不多,窗口闪烁效果并不严重;当图元数目较多时,绘图窗口进行重绘的图元数量增加,绘图窗口每一次刷新都会导致较多的图元重新绘制,窗口的较多位置都在刷新,闪烁现象自然就会越来越严重。特别是图元比较大绘制时间比较长时,闪烁问题会更加严重,因为时间延迟会更长。
     解决上述问题的关键在于:窗口刷新一次的过程中,让所有图元同时显示到窗口。
      二、进行鼠标跟踪绘制操作或者对图元进行变形操作时,当进行鼠标跟踪绘制操作或者对图元进行变形操作时,Paint事件会频繁发生,这会使窗口的刷新次数大大增加。虽然窗口刷新一次的过程中所有图元同时显示到窗口,但也会有时间延迟,因为此时窗口刷新的时间间隔远小于图元每一次显示到窗口所用的时间。因此闪烁现象并不能完全消除!所以说,此时导致窗口闪烁现象的关键因素在于Paint事件发生的次数多少。
      解决此问题的关键在于:设置窗体或控件的几个关键属性。
 
解决双缓冲的关键技术:
1、设置显示图元控件的几个属性: 必须要设置,否则效果不是很明显!
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw |ControlStyles.AllPaintingInWmPaint, true);
2、窗口刷新一次的过程中,让所有图元同时显示到窗口。
    可以通过以下几种方式实现,这几种方式都涉及到Graphics对象的创建方式。
Graphics对象的创建方式:
a、在内存上创建一块和显示控件相同大小的画布,在这块画布上创建Graphics对象。接着所有的图元都在这块画布上绘制,绘制完成以后再使用该画布覆盖显示控件的背景,从而达到“显示一次仅刷新一次”的效果!
实现代码(在OnPaint方法中):
  Rectangle rect = e.ClipRectangle;
  Bitmap bufferimage = new Bitmap(this.Width, this.Height);
      Graphics g = Graphics.FromImage(bufferimage);
  g.Clear(this.BackColor);
      g.SmoothingMode = SmoothingMode.HighQuality; //高质量
      g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移质量
  foreach (IShape drawobject in doc.drawObjectList)
       {
                  if (rect.IntersectsWith(drawobject.Rect))
                {
                    drawobject.Draw(g);
                    if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
                        && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
                    {
                        drawobject.DrawTracker(g);
                     }
                }

}

    using (Graphics tg = e.Graphics)
            {
                tg.DrawImage(bufferimage, 0, 0);  //把画布贴到画面上
            }
b、直接在内存上创建Graphics对象:
     Rectangle rect = e.ClipRectangle;
     BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;
            BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);
            Graphics g = myBuffer.Graphics;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            g.Clear(this.BackColor);
            foreach (IShape drawobject in doc.drawObjectList)
            {
                if (rect.IntersectsWith(drawobject.Rect))
                {
                    drawobject.Draw(g);
                    if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
                        && this.CurrentOperator == Enum.Operator.Transfrom)//仅当编辑节点操作时显示图元热点
                    {
                        drawobject.DrawTracker(g);
                     }
                }
            }
    myBuffer.Render(e.Graphics);
            g.Dispose();
            myBuffer.Dispose();//释放资源
至此,双缓冲问题解决,两种方式的实现效果都一样,但最后一种方式的占有的内存很少,不会出现内存泄露!

或者:

      1、在内存中建立一块“虚拟画布”:

Bitmap bmp = new Bitmap(600, 600);

  2、获取这块内存画布的Graphics引用:

Graphics g = Graphics.FromImage(bmp);

  3、在这块内存画布上绘图:

g.FillEllipse(brush, i * 10, j * 10, 10, 10);

  4、将内存画布画到窗口中

this.CreateGraphics().DrawImage(bmp, 0, 0);

还有的方式
在构造函数中加如下代码

代码一:
      SetStyle(ControlStyles.UserPaint, true);
      SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
      SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲

代码二:
   this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
   this.UpdateStyles();

=======================================================================

使用 GDI+ 双缓冲 解决绘图闪烁问题

现在的问题是很多人不知道怎么怎么使用GDI+ 双缓冲

public partial class Form1 : Form
    {
        //记录矩形位置的变量
        Point p = Point .Empty ;
        Point location = new Point(0, 0);
        int x = 0;
        int y = 0;

public Form1()
        {
            InitializeComponent();
            //采用双缓冲技术的控件必需的设置
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);

        }
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.Black, x, y, 200, 200);
        }
        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right) return;
            p = e.Location;
        }
        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right) return;
            location.X += e.X - p.X;
            location.Y += e.Y - p.Y;
            p = Point.Empty;
        }
        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
         if (p == Point.Empty) return;
            x = e.X - p.X + location.X;
            y = e.Y - p.Y + location.Y;
            this.Invalidate(true);//触发Paint事件
        }
     }
这个简单的例子实现了用鼠标拖动窗口中矩形,利用双缓冲技术使动画过程不会产生闪烁.
在这个例子上我犯的错误:
在 OnPaint(PaintEventArgs e)中,我使用下面两种方法获取graphics 对象
     Graphics g = this.CreateGraphics();
     Graphics g = Graphics.FromHwnd(this.Handle);
这都使双缓冲失效.
获得graphics 对象还有两种方法是
     Graphics g = Graphics.FromImage(image); //后面将用此方法实现双缓冲
     Graphics g = e.Graphics;  //这是唯一好使的方法

上面是在Form窗口直接绘制图形,那么如何在控件上(比如Panel)利用双缓冲技术绘图呢?
在窗口窗创建一个Panel , 并修改一下代码
        private void panel1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.FillRectangle(Brushes.Black, x, y, 200, 200);
        }
运行后发现拖动在panel1上绘制的图形依然有闪烁,那么是不是应该这样设置
panel1.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);//这样并不行,
因为SetStyle()在Panel类中不是public方法

使用从Panel类继承的MyPanel类 的构造函数中设置双缓冲
    public class MyPanel:Panel
    {
        public MyPanel()
        {
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
        }
    }

不管怎么说这个方法的确好用,不过注意以后你使用的是MyPanel类,而不是Panel.
把自己定义MyPanel从工具栏里拖到窗口上和Panle一样使用.

注意:在控件上绘制就必须设置该控件的DoubleBuffer,而不是Form的DoubleBuffer.

在此之前我采用自己的方法实现双缓冲而不是控件自身的DoubleBuffer
先在内存里绘制图形,包括清除旧画面和绘制新画面,然后将内存的图形绘制到屏幕上
public void Draw(System.Windows.Forms.Panel _panel, float  _x, float  _y)
{
  Graphics g = Graphics.FromHwnd(_panel.Handle);
  try{
     //在内存创建一块和panel一大小的区域
     Bitmap bitmap = new Bitmap(_panel.ClientSize.Width, _panel.ClientSize.Height);
     using (Graphics buffer = Graphics.FromImage(bitmap))
      {
        //buffer中绘图
        buffer.Clear(_panel.BackColor); //用背景色填充画面
        buffer.Transform = matrix;     
        buffer.DrawImage(source, _x/Scale , _y/Scale ); //绘制新画面
        //屏幕绘图
        g.DrawImage(bitmap, 0, 0); //将buffer绘制到屏幕上
       }
   }
  finally
  {
      g.Dispose();
   }
}
使用上面方法不需要任何设置.

总结一下
与绘图有关的ControlStyles
enum ControlStyles{
AllPainingInWmPaint, //将绘制阶段折叠入Paint事件
DoubleBuffer, //直到Paint返回,再显示绘制对象
UserPaint,  //用于自身有着特别绘制的控件

Opaque, //忽略OnPaintBackground,Paint事件绘制整个区域
ResizeRedraw,//当调整控件大小时使整个工作区无效
SupportsTransparentBackColor,//模拟透明控件
...
}

1.在OnPaint(PaintEventArgs e)或Paint中 使用e获取graphics,我之所以费了很大周折就是因为在网上找到一篇实现双缓冲文章介绍,不要使用e获取graphics,而用this.CreateGraphics(),还有的文章介绍了奇怪的方法居然最终也好使.

2.在继承了Form和control 的控件上利用双缓冲绘制的时候,可以在控件的构造函数里设置双缓冲属性,而在窗口Form 里设置doublebuffer,只针对窗口的绘制起作用.

3.使用自己的方法,双缓冲的原理都是一样的.

示例:

private void DrawRectBackImage()
        {
            Graphics g = Graphics.FromHwnd(_currentChart.Handle);
            try
            {
                //在内存创建一块和panel一大小的区域
                Bitmap bitmap = new Bitmap(_currentChart.ClientSize.Width, _currentChart.ClientSize.Height);
                using (Graphics buffer = Graphics.FromImage(bitmap))
                {
                    buffer.Transform =new  Matrix();

//要画的背景图片

                    buffer.DrawImage(curChartImage, _currentChart.ClientRectangle);

//背景图片上的内容
                    DrawFitAdjustRect(buffer);
                    //屏幕绘图
                    g.DrawImage(bitmap, 0, 0); //将buffer绘制到屏幕上
                }
            }
            finally
            {
                g.Dispose();
            }
        }

C#画图解决闪烁问题的更多相关文章

  1. ddraw 视频下画图 不闪烁的方法

    我们如果是在在RGB视频上画图(直线,矩形等),一般采用双缓冲区继续,使用内存MemoryDC,来实现画的图形在视频上显示不闪烁的功能,但是我们知道用RGB显示视频都是使用GDI进行渲染,这样很耗CP ...

  2. VC++ 实现VC程序启动时最小化到任务栏(完美解决闪烁问题)

    之前写的一个VC应用程序,是程序启动时就直接出现在任务栏, 窗体不出现,等用户点击任务栏图标再出现窗口.和一些防火墙什么的软件类似. 这种效果实现并不是很困难的,硬是找不到最好的.为什么呢? 首先,在 ...

  3. cefsharp解决闪烁

    CefSharp禁用GPU的命令行参数 其中,Major和Minor分别指代系统的主版本(大版本).次版本(小版本)版本号.其中指定了Windows7系统会禁用 GPU.,突发奇想,是否windows ...

  4. c# winfrom 更新控件时停止刷新,解决闪烁问题

    static Dictionary<Control, bool> m_lstFreezeControl = new Dictionary<Control, bool>(); / ...

  5. C# 实现PNG文件的背景透明显示,解决动态显示闪烁问题 【转】

    http://blog.sina.com.cn/s/blog_402c071e0102x4rl.html    以下内容,对于想要使用C#实现PNG图片背景透明显示,同时动态显示时无闪烁问题的人来说, ...

  6. winform画图闪烁问题

    问题:在winform程序的onpaint方法中画图, 连续画, 如鼠标移动时就要不断画图, 会闪烁. 解决方法:将要画图的部分放到一个自定义控件中, 自定义控件的onpaint方法里面画图, 然后再 ...

  7. vue解决加载闪烁问题

    <!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>& ...

  8. C# 中DataGridView和ListView闪烁问题的解决方法

    C# 中DataGridView和ListView闪烁问题的解决方法 方法一首先定义类,将此类放在datagridview或ListView所在的窗体类外面,然后代码如下, <span styl ...

  9. 使用Three.js挖空安装门来解决重叠闪烁的问题

    一.挖空原理说明 subtract 用墙面减去与门重叠的部分,产生一个新的对象,导入材质安装门即可 //参与减去几何体 //平行于x轴门 var meshH4Door = new ThreeBSP( ...

随机推荐

  1. 【js】正则表达式豁然开朗

    http://www.jikexueyuan.com/course/809_3.html?ss=1 小括号,中括号 中括号:[] ,它表示里面的字符任选一个 比如[abcd]+,就表示abcd这四个字 ...

  2. JPA学习---第二节:JPA开发环境和思想介绍

    一.下载相关 jar http://hibernate.org/orm/ 下载 hibernate ,解压 http://www.slf4j.org/download.html 下载 slf4j,解压 ...

  3. Ubuntu 12.4 下升级 Subversion 1.7

    Ubuntu 12.04 默认使用的是Subversion 1.6,而Ubutnu12.10开始,就使用的是Subversion 1.7. 如果从别人的地方拷过来的SVN目录,在使用SVN命令时会报以 ...

  4. duilib中各控件响应的消息类型

    消息 说明 Sender click 鼠标点击 CButtonUI dropdown 下拉显示 CComboUI headerclick 点击列标题 CListHeaderItemUI itemact ...

  5. 在唯一密钥属性“value”设置为“Default.aspx”时,无法添加类型为“add”的重复集合项

    环境:windows server 2012  asp.net 找到网站目录:wwwroot ,打开web.config文件,在 在<files>与</files>之间加入代码 ...

  6. Google history

    传说,硅谷的公司在和微软的竞争中一直处于下风,不论在市场,人才,还是在打官司上,直到婴儿巨人Baby Giant谷歌的出现,历史才出现前所未有的改变.Google以一个强大的挑战者的身份出现在人们的视 ...

  7. c# 应用程序部署发布

    转自:http://blog.csdn.net/chenyujing1234/article/details/7558185 最近做了C#软件,发布给客户用时,发现客户运行不起来,原因是客户电脑上没有 ...

  8. CString向char类型转化 ---“=”: 无法从“wchar_t *”转换为“char *

    此文从网上复制过来,原文出处已丢失,望见谅哈       VC 2005中,这个本来很简单的问题又稍微复杂了一点.    在工程里面,一个必不可少的步骤就是把CString转换为shar*字符串.通过 ...

  9. iOS开发网络编程之断点续传-NSURLConnection

    最近在做一个小项目的时候,发现使用NSURLSession或者AFNNetworking进行断点续传时诸多的不便,于是自己封装了一个类来实现断点续传,在程序重新启动时仍然可以继续下载(需自己调用方法) ...

  10. 【WCF--初入江湖】12 WCF与Ajax编程

    12 WCF与Ajax编程 Ajax Ajax基本原理 AJAX技术的本质原理就是:使用 JavaScript 的 XMLHttpRequest 对象来直接与服务器进行通信. 通过这个对象,JavaS ...