MFC中为什么不用UpdateWindow也可以发送WM_PAINT消息

哈利_蜘蛛侠 2016-10-23 09:57:57
正在学习
《Ivor Horton's Beginning Visual C++ 2013》
这本书,这本书的第二部分是讲MFC的,然后遇到了一个问题。是这样的:
书上在讲在MFC中绘制的时候,是这样说的:如果要让新创造的图形被绘制出来,那么需要做这三件事情:


然而在后面给鼠标的消息做响应的时候,我发现一件奇怪的事情:在LButtonUp()中,在调用InvalidateRect()之后就马上收到了WM_PAINT消息。请问这是怎么回事呢?LButtonUp是这样写的:

void CSketcherView::OnLButtonUp(UINT nFlags, CPoint point)
{
// Make sure there is an element
if (m_pTempElement)
{
// Add the element pointer to the sketch
GetDocument()->AddElement(m_pTempElement);
InvalidateRect(&m_pTempElement->GetEnclosingRect());
m_pTempElement.reset(); // Reset the element pointer
}
}
...全文
768 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
schlafenhamster 2016-11-21
  • 打赏
  • 举报
回复
CDC::GetClipBox virtual int GetClipBox( LPRECT lpRect ) const; Return Value The clipping region’s type. It can be any of the following values: COMPLEXREGION Clipping region has overlapping borders. ERROR Device context is not valid. NULLREGION Clipping region is empty. SIMPLEREGION Clipping region has no overlapping borders. Parameters lpRect Points to the RECT structure or CRect object that is to receive the rectangle dimensions. Remarks Retrieves the dimensions of the tightest bounding rectangle around the current clipping boundary. The dimensions are copied to the buffer pointed to by lpRect.
哈利_蜘蛛侠 2016-11-20
  • 打赏
  • 举报
回复
引用 13 楼 zzz3265 的回复:
创建一个空的窗体, 什么代码都不写, 一样会有WM_PAINT消息 系统的窗体机制本来就是这样的 楼上也说了InvalidateRect 会导致系统会生成WM_PAINT消息, 但是会延后 我上面是解释UpdateWindow 在某些时候的必要性, 也是你引用文章所表达的意思
还有一个问题: View类的OnDraw函数应该怎么写才能够使用UpdateRegion的信息呢?
worldy 2016-10-29
  • 打赏
  • 举报
回复
mfc框架处理WM_PAINT消息代码会调用OnDraw的虚拟函数,你应该重新你自己版本的OnDraw以实现你的展示
schlafenhamster 2016-10-27
  • 打赏
  • 举报
回复
10楼的例子不是说明白了吗?
哈利_蜘蛛侠 2016-10-27
  • 打赏
  • 举报
回复
引用 15 楼 jiu6332356 的回复:
UpdateWindow 是你想让他刷新窗口 所以会调用 你说的调用应该是自动调用的 你可以调试下 只要鼠标移动就会调用onpaint函数
鼠标移动是不会受到WM_PAINT消息的,这个例子就是鼠标按下并拖动就是在绘图,鼠标松开则绘图完成;只有在鼠标松开的时候才会调用OnDraw()函数。
jiu6332356 2016-10-27
  • 打赏
  • 举报
回复
UpdateWindow 是你想让他刷新窗口 所以会调用 你说的调用应该是自动调用的 你可以调试下 只要鼠标移动就会调用onpaint函数
哈利_蜘蛛侠 2016-10-26
  • 打赏
  • 举报
回复
引用 11 楼 zzz3265 的回复:
书上的原文没说清楚导致理解有问题 InvalidateRect 调用时系统不会立即生成WM_PAINT消息, 只是做了一些标记, 然后立即返回了 UpdateWindow 是会更新窗体生成WM_PAINT消息, 等待窗体绘制完成后才返回 如果你在 InvalidateRect 后立即截图可能会看到截取的图像是不对的, 用了UpdateWindow后可以保证图形显示完成
然后这个跟我的问题有何关系呢?我这里并没有调用UpdateWindow呀
Yofoo 2016-10-26
  • 打赏
  • 举报
回复
书上的原文没说清楚导致理解有问题 InvalidateRect 调用时系统不会立即生成WM_PAINT消息, 只是做了一些标记, 然后立即返回了 UpdateWindow 是会更新窗体生成WM_PAINT消息, 等待窗体绘制完成后才返回 如果你在 InvalidateRect 后立即截图可能会看到截取的图像是不对的, 用了UpdateWindow后可以保证图形显示完成
OnTheWay_Boy 2016-10-26
  • 打赏
  • 举报
回复
WM_PAINT 只是一个消息id,调用onpaint函数,view类,隐式转换为ondraw函数 mfc机制认为需要重绘view的时候,就会发送WM_PAINT消息,所以onsize,UpdateWindow等等会发送WM_PAINT消息 但有的时候,用户自定义,需要改变视图,这个时候mfc不知道该不该重绘视图,所以需要用户自己调用Invalidate()等会发送WM_PAINT消息的函数,重绘view 所以是我理解错了楼主的意思,还是楼主应该了解下windows的消息机制
Yofoo 2016-10-26
  • 打赏
  • 举报
回复
创建一个空的窗体, 什么代码都不写, 一样会有WM_PAINT消息 系统的窗体机制本来就是这样的 楼上也说了InvalidateRect 会导致系统会生成WM_PAINT消息, 但是会延后 我上面是解释UpdateWindow 在某些时候的必要性, 也是你引用文章所表达的意思
schlafenhamster 2016-10-25
  • 打赏
  • 举报
回复
UpdateWindow 只有窗口无效时 才 起作用 ,他是用来 窗口 无效后 立即 更新窗口用的。 下面的代码 说明 它的用处 //afxDump << "OnPaint\n"; CDialog::OnPaint();// 这里 对话框 已经 使 m_Pic2 窗口无效了,但 m_Pic2 的重绘 发生在 OnPaint 后! // CStatic m_Pic2; m_Pic2.UpdateWindow();// 所以 我们调用 这个函数 让 m_Pic2 重绘,重绘 后 m_Pic2 窗口有效 CDC *picDC=m_Pic2.GetDC();// 然后 我们 取 m_Pic2 的 客户DC , CRect rc; m_Pic2.GetClientRect(rc); // FillPoints(picDC,rc,0,RGB(0,255,255));// 并在 m_Pic2 上 绘图。 // ReleaseDC(picDC);// 这就是 在 对话框 中 绘制 对话框中的 控件。 }
coolooooooo 2016-10-24
  • 打赏
  • 举报
回复
其实这些问题,你下载一个c++6.0调试跟进去看就懂了
coolooooooo 2016-10-24
  • 打赏
  • 举报
回复
因为响应wm_paint消息函数,函数会调用ondraw
赵4老师 2016-10-24
  • 打赏
  • 举报
回复
建议楼主学会使用Spy++
哈利_蜘蛛侠 2016-10-24
  • 打赏
  • 举报
回复
引用 5 楼 worldy 的回复:
InvalidateRect,本质就是投递WM_PAINT消息到线程的消息队列中,WM_PAINT是低优先级消息,可能会延迟及合并处理 UpdateWindow也是使用WM_PAINT消息但它是调用窗口函数,因此会立即得到执行
原来是这样吗?跟书上说的好像有点不一样啊。不过真这样的话那我就明白了。不过还有问题:书上的view类的OnDraw()函数是这样实现的:

void CSketcherView::OnDraw(CDC* pDC)
{
	CSketcherDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// Draw the sketch
	for (const auto& pElement : *pDoc)
	{
		if (pDC->RectVisible(pElement->GetEnclosingRect()))
			pElement->Draw(pDC);
	}
}
这个是不是并没有用到WM_PAINT消息的update region信息呀?如果是的话,那么怎么利用这个update region信息呢?
worldy 2016-10-24
  • 打赏
  • 举报
回复
InvalidateRect,本质就是投递WM_PAINT消息到线程的消息队列中,WM_PAINT是低优先级消息,可能会延迟及合并处理 UpdateWindow也是使用WM_PAINT消息但它是调用窗口函数,因此会立即得到执行
哈利_蜘蛛侠 2016-10-24
  • 打赏
  • 举报
回复
引用 3 楼 accessysq 的回复:
InvalidateRect(&m_pTempElement->GetEnclosingRect()); 你这里标记的无效区域如果存在,系统就会发送WM_PAINT的。所以你说的问题是很正常的啊。
不可能是每调用一次InvalidateRect()就会发送这个消息吧,因为可能连续多次调用了这个函数的。而且书上也说了,要再调用UpdateWindow()才行的。
  • 打赏
  • 举报
回复
InvalidateRect(&m_pTempElement->GetEnclosingRect()); 你这里标记的无效区域如果存在,系统就会发送WM_PAINT的。所以你说的问题是很正常的啊。
哈利_蜘蛛侠 2016-10-23
  • 打赏
  • 举报
回复
引用 1 楼 qq407311984 的回复:
因为重绘窗口或者刷新窗口都会发送wm_paint消息通知程序
然而我只是松开了鼠标而已。拖动鼠标的过程中不会,尽管这时候也发生了绘制的行为。具体的比较复杂,如果你看过这本书的话就知道了。我觉得就应该是调用了InvalidateRect()的缘故,至于为何没有调用UpdateWindow()就发送了WM_PAINT消息,我觉得可能是在基类的某个地方调用了吧。
coolooooooo 2016-10-23
  • 打赏
  • 举报
