为完成PaintBoardDemo(本人设计的一个基于.NET Framework的WinForm的画图程序),过程当中遇到的技术难点之一就是就是要显示任何图形绘制过程当中的轨迹,也即须要在pictureBox控件的MouseMove事件中添加相应的Graphics对象的DrawXX Methods.
在设计之初,仅仅可以在MouseMove事件中写出一行代码:g.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y)(ptLast为Point类型的全局变量,用以保存MouseDown事件的坐标)。可想而知,鼠标的每一次Move就将触发MouseMove事件,即执行一次g.DrawLine(画一条直线)。所以程序运行的结果就是,画板上显示了绘制过程当中的全部轨迹,以下图,显然,这是与咱们的设计目的相不符的。
通过对Bitmap与Graphics的学习及通过同窗开导,终于解决了不显示绘制过程当中多余轨迹的问题。基本实现的思想能够总结以下:
1.创建一个位图对象,即Bitmap _bitmapTemp,和一个Graphics对象用来实现一系列绘制操做,即Graphics g(_bitmapTemp,g均为全局变量)。
2.在主窗体的Shown事件中,对_bitmapTemp和g进行初始化:
_bitmapTemp=new Bitmap(pictureBox.Width,pictureBox.Height);//初始化位图大小和pictureBox一致
Graphics.FromImage(_bitmapTemp).FillRectangle(Brushes.White,0,0,pictureBox.Width,pictureBox.Height);//而且将位图_bitmapTemp以白色填充所有区域,以做绘制之用。
g=pictureBox.CreateGraphics();//实例化g对象,代表之后对g的全部绘制操做均在pictureBox之上。
pictureBox.Image=_bitmapTemp;//“引用传递”,将pictureBox.Image&_bitmapTemp指向内存中同一块位图,实如今pictureBox显示实时的绘制结果。
3.pictureBox的MouseMove事件中执行绘制操做:
g.DrawImage(_bitmapTemp,0,0);//在第N次绘制时,将第N-1次绘制所得的结果画到pictureBox中,既实现了绘制结果的同步显示,也消除了第N次绘制过程当中产生的轨迹。
g.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);//绘制直线。
4.pictureBox的MouseUp事件中将绘制结果绘制到_bitmapTemp,保证绘制结果的正确性(所绘的图形均被保留):
Graphics.FromImage(_bitmapTemp).DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);
至此,就能够在pictureBox中绘制各种图形了(调用Graphics类的DrawXX Methods便可)。可是会发现整个绘制过程当中,图形的闪动极其厉害。为何呢?回过头来再看代码,发现问题出在g.DrawImage(_bitmapTemp,0,0);每次MouseMove,就在pictureBox中将以前的图所有从新绘制一次,每每绘制过程当中触发的MouseMove事件是不可胜数的,频繁的重绘全图,不只效率低下,更带来了直观的闪动问题。可是也不可能把这行代码去掉,它但是解决绘制轨迹问题的核心代码。
百度了关于闪动优化的方法,发现很多都提到了“双缓冲"技术,参照着网上的方法,发现闪动问题确实获得了解决。看来有必要对这个双缓冲技术进行一下学习。
双缓冲解决闪动问题的原理主要是因为当启用双缓冲时,全部绘制操做均呈现到内存缓冲区,而不是在屏幕上绘制。全部绘制完成以后,内存缓冲区直接复制到与其关联的绘制区域。由于在屏幕上只执行了一次图形操做,因此消除了由复杂操做形成的图像闪烁。
【默认双缓冲】
在应用程序中启用双缓冲的最简便的方法是使用.NET Framework为窗体和控件提供的默认双缓冲。经过将DoubleBuffered属性设置为True或者使用SetStyle方法能够为Windows窗体和所创做的Windows控件启用默认双缓冲。
public void EnableDoubleBuffering()
{
// Set the value of the double-buffering style bits to true.
this.SetStyle(ControlStyles.DoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint,
true);
this.UpdateStyles();
}
【管理和显示缓冲的图形】 c#
.NET中负责单独分配和管理图形缓冲区的类是BufferedGraphicsContext类。每一个应用程序都有本身的默认BufferedGraphicsContext实例来管理此应用程序的全部默认双缓冲。大多数状况下,每一个应用程序只有一个应用程序域,因此每个应用程序一般只有一个BufferedGraphicsContext。默认 BufferedGraphicsContext 实例由 BufferedGraphicsManager 类管理。经过调用 BufferedGraphicsManager.Current 属性能够检索对默认 BufferedGraphicsContext 实例的引用。还能够建立一个专用的 BufferedGraphicsContext 实例以提升图形密集型应用程序的性能。介绍一下所需用到的几个类。
BufferedGraphicsContext类提供建立图形缓冲区的方法,该缓冲区能够用于双缓冲,建立用于绘制缓冲图形的 BufferedGraphics 实例。
BufferedGraphicsManager能够实现图形的自定义双缓冲,提供对应用程序域的主缓冲图形上下文对象的访问。此类有一个静态属性Current,该属性返回当前应用程序域的主BufferGraphicsContext
BufferedGraphics类用于绘制缓冲图形,它没有公共的构造函数,必须有应用程序域的BufferedGraphicsContext对象使用其Allocate方法建立。提供图形缓冲区的包装,以及可用于写入缓冲区和将其内容呈现到输出设备的方法。经过Graphics属性提供对Graphics对象的访问(即访问一系列的Draw方法,便可在缓冲区中进行绘制操做),Render()方法用来将图形缓冲区的内容写入到与缓冲区关联的区域。
所以实如今缓冲区绘制图形而且最终显示到屏幕上的目标控件中可经过以下代码:
//得到分配和管理图形的缓冲区(该应用程序域的主缓冲区)
BufferedGraphicsContext current = BufferedGraphicsManager.Current;
//建立用于缓冲区绘制的对象myBuffer,像素格式为pictureBox.CreateGraphics,大小和pictureBox一致
//这里咱们假设缓冲区图像最终是显示到pictureBox中的
BufferedGraphics myBuffer = current.Allocate(pictureBox.CreateGraphics(),pictureBox.DisplayReactangle);
//和以前的想法一致,DrawImage用以消除绘制过程当中的多余轨迹,DrawLine进行绘制操做(以直线为例)
myBuffer.Graphics.DrawImage(_bitmapTemp,0,0);
myBuffer.Graphics.DrawLine(new Pen(Color.Red),ptLast.X,ptLast.Y,e.X,e.Y);
//将缓冲区的图片绑定至pictureBox,最终显示在屏幕上
myBuffer.Render(pictureBox.CreateGraphics());
//释放系统资源
myBuffer.Dispose();