【发布时间】:2010-10-31 11:23:48
【问题描述】:
我的程序在其面板上绘制文本,但如果我想删除文本,我必须重新绘制。
如何手动调用(引发)绘制事件?
【问题讨论】:
我的程序在其面板上绘制文本,但如果我想删除文本,我必须重新绘制。
如何手动调用(引发)绘制事件?
【问题讨论】:
在表单或控件的方法中,您有 3 个选择:
this.Invalidate(); // request a delayed Repaint by the normal MessageLoop system
this.Update(); // forces Repaint of invalidated area
this.Refresh(); // Combines Invalidate() and Update()
通常,您只需致电Invalidate() 并让系统将其与其他屏幕更新结合起来。如果你赶时间,你应该打电话给Refresh(),但是你冒着因为其他控件(尤其是父控件)无效而连续多次重新绘制它的风险。
Windows(Win32 和 WinForms.Net)处理此问题的正常方式是等待MessageQueue 运行为空,然后处理所有无效的屏幕区域。这是有效的,因为当某些事情发生变化时,通常会级联到其他事情(控件)也发生变化。
Update() 最常见的情况是当您在 for 循环中更改属性(例如 label1.Text,这将使标签无效)并且该循环暂时阻塞消息循环时。每当你使用它时,你应该问问自己是否不应该使用线程。但答案并不总是肯定的。
【讨论】:
this.Invalidate() 需要最少的 CPU 资源,因为您不会在标准消息循环之外强制执行任何操作,如果您可以调用 this.Invalidate(false) 则尤其如此,因此您只在主控件/表单上调用 OnPaint() 而不是子控件。对于我的一个 UserControl,从 this.Update() 移动到 this.Invalidate(false) 将 CPU 负载减少了 4 倍。
Invalidate() 方法将导致重绘。
【讨论】:
我发现 Invalidate() 造成了太多的闪烁。这是我的情况。我正在开发的自定义控件通过处理 Paint 事件来绘制其全部内容。
this.Paint += this.OnPaint;
这个处理程序调用一个自定义例程来进行实际的绘画。
private void OnPaint(object sender, PaintEventArgs e)
{
this.DrawFrame(e.Graphics);
}
为了模拟滚动,我想在每次按下鼠标左键时光标移动时重新绘制控件。我的第一选择是使用 Invalidate(),如下所示。
private void RedrawFrame()
{
var r = new Rectangle(
0, 0, this.Width, this.Height);
this.Invalidate(r);
this.Update();
}
控件滚动正常,但闪烁远远超出任何舒适的水平。因此,我决定在处理 MouseMove 事件后直接调用我的自定义 DrawFrame() 方法,而不是重新绘制控件。这产生了没有闪烁的平滑滚动。
private void RedrawFrame()
{
var g = Graphics.FromHwnd(this.Handle);
this.DrawFrame(g);
}
这种方法可能并不适用于所有情况,但到目前为止它很适合我。
【讨论】:
using() {} 中)
我想你也可以拨打Refresh()。
【讨论】:
也许这是一个老问题,这就是为什么这些答案对我不起作用的原因......使用 Visual Studio 2019,经过一番调查,这是我找到的解决方案:
this.InvokePaint(this, new PaintEventArgs(this.CreateGraphics(), this.DisplayRectangle));
【讨论】:
调用 control.invalidate 并引发绘制事件。
【讨论】:
根据上下文,刷新也可能使代码更具可读性。
【讨论】:
使用Control.InvokePaint也可以用于手动双缓冲
【讨论】: