MFC常见问题解惑

摘要:
OnCreate函数用于“指示正在生成窗口”。因为在MFC中,消息映射机制用于响应消息,也就是说,可以使用函数来响应相应的消息。以CMainFrame类为例。创建窗口时,将生成WM_ CREATE消息。我们可以在OnCreate函数中实现我们想在窗口中添加的内容,例如按钮、状态栏、工具栏等。OnDraw()是CVIEW的成员函数,没有响应消息的功能。第二,我们已经在《每天和我一起学习MFC 3》的开头提到了。MFC提供CPaintDC类和CWindwoDC类用于实时响应,而CPaintC支持重绘。

MFC类的分类

1Root: CObject :CObject
2Application Architecture Classes:CWinApp/CFrameWnd/...

3Window, Dialog, and Control Classes:CWnd/CDialog/...
4Drawing and Printing Classes :CGdiObject/CPrintInfo/...
5Simple Data Type Classes :CString/CRect/...
6Array, List, and Map Classes :CTypedPtrArray/...
7File and Database Classes :CStdioFile/CDatabase/...
8Internet and Networking Classes :CSocket/...
9OLE Classes...
10Debugging and Exception Classes:CMemoryState/CException/...

OnCreate InitDialog区别:

WM_CREATE的时候窗口还没有建立,你可以给成员变量赋值,也可以修改窗口的基本内容比如风格,但是不能对窗口控件操作,因为窗口还没有建立。
INITDIALOG时候窗口已经建立,你可以移动窗口,修改风格等等,也可以给成员变量赋值,

通常你要先建立(OnCreate)一个窗体,然后再对他进行初始化(OninitDialog).

WM_INITDIALOG
The WM_INITDIALOG message is sent to the dialog box procedure immediately before a dialog box is displayed.
WM_CREATE
The WM_CREATE message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function.

Create与OnCreate的区别:

OnCreate是一个消息响应函数,是响应WM_CREATE消息的一个函数,而WM_CREATE消息是由Create函数调用(#add 发生?)的。

在view类中,Create 是虚函数由框架调用,是用来“生成一个窗口的子窗口”。 而OnCreate 函数是用来“表示一个窗口正在生成”。

一个窗口创建(Create)之后,会向操作系统发送WM_CREATE消息,OnCreate()函数主要是用来响应此消息的。因为在MFC里面用一种消息映射的机制来响应消息,也就是可以用函数来响应相应的消息。就拿CMainFrame类来说,当窗口创建后会产生WM_CREATE消息,我们可以在OnCreate函数里实现我们要在窗口里面增加的东西,例如按扭,状态栏,工具栏等。这些子窗口一般是定义成类中的一个成员变量,因为要保证生命周期。一般以m_开头来表示成员(member)。

OnCreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等,Create()负责注册并产生窗口

Create()不是对应于消息WM_CREATE的,OnCreate()才是。Create()只用于产生窗口,像动态创建控件中的Create()一样。

MSDN:

TheCWndobject receives this call after the window is created but before it becomes visible.OnCreateis called before theCreateorCreateExmember function returns.

Override this member function to perform any needed initialization of a derived class.

OnDraw和OnPaint区别:

OnPaint是WM_PAINT消息的消息处理函数在OnPaint中调用OnDraw,一般来说,用户自己的绘图代码应放在OnDraw中

OnPaint()是CWnd的类成员,负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,没有响应消息的功能.当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows发送WM_PAINT消息。该视图的OnPaint 处理函数通过创建CPaintDC类的DC对象来响应该消息并调用视图的OnDraw成员函数.OnPaint最后也要调用OnDraw,因此一般在OnDraw函数中进行绘制。

The WM_PAINT message is sent when the UpdateWindow or RedrawWindow member function is called.

在OnPaint中,将调用BeginPaint,用来获得客户区的显示设备环境,并以此调用GDI函数执行绘图操作。在绘图操作完成后,将调用EndPaint以释放显示设备环境。而OnDraw在BeginPaint与EndPaint间被调用。

1) 在mfc结构里OnPaint是CWnd的成员函数. OnDraw是CView的成员函数.(#add 当然CView 是CWnd的子类)
2) OnPaint()调用OnDraw(),OnPrint也会调用OnDraw(),所以OnDraw()是显示和打印的共同操作。

OnPaint是WM_PAINT消息引发的重绘消息处理函数,在OnPaint中会调用OnDraw来进行绘图。OnPaint中首先构造一个CPaintDC类得实例,然后一这个实例为参数来调用虚函数OnPrepareDC来进行一些绘制前的一些处理,比设置映射模式,最后调用OnDraw。而OnDraw和OnPrepareDC不是消息处理函数。所以在不是因为重绘消息所引发的OnPaint导致OnDraw被调用时,比如在OnLButtonDown等消息处理函数中绘图时,要先自己调用OnPrepareDC。
至于CPaintDC和CClientDC根本是两回事情 CPaintDC是一个设备环境类,在OnPaint中作为参数传递给OnPrepareDC来作设备环境的设置。真正和CClientDC具有可比性的是CWindowDC,他们一个是描述客户区域,一个是描述整个屏幕。
如果是对CVIEW或从CVIEW类派生的窗口绘图时应该用OnDraw。

OnDraw()和OnPaint()有什么区别呢?
首先:我们先要明确CView类派生自CWnd类。而OnPaint()是CWnd的类成员,同时负责响应WM_PAINT消息。OnDraw()是CVIEW的成员函数,并且没有响应消息的功能。这就是为什么你用VC成的程序代码时,在视图类只有OnDraw没有OnPaint的原因。而在基于对话框的程序中,只有OnPaint(#add,因为没有VIEW类)。
其次:我们在第《每天跟我学MFC》3的开始部分已经说到了。要想在屏幕上绘图或显示图形,首先需要建立设备环境DC。其实DC是一个数据结构,它包含输出设备(不单指你17寸的纯屏显示器,还包括打印机之类的输出设备)的绘图属性的描述。MFC提供了CPaintDC类和CWindwoDC类来实时的响应,而CPaintDC支持重画。当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。
///CView默认的标准的重画函数
void CView::OnPaint() //见VIEWCORE.CPP,这是源码
{

CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc); //调用了OnDraw
}
///CView默认的标准的OnPrint函数
void CView::OnPrint(CDC* pDC, CPrintInfo*)
{
ASSERT_VALID(pDC);
OnDraw(pDC); // Call Draw
}

既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。下面是一个典型的程序。
///视图中的绘图代码首先检索指向文档的指针,然后通过DC进行绘图调用。
void CMyView::OnDraw( CDC* pDC )
{

CMyDoc* pDoc = GetDocument();
CString s = pDoc->GetData();
GetClientRect( &rect ); // Returns a CString CRect rect;
pDC->SetTextAlign( TA_BASELINE | TA_CENTER );
pDC->TextOut( rect.right / 2, rect.bottom / 2, s, s.GetLength() );
}
最后:现在大家明白这哥俩之间的关系了吧。因此我们一般用OnPaint维护窗口的客户区(例如我们的窗口客户区加一个背景图片),用OnDraw维护视图的客户区(例如我们通过鼠标在视图中画图)。当然你也可以不按照上面规律来,只要达到目的并且没有问题,怎么干都成。补充:我们还可以利用Invalidate(),ValidateRgn(),ValidateRect()函数强制的重画窗口,具体的请参考MSDN吧。

OnDraw中可以绘制用户区域。OnPaint中只是当窗口无效时重绘不会保留CClientDC绘制的内容。

这两个函数有区别也有联系:

1、区别:OnDraw是一个纯虚函数,定义为virtual void OnDraw( CDC* pDC ) = 0; 而OnPaint是一个消息响应函数,它响应了WM_PANIT消息,也是是窗口重绘消息。

2、联系:我们一般在视类中作图的时候,往往不直接响应WM_PANIT消息,而是重载OnDraw纯虚函数,这是因为在CVIEW类中的WM_PANIT消息响应函数中调用了OnDraw函数,如果在CMYVIEW类中响应了WM_PAINT消息,不显式地调用OnDraw函数的话,是不会在窗口重绘的时候调用OnDraw函数的。

应用程序中几乎所有的绘图都在视图的 OnDraw 成员函数中发生,必须在视图类中重写该成员函数。(鼠标绘图是个特例,这在通过视图解释用户输入中讨论。)

OnDraw 重写:
通过调用您提供的文档成员函数获取数据。
通过调用框架传递给 OnDraw 的设备上下文对象的成员函数来显示数据。
当文档的数据以某种方式更改后,必须重绘视图以反映该更改。默认的 OnUpdate 实现使视图的整个工作区无效。当视图变得无效时,Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的设备上下文对象来响应该消息并调用视图的 OnDraw 成员函数。