回复
因为重绘窗口或者刷新窗口都会发送wm_paint消息通知程序
该代码是基于vc的数据可视化功能 #include #include LRESULT CALLBACK WangProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { WNDCLASS wndcls; wndcls.cbClsExtra=0; wndcls.cbWndExtra=0; wndcls.hbrBackground=NULL;//(HBRUSH)GetStockObject(BLACK_BRUSH); wndcls.hCursor=LoadCursor(NULL,IDC_NO); wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); wndcls.hInstance=hInstance; wndcls.lpfnWndProc=WangProc; wndcls.lpszClassName="王朝帅"; wndcls.lpszMenuName=NULL; wndcls.style=CS_VREDRAW | CS_PARENTDC/* | CS_HREDRAW*/; RegisterClass(&wndcls); HWND hwnd; hwnd=CreateWindow("王朝帅","安徽大学 计算机科学与技术学院 王朝帅",WS_OVERLAPPEDWINDOW, 50,50,600,400,0,0,0,NULL); ShowWindow(hwnd,SW_SHOWNORMAL); UpdateWindow(hwnd); MSG msg; while(GetMessage(&msg,hwnd,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT CALLBACK WangProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { case WM_CHAR: char szChar[20]; sprintf(szChar,"char is '%c'",wParam); MessageBox(hwnd,szChar,"朝帅作品",MB_OK); break; case WM_LBUTTONDOWN: MessageBox(hwnd,"mouse clicked","朝帅作品",MB_OK); HDC hdc; hdc=GetDC(hwnd); TextOut(hdc,0,50,"MFC学习心--朝帅作品",strlen("MFC学习心--朝帅作品")); ReleaseDC(hwnd,hdc); break; case WM_PAINT: HDC hDC; PAINTSTRUCT ps; hDC=BeginPaint(hwnd,&ps); TextOut(hDC,0,0,"MFC培训",strlen("MFC培训")); EndPaint(hwnd,&ps); break; case WM_CLOSE: if(IDYES==MessageBox(hwnd,"是否真的结束?","朝帅作品",MB_YESNO)) { DestroyWindow(hwnd); } break; case WM_DESTROY: PostQuitMessage(1); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; }
#include <windows.h> #include #include #include LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInst,LPSTR lpszCmdLine,int nCmdShow) { HWND hwnd; MSG Msg; WNDCLASS wndclass; char lpszClassName[] = "基本绘图"; char lpszTitle[]= "My_Drawing"; wndclass.style = 0; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor( NULL, IDC_ARROW) ; wndclass.hbrBackground =(HBRUSH) GetStockObject( WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = lpszClassName ; if( !RegisterClass( &wndclass)) { MessageBeep(0) ; return FALSE ; } hwnd = CreateWindow ( lpszClassName, lpszTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while( GetMessage(&Msg, NULL, 0, 0)) { TranslateMessage(&Msg) ; DispatchMessage(&Msg) ; } return Msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; HPEN hP; //定义画笔句柄 HBRUSH hB; //定义画刷句柄 switch(message) { case WM_PAINT: //通过响应WM_PAINT消息完成绘图工作 hdc=BeginPaint(hwnd,&ps); //取得设备环境句柄 SetMapMode(hdc,MM_TEXT); //设置映射模式 Rectangle(hdc,130,60,270,200); //使用当前画笔绘制矩形并填充 hB=CreateHatchBrush(HS_CROSS,RGB(255,0,0)); //自定义一个红色网格状画刷 SelectObject(hdc,hB); //更新画刷为自定义画刷 Ellipse(hdc,130,70,270,190); //绘制椭圆并填充 hP=CreatePen(PS_DASHDOT,1,RGB(0,255,0)); //宽度为1的绿色画笔,画点划线 SelectObject(hdc,hP); //更新画笔为自定义画笔 MoveToEx(hdc,100,130,NULL); //使用当前画笔绘制轴线 LineTo(hdc,300,130); MoveToEx(hdc,200,30,NULL); LineTo(hdc,200,230); EndPaint(hwnd,&ps); //释放设备环境句柄 DeleteObject(hP); //删除画笔句柄 DeleteObject(hB); //删除黑色画刷句柄 break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,message,wParam,lParam); } return 0; }
主要是模拟初的物理实验的有源代码,可供初学者使用! 采用 MFC 编制 MVC 模式之球体演示程序 作者:haykey 下载源代码   在传统面向过程的程序设计,往往采用 Input-Processing-Output 模式,这“归功”于 DOS 操作系统的单任务。当 Windows 图形界面 OS 出现后,MVC(Model-View-Controller)模型更适合 Windows 图形界面程序的设计,它将数据处理和数据显示分离开,使维护,扩展系统更加灵活 。其,View:负责 显示数据,它从Model处获得数据然后显示。当然,一个Model会有用户可从不同角度来观察的多个View。Model:存储数据以及对数据进行各种运算和处理 。Controller:负责接受用户输入,并且把用户输入转换成对 Model 的操作。因此Controller 可能会修改 Model 的数据,当数据修改后,更新 View。其结构示意图如下:   一直采用MFC编程的朋友可能不太熟悉它,这是因为MFC的文档视图结构就是基于MVC的高层结构,这蒙蔽了我们的双眼。虽然MS替我们做了,我们还是有必要接触它,以在SDK or 其他地方有的放矢。我做了一个球体演示的例子,其界面如下:   左侧两个表面积和体积Edit让使用者从文本的角度精确地观察,我们称其为TextView。右侧为从CStatic派生的CGraphicView,使得人们可直观地观察Sphere.对话窗口CMVCSphereDlg是控制器,来获取用户的键盘输入(输入半径后回车)和在Static上的鼠标点击与拖动(可动态调整球体半径并实时反馈球体变化)而CSphere类是模型,存储了球体半径和计算表面积,计算体积等处理半径数据的操作.   现在让我们详细看看代码,来感受下Model,View,Controller之间如何关联,如何协同工作的。 class CSphere { public: ... .... //更新Graphic-VIEW BOOL UpdateGraphicView(HWND hWnd,const CRect &rect,BOOL bErase); //更新Text-VIEW void UpdateTextView(); //外界Controller的接口:设置球体半径 void SetRadius(float r); private: //球体半径 float m_fRadius; //计算球体表面积 float CalculateArea(float radius); //计算球体体积 float CSphere::CalculateVolumn(float radius); };   这里面 UpdateTextView,UpdateTextView 就是当用户输入新半径或拖动鼠标 Controller 捕获后通知 Model,Model 通知两个View更新显示 。具体代码如下: BOOL CSphere::UpdateGraphicView(HWND hWnd,const CRect &rect,BOOL bErase) { //data format examination if(!::IsWindow(hWnd)||::IsRectEmpty(&rect)) { AfxMessageBox("View is not created by now or rect is empty"); return false; } //get the window pointer from window handle CWnd *pView = CWnd::FromHandle(hWnd); if(pView == NULL) return false; //set graphic view''s radius in order to painting ((CGraphicView*)pView)->SetRadius(m_fRadius); bPaintSphere = true;//set paint tag true //repaint if(!::InvalidateRect(hWnd,&rect,bErase)&& !::UpdateWindow(hWnd)) { AfxMessageBox("UpdateView failed"); return true; } pView = NULL; return false; } void CSphere::UpdateTextView() { CMVCSphereDlg *parent = (CMVCSphereDlg *)AfxGetMainWnd(); CWnd *wnd1 = parent->GetDlgItem(IDC_SURFACE); CWnd *wnd2 = parent->GetDlgItem(IDC_VOLUMN); CString str; str.Format("%.2f平方米",CalculateArea(m_fRadius)); wnd1->SetWindowText(str); str.Empty(); str.Format("%.2f立方米",CalculateVolumn(m_fRadius)); wnd2->SetWindowText(str); } CGraphicView绘图关键代码如下: void CGraphicView::OnPaint() { ... ..... if(!bPaintSphere) dc.DrawText("球体演示",rect,DT_VCENTER|DT_CENTER|DT_SINGLELINE); else { int r=(int)m_radius;//半径取整 CPoint MiddlePoint = rect.CenterPoint();//以矩形框的心为球心 int x=MiddlePoint.x; int y=MiddlePoint.y; oldpen = (CPen*)dc.SelectObject(&solid_pen); oldbru = (CBrush*)dc.SelectObject(&brush); dc.Ellipse(x-r,y-r,x+r,y+r); //先画一个圆形 dc.SelectObject(&dash_pen); dc.Arc(x-r/2,y-r,x+r/2,y+r,x,y-r,x,y+r); //再画4个半圆弧 dc.Arc(x-r/2,y-r,x+r/2,y+r,x,y+r,x,y-r); dc.Arc(x-r,y-r/2,x+r,y+r/2,x-r,y,x+r,y); dc.Arc(x-r,y-r/2,x+r,y+r/2,x+r,y,x-r,y); ... ... } } 关于控制器CMVCSphereDlg响应用户输入半径回车核心代码如下: BOOL CMVCSphereDlg::PreTranslateMessage(MSG* pMsg) { UpdateData(); //violation examination if(m_r100) { AfxMessageBox("半径输入范围(0---100)"); return false; } if(pMsg->message == WM_KEYDOWN) if(pMsg->wParam == VK_RETURN)//回车 { CRect rect; m_GraphicView.GetClientRect(rect); m_Sphere.SetRadius(m_r);//把用户输入转换成对Model的操作 m_Sphere.UpdateTextView();//更新View m_Sphere.UpdateGraphicView(m_GraphicView.GetSafeHwnd(),rect,true);//更新View return true; } ... ... } 响应鼠标拖动核心代码如下: void CMVCSphereDlg::OnMouseMove(UINT nFlags, CPoint point) { CRect rect; m_GraphicView.GetClientRect(rect); CPoint middlepoint = rect.CenterPoint(); //if click on the graphic view if(rect.PtInRect(point)&&bIsDragging) { double dbDistance2 = (point.x-middlepoint.x)*(point.x-middlepoint.x)+(point.y-middlepoint.y)*(point.y-middlepoint.y); double dbDistance = sqrt(dbDistance2); if(dbDistance>100.) dbDistance = 100.; m_r = (float)dbDistance; //update radius edit UpdateData(false); m_Sphere.SetRadius(m_r); m_Sphere.UpdateTextView(); m_Sphere.UpdateGraphicView(m_GraphicView.GetSafeHwnd(),rect,true); } ... ... } 该程序功能简单,只是示例性说明采用 MFC 如何实现MVC模型,就当抛砖引玉了。具体实现参考源代码例子。

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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