窗体等控件的OnPaint()方法到底是什么时候调用的,高分求解!

hujiiori 2005-01-17 03:00:38
一个问题困扰我很久了,窗体等控件的OnPaint()方法到底是什么时候调用的,说invalidate()会引起窗体重绘,但我发现调用invalidate()不会立即引发OnPaint(),譬如连续调用一百次Invalidate(),只会执行一次OnPait(),即使用update()这个方法,情况也一样。那想请教各位,系统内部到底是如何控制控件刷新的(类似的还有鼠标键盘等消息的处理),OnPaint跟显卡及显示器的刷新有没有关系,是否有个系统线程专门来负责底层的各种消息的处理,然后调用相应的方法,因为我是。net出身的程序员,所以对这些底层的东东很不了解,希望有高手跟我说说,或者推荐一下这方面的书籍或网站,谢谢。
...全文
2285 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
nga96 2005-01-17
  • 打赏
  • 举报
回复
我也不熟悉的,呵
cppTrier 2005-01-17
  • 打赏
  • 举报
回复
好吧,我觉得你应该去补习一下Win32 API的知道,这样你就能明白这是怎么回事了,我可以在这里粗略的讲一下。

任何一个Win32 API写的Windows应用程序,都有一个消息队列,而Win Form其实是对Win32 API的封装,所以Win Form程序也有一个消息队列。当需要重新绘制窗口时(比如窗口被用户移动了,或者程序调用了invalidate()方法等),操作系统会将一条WM_PAINT消息放到程序的消息队列中。

程序中会有一个循环,负责不断的从消息队列中取出消息,然后交给一个被称为窗口处理函数的函数(WinProc)来处理,WinProc会根据不同的消息类型,进行不同的处理(一个Switch语句)。

在Win32 API程序中,程序员自己负责写那个循环和WinProc,但是在Win Form里,这些已经被封装到Form类和Application类中去了。在Form类中的WinProc中,收到WM_PAINT消息就会去调用OnPaint()函数,这也就是我们为什么可以通过override OnPaint()函数和处理WM_PAINT消息。

最后,为了提高效率,操作系统在发给程序WM_PAINT消息时,会首先检查程序的消息队列,如果队列中有尚未处理的WM_PAINT消息,那么操作系统会把两条消息合并之后放在队列的尾部,这也就是为什么你反复调用invalidate()函数,但还是只会发生一次OnPaint()的原因。

鼠标和键盘的情况也是一样,操作系统会发相应的消息给程序,然后程序把消息交给窗口处理函数来处理。

如果你想更多地了解Win32 API,我建议你看Charles Petzold写的《Windows程序设计》一书,这本书可以称得上是Win32 API领域的Bible

http://www.dearbook.com/book/viewbook.aspx?pno=TS003163
Jim3 2005-01-17
  • 打赏
  • 举报
回复
invalidate()最后要调用的Api函数是InvalidateRect

在这个api的Remark中说
The system sends a WM_PAINT message to a window whenever its update
region is not empty and there are no other messages in the
application queue for that window.

我的理解就是当update region不为空并且没有别的消息在应用程序消息队列里
时就发送WM_PAINT到那个窗口

这里“别的消息”我认为就是WM_PAINT

这样你说调用100次也只执行一次就好理解了

Amoon 2005-01-17
  • 打赏
  • 举报
回复
-_- 怒了,回个帖子都得弄N多次。
Amoon 2005-01-17
  • 打赏
  • 举报
回复
OnPaint()是在窗口收到WM_PAINT消息时,由操作系统来调用的。窗口移动,大小变化,不是导致调用OnPaint的根本原因。当你逮着你的窗口四处拖,只要不超出屏幕范围,窗口就不会重绘。修改窗口大小也一样,你一直减小窗口的尺寸试试,窗口也不会重绘。因为窗口的客户区部分始终是有效的。
而窗口客户区一部分或全部因为被遮挡,需要重新显示出来的时候(比如:你的窗口从其他窗口背后冒出来,0_o),WM_PAINT消息就会发送到窗口的消息队列,这时候该还不会马上就调用OnPaint来重绘,而是当窗口的消息循环处理处GetMessage取到WM_PAINT消息时,OnPaint被调用,窗口重绘。
hujiiori 2005-01-17
  • 打赏
  • 举报
回复
Jim3(Jim) 说的就是我想要了解的,谢谢,
但如何解释连续多次调用invalidate(),只执行一次OnPaint()的事实,能说更详细些吗
hujiiori 2005-01-17
  • 打赏
  • 举报
回复
错了,是楼上的楼上
hujiiori 2005-01-17
  • 打赏
  • 举报
回复
楼上的说出了我的心声,给你加分
Jim3 2005-01-17
  • 打赏
  • 举报
回复
invalidate()这个方法将会根据你的调用参数,发送不同的消息到
应用程序的消息队列中,比如:WM_NCPAINT ,WM_PAINT ,WM_ERASEBKGND 等

这个函数并不直接去调用Paint方法,等这个方法结束后,应用程序中有一个
消息循环在不停的去读取这个消息队列,当读到消息WM_PAINT时就会去调应
OnPaint函数,OnPaint函数触发事件.......

windows应用程序的核心在消息循环,可以搜索一下,有很多文章介绍的
Amoon 2005-01-17
  • 打赏
  • 举报
回复
-_- 又尽是些Copy MSDN的某某 0_o oh,yeah,不爽啊~~~~
老汉 2005-01-17
  • 打赏
  • 举报
回复
Control.OnPaint 方法请参见
Control 类 | Control 成员 | System.Windows.Forms 命名空间 | Paint | Control 成员(Visual J# 语法) | C++ 托管扩展编程
要求
平台: Windows 98, Windows NT 4.0, Windows ME, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003 系列, .NET Framework 精简版 - Windows CE .NET
语言
C#

C++

JScript

Visual Basic

全部显示
引发 Paint 事件。

[Visual Basic]
Protected Overridable Sub OnPaint( _
ByVal e As PaintEventArgs _
)

[C#]
protected virtual void OnPaint(
PaintEventArgs e
);

[C++]
protected: virtual void OnPaint(
PaintEventArgs* e
);

[JScript]
protected function OnPaint(
e : PaintEventArgs
);

参数
e
包含事件数据的 PaintEventArgs。
备注
引发事件时会通过委托调用事件处理程序。有关更多信息,请参见引发事件。

OnPaint 方法也允许派生类对事件进行处理而不附加委托。这是在派生类中处理事件的首选技术。

对继承者的说明: 在派生类中重写 OnPaint 时,请确保调用了基类的 OnPaint 方法,以便注册的委托接收该事件。

示例
[Visual Basic, C#, C++] 下面的示例使用户能够将图像或图像文件拖到窗体上,并使它在放置点显示。每次绘制窗体时,都重写 OnPaint 方法以重新绘制图像;否则图像将保持到下一次重新绘制。DragEnter 事件处理方法决定拖到窗体中的数据的类型,并提供适当的反馈。如果 Image 可以从该数据中创建,则 DragDrop 事件处理方法就会在该窗体上显示此图像。因为 DragEventArgs.X 和 DragEventArgs.Y 值为屏幕坐标,所以示例使用 PointToClient 方法将它们转换成工作区坐标。
liduke 2005-01-17
  • 打赏
  • 举报
回复
在任何需要引发Paint事件时,都会执行这个方法,如大小变动,invalidate()等
hujiiori 2005-01-17
  • 打赏
  • 举报
回复
TO brbrm(般若波若密)
OnPaint是方法,需要你调用,引发的是Paint事件
----------------------
我想知道由系统调用时的情形
hujiiori 2005-01-17
  • 打赏
  • 举报
回复
To RockyZhang(Rocky)
控件改变大小或位置后重绘是因为设置窗体大小和位置属性时,在内部调用了control.Invalidate(),但我想知道的是 control.Invalidate()之后过多久满足什么条件会调用control.OnPaint()
brbrm 2005-01-17
  • 打赏
  • 举报
回复
看MSDN中的说明:
The OnPaint method of the base Control class does not implement any drawing functionality but merely invokes the event delegates that are registered with the Paint event. When you override OnPaint, you should typically invoke the OnPaint method of the base class so that registered delegates receive the Paint event. However, controls that paint their entire surface should not invoke the base class'sOnPaint, as this introduces flicker. For an example of overriding the OnPaint event, see the Windows Forms Control Sample.

brbrm 2005-01-17
  • 打赏
  • 举报
回复
OnPaint跟显卡及显示器的刷新当然没有关系
OnPaint是方法,需要你调用,引发的是Paint事件
RockyZhang 2005-01-17
  • 打赏
  • 举报
回复
应该是窗体重绘时发生吧.
我得理解是当窗体的大小位置等发生变化时发生.
例如:一个窗体的大小是500*600, Form上有一个Button,点击Button后,窗体大小将改为800*600. 设置Paint事件后,点击Button后,会触发Paint事件.
例:
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    MessageBox.Show("Test");
}

private void button1_Click(object sender, System.EventArgs e)
{
this.ClientSize = new System.Drawing.Size(800, 600);
}

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