当没有添加WM_PAINT消息处理时,窗口重绘时,由OnDraw来进行消息响应...当添加WM_PAINT消息处理时,窗口重绘时,WM_PAINT消息被投递,由OnPaint来进行消息响应.这时就不能隐式调用OnDraw了.必须显式调用( CDC *pDC=GetDC(); OnDraw(pDC); )..(#add,在OnPaint函数中添加)
隐式调用:当由OnPaint来进行消息响应时,系统自动调用CView::OnDraw(&pDC).

想象一下,窗口显示的内容和打印的内容是差不多的,所以,一般情况下,统一由OnDraw来画。窗口前景需要刷新时,系统会会调用到OnPaint,而OnPaint一般情况下是对DC作一些初始化操作后,调用OnDraw()。

OnEraseBkGnd(),是窗口背景需要刷新时由系统调用的。明显的一个例子是设置窗口的背景颜色(你可以把这放在OnPaint中去做,但是会使产生闪烁的现象)。
至于怎么界定背景和前景,那要具体问题具体分析了,一般情况下,你还是很容易区别的吧。

的确,OnPaint()用来响应WM_PAINT消息,视类的OnPaint()内部根据是打印还是屏幕绘制分别以不同的参数调用OnDraw()虚函数。所以在OnDraw()里你可以区别对待打印和屏幕绘制。
其实,MFC在进行打印前后还做了很多工作,调用了很多虚函数,比如OnPreparePrint()等。

对于OnDraw()
This method is called by the framework to render an image of the document. The framework calls this method to perform screen display, printing, and print preview, and it passes a different device context in each case. There is no default implementation.

OnDrawItemDrawItem的区别:

1.OnDrawItem:WM_DRAWITEM消息的消息处理函数

子控件有自画属性且控件需重画时,父窗口会调用该函数,在具有Owner Draw属性的控件需要重画的时候,就会激发OnDrawItem

当自画子按钮控件、组合框控件、列表框控件或菜单的可视部分需要被画出时调用这个函数

OnDrawItem()-àDrawItem();

2.DrawItem:虚函数,需要重载

如果使用DrawItem来自画控件,需要给控件加上自画(owner draw)样式,然后重载该控件类的自画函数(DrawItem)函数,如果该控件的父窗口提供了ON_WM_DRAWITEM消息映射宏,并重载了OnDrawItem函数,则重画消息会由父窗口处理,父窗口调用基类的OnDrawItem来调用派生的子控件的DrawItem函数.

(#add 似乎有表述错误,像"重载了OnDrawItem函数"这句,真他妈的sb说的话, 仍未彻底明白,待解决)

OnCtlColor CtlColor区别:

OnCtlColor是父窗口中的消息响应函数,用来处理子控件的绘制,

CtlColor是子控件中的响应函数,这种控件拥有自绘功能,由他自己来绘制,属消息反射机制.

OnCtlColor是响应子控件发来的WM_CTLCOLOR消息,

CtlColor是反射控件自己发出的WM_CTLCOLOR消息。

ClientToScreen和 ScreenToClient 区别:

ClientToScreen()是把窗口坐标转换为屏幕坐标

ScreenToClient()是把屏幕坐标转换为窗口坐标
屏幕坐标是相对于屏幕左上角的,而窗口坐标是相对于窗口用户区左上角的
VC下,有些函数使用窗口坐标,有些使用屏幕坐标,使用时要分清。
一个窗体分为两部分:系统区和客户区
象标题和菜单之类的是系统区,由系统来控制,客户区就是你的地盘喽!!!
Width,Height是指整体的,ClientWidth,ClientHeight是指客户区的,两者相减就是
系统区的啦!!!
ClientToScreen是把坐标从当前窗体转化成全屏幕的!!!
ScreenToClient是把屏幕坐标转化成相对当前窗体的坐标!!!!

WPARAM和 LPARAM 的区别:

wParam和lParam 这两个是Win16系统遗留下来的产物,在Win16API中WndProc有两个参数:

一个是WORD类型的16位整型变量;另一个是LONG类型的32位整型变量。因此根据匈牙利命名法,16位的变量就被命名为wParam,32位的变量就被命名为lParam。

到了Win32API中,原来的16位变量也被扩展为32位,因此此时wParam和lParam的大小完全相同。
在Win32API的早期,为了保证和Win16API的代码可移植性MS定义了WPARAM和LPARAM两个宏。
当时保留了w前缀的原因一方面是由于WPARAM宏也已W开头,还有也因为要提醒程序员注意到可移植性,当然到了现在Win16早已退出历史舞台,这个前缀也就约定俗成的沿用下来了。

Invalidate以及Invalidate(false)和Invalidate(true)的区别:

1.void Invalidate( BOOL bErase = TRUE );

该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变

它和 UpdateWindow( )区别在于:

UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。

2.Invalidate(false)和Invalidate(true)的区别

(1)Invalidate(false)正常 (2)Invalidate(true)出现刷屏 (3)如果不用,那么每次打开文件将不能自动显示,可以手动更改窗口大小,那么就可以正常显示了。

PostMessage和SendMessage以及PeekMessage和GetMessage区别:

PostMessage发送完消息后立刻返回继续执行程序.将消息放到消息队列
SendMessage发送完消息后等待消息处理完以后,才能返回继续执行程序.
不将消息放到队列

更确切地说:PostMessage是给一个窗口发送了消息.
SendMessage是直接去调用这个窗口的窗口处理函数来处理一个消息并等其返回,

a。GetMessage类似于SendMessage. 有消息才会传回, 否则是阻塞的...,同时取出(删除)原队列的消息.
b。 PeekMessage 类似于PostMessage ,没有消息也立即返回, 同时可以选项选择是否删除原消息...

1. GetMessage()只有在接收到消息后才将控制权转给你的程序,而PeekMessage()无论有没有消息都会将控制权转给你的程序:如果有消息,返回真,没有消息返回假。
2。GetMessage()的主要功能是从消息队列中“取出”消息,消息被取出后,消息队列中就不再由该消息了;而PeekMessage()的主要功能是“窥视(peek)”消息,如果有消息,返回真,没有返回假。但PeekMessage()允许你从消息队列中“取出”消息,这就是PeekMessage()第五个参数的用途:如果选用PM_REMOVE,则消息从队列中取出,如选用PM_NOREMOVE,则PeekMessage()则“文如其人”,只是“偷看”,而保留消息。
3。GetMessage()每次都“等待处理消息”而PeekMessage()只是“察看有无消息”。
和PeekMessage()相关的代码:
while (TRUE)
{
if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break ;
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
else
{
//other program lines to do some work
}
}

DEClARE_DYNCREATE 与DECLARE_DYNAMIC区别:

IMPLEMENT_DYNAMIC是实现“运行时类型识别”宏,与之相对应的是DECLARE_DYNAMIC(声明“运行时类型识别”宏)。也就是说你在.CPP文件中如果看见有IMPLEMENT_DYNAMIC,则在.H文件中必定有DECLARE_DYNAMIC的声明。
所以DECLARE_DYNAMIC/DEClARE_DYNAMIC是为了确定运行时对象属于哪一个类而定义的宏。你可以在运行时利用RUNTIME_CLASS宏和CObject::IsKindOf函数来确定对象所属的类。

DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE是为了“动态创建"类的实例而定义的宏。new可以用来创建对象,但不是动态的。比如说,你要在程序中实现根据拥护输入的类名来创建类的实例,下面的做法是通不过的:
char szClassName[60];
cin >> szClassName;
CObject* pOb=new szClassName; //通不过
所以要实现动态创建类就要用到DEClARE_DYNCREATE/IMPLEMENT_DYNCREATE定义的功能了。

memmove和memcpy区别:

1. 当 src 和 dest 所指内存区有重叠时,memmove 相对 memcpy 能提供保证:保证能将 src 所指内存区的前 n 个字节正确的拷贝到 dest 所指内存中;
2. 当 src 地址比 dest 地址低时,两者结果一样。换句话说,memmove 与 memcpy 的区别仅仅体现在 dest 的头部和 src 的尾部有重叠的情况下;
3.memcpy用汇编实现的,效率高一些。

PreTranslateMessage 与WndProc区别:

在MFC中,PreTranslateMessage是虚函数,我们可以重载它来处理键盘和鼠标消息。

在sdk中,这又有所不同,我们必须在回调函数中LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)处理消息:它和PreTranslateMessage起的作用是类似的。只是MFC封装的更好而已。
重载该函数可以实现窗口消息在派发给窗口函数TrnaslateMessage和DispatchMessae()之前的过滤.缺省的实现是完成加速键的翻译.因为您必须在你的重载版本中调用CWinApp:PreTranslateMessage()函数.很显然,在SDK中在TranslateMassage()函数之前来实现该功能.

MFC中PreTranslateMessage是GetMessage(...)函数的下一级操作,即 GetMessage(...)从消息队列中获取消息后,交由PreTranslateMessage()处理,

若其返回FALSE则再交给 TranslateMessage和DispatchMessage处理(即进入WindowProc);

PreTranslateMessage 仅仅是一个类似钩子回调函数(hook callback function) 的东西,给你一个在 TranslateMessage 之前优先处理消息的机会。伪代码:

MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
PreTranslateMessage(&msg);
TranslateMessage(&msg);
DispatchMessage(&msg);
}

如果用SendMessage,则消息直接交到WindowProc处理,所以GetMessage不会取得SendMessage的消息,当然PreTranslateMessage也就不会被调用。

如果用PostMessage,则消息进入消息队列,由GetMessage取得,PreTranslateMessage就有机会进行处理。

Invalidate() -- RedrawWindow() -- UpdateWindow()三个函数有什么异同:

Invalidate()是强制系统进行重画,但是不一定就马上进行重画。因为Invalidate()只是通知系统,此时的窗口已经变为无效。强制系统调用WM_PAINT,而这个消息只是Post(寄送)就是将该消息放入消息队列。当执行到WM_PAINT消息时才会对敞口进行重绘。

UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT。发送即不经过消息队列,直接发送到对应窗口,因此此函数可以立即更新窗口。

RedrawWindow()则是具有Invalidate()和UpdateWindow()的双特性。声明窗口的状态为无效,并立即更新窗口,立即调用WM_PAINT消息处理。

附一段垃圾,只可看一点,别当真:

在view类中,create 是虚函数由框架调用,是用来“生成一个窗口的子窗口”。
oncreate 消息响应函数,是用来“表示一个窗口正在生成”。

某个CWnd的Create函数由当前CWnd的Owner调用, 而在CWnd::Create中,又会调用OnCreate函数,但是实际上这个时候Create函数还没有退出,CWnd的某些部分还没有创建好。所以,在ToolBar::OnCreate中,不能调用CommandToIndex,因为CommandToIndex需要等CToolBar全部创建好之后CToolBar::Create退出)才能被调用,否则返回值一直是-1

