持久图形 WinForms
Posted
技术标签:
【中文标题】持久图形 WinForms【英文标题】:Persistent graphics 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);
我可以设置任何属性来提高绘制图形的平滑度吗?
【问题讨论】:
【参考方案1】:目标
图像显示在控件(或窗体)上。
失效
任何时候控件(或窗体)被调整大小、最小化/最大化、部分遮挡或移动,它必须(部分)重绘。当这种情况发生时,必须重绘的控件部分被称为无效。
当控件无效时,会执行如下操作:
-
调用
OnPaintBackground
:这会用背景色填充无效区域。
调用OnPaint
:这会在背景上绘制文本和图形。
为什么OnPaint
会导致闪烁
您已经覆盖了控件的OnPaint
method。每次重绘控件时,您都会看到控件的闪烁,其中仅绘制了其背景颜色。那是在调用OnPaintBackground
之后和调用OnPaint
之前。
解决方案
如果你有一个静态图像(即它永远不会改变):
-
在
Load
事件中:创建一个新的Bitmap
object。
用背景色填充并在其上绘制线条和形状。
将此Bitmap
对象分配给控件的BackgroundImage
property。
如果您有一个在控件调整大小时必须调整大小的静态图像:
-
覆盖
OnResize
method 并在其中创建新的Bitmap
。使用控件的ClientSize
property 作为Bitmap
的大小。
用背景色填充并在其上绘制线条和形状。
将此Bitmap
对象分配给控件的BackgroundImage
属性。
如果您有动画图像:
-
在
Load
事件中,将控件的DoubleBuffered
property 设置为true
。设置此项可防止您看到的闪烁,因为它使用不可见的缓冲区来绘制控件。
覆盖控件的OnPaint
method。获取控件的Graphics
context,直接在控件上绘制线条和形状。
创建并启用一个新的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,阅读以下内容:
"缓冲图形可以减少或消除由以下原因引起的闪烁 逐步重绘显示表面的各个部分。缓冲的 图形要求首先将更新的图形数据写入 缓冲。然后将图形缓冲区中的数据快速写入 显示表面记忆。显示的切换比较快 图形内存通常会减少闪烁,否则 发生。”
【讨论】:
以上是关于持久图形 WinForms的主要内容,如果未能解决你的问题,请参考以下文章