oncreate()不产生窗口,只是在窗口显示前设置窗口的属性如风格、位置等,
create()负责注册并产生窗口

create()不是对应于消息wm_create的,oncreate()才是。create()只用于产生窗口,像动态创建控件中的create()一样

SetActiveWindow、SetFocus、 SetForegroundWindow、 BringWindowToTop区别:

SetForegroundWindow()是把一个程序带到前台,是不是激活的不一定。

SetActiveWindow()是激活一个程序。
SetFocus()是如输入框等可以输入的地方得到光标。

BringWindowToTop

http://blog.csdn.net/weiwangchao_/article/details/6923976

免责声明:文章转载自《MFC常见问题解惑》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ADF 第五篇:转换数据Windows下Python虚拟环境安装及新建django项目及简单流程下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

libcurl教程

名称 libcurl 的编程教程 目标 本文档介绍使用libcurl编程的一般原则和一些基本方法。本文主要是介绍 c 语言的调用接口,同时也可能很好的适用于其他类 c 语言的接口。 跨平台的可移植代码 libcurl库背后的开发人员投入了相当大的努力确保libcurl可以在很多不同的系统和环境里工作。 全局的准备 程序必须初始化一些libcurl的全局函数...

0x01 译文:Windows桌面应用Win32开发简介

本节课将简单介绍下使用C++开发Windows桌面应用的一些基础知识  目录: 准备你的开发环境 Windows 代码规范 操作字符串 什么是一个Window? WinMain:程序的入口点 1. 准备你的开发环境 安装 Windows SDK        要用C或者C++开发Windows 程序,你必须安装 Microsoft Windows So...

MFC程序自动生成dump Windbg文件

  在客户机器上如何得到应该程序的详细出错信息, 这里使用drwtsn32,在应用程序崩溃的时候自动将调用栈的信息以文件形式保存在磁盘。   生成dump有drwtsn32, NTSD,CDB等多种工具,drwtsn32 于系统自带。   在项目中使用以下几个步骤: 1. 创建minidmp.h   minidmp.h #pragma once#inc...

单片机用定时器分配任务的程序结构总结

转载请注明本文地址:http://blog.sina.cn/dpool/blog/s/blog_6f2b6ba80101bwka.html?vt=4 http://blog.sina.cn/dpool/blog/s/blog_6f2b6ba80101bwka.html?vt=4本文是2013年写的,后来整理成了系统文章,请访问 http://nicekwe...

Netlink机制详解

使用netlink机制在内核与应用程序之间通信 https://blog.csdn.net/zhongbeida_xue/article/details/79026398 转载:https://blog.csdn.net/zoe6553/article/details/8026033 前一段时间,在开发一个驱动程序的过程中,需要在驱动程序与应用程序之间进行...

kafka入门

问题导读: 1.zookeeper在kafka的作用是什么? 2.kafka中几乎不允许对消息进行“随机读写”的原因是什么? 3.kafka集群consumer和producer状态信息是如何保存的? 4.partitions设计的目的的根本原因是什么? 一、入门 1、简介 Kafka is a distributed,partitioned,rep...