OnDraw和OnPaint

zhuyf87 2014-07-27 10:29:09
用vs2012新建了一个SDI单文档程序,在CXXXView::OnDraw函数中画了一个圆。
void CFirstSDIView::OnDraw(CDC* pDC)
{
pDC->SelectStockObject(GRAY_BRUSH);
pDC->Ellipse(CRect(10, 10, 100, 100));
}

没有问题,圆正常画出来了。然后又添加了WM_PAINT消息处理函数OnPaint。
void CFirstSDIView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CView::OnPaint();
}

这时候圆就画不出来了,或者可能是画出来了,又被覆盖了。如果我去掉OnPaint()中的CPaintDC dc(this); 这一行,或者把这一行放到CView::OnPaint()下面就可以画出圆来了。我对这块有点不太明白其中的道理,请大家帮忙解释一下。谢谢。我觉得可能是CPaintDC 的构造函数中做了什么事情,产生的影响。
当然也可以去掉CView::OnPaint();,然后显示调用OnDraw。
void CFirstSDIView::OnPaint()
{
CPaintDC dc(this); // device context for painting
OnDraw(&dc);
}

这样也可以。我只是不明白刚才不能显示的具体原因。
...全文
183 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
mfs 2014-07-27
  • 打赏
  • 举报
回复
我是一个初初学者,单从代码上分析,可是一点可取性没有。学习来的。请指正。 void CFirstSDIView::OnPaint() { CPaintDC dc(this); // device context for painting CView::OnPaint(); } 你把 CPaintDC dc(this); 移到 CView::OnPaint(); 这句后面,相当于没有定义。因为执行完CView::OnPaint(); 你再定义个局部对象到最后最这对象生命周期到了又没了。 void CFirstSDIView::OnPaint() { CPaintDC dc(this); // device context for painting OnDraw(&dc); } 这个能画出来是因为 你把HDC 传给了OnDraw调用,所以你还是用的OnDraw函数。不知道你本意是想做什么。 我是乱分析的,见笑了。
我喝多了 2014-07-27
  • 打赏
  • 举报
回复
zhuyf87 2014-07-27
  • 打赏
  • 举报
回复
谢谢大家的认真回帖,特别是2楼和5楼。我又看了一下代码:
void CFirstSDIView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CView::OnPaint();
}
CView::OnPaint()的源码是这样的:
void CView::OnPaint()
{
	// standard paint routine
	CPaintDC dc(this);
	OnPrepareDC(&dc);
	OnDraw(&dc);
}
所以相当于连续构造了两个CPaintDC 对象。CPaintDC 构造函数的源码:
CPaintDC::CPaintDC(CWnd* pWnd)
{
	ASSERT_VALID(pWnd);
	ASSERT(::IsWindow(pWnd->m_hWnd));

	if (!Attach(::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)))
		AfxThrowResourceException();
}
里面调用了全局的::BeginPaint,BeginPaint在MSDN上的解释如下: This function prepares the specified window for painting and fills a PAINTSTRUCT structure with information about the painting. The BeginPaint function sets the clipping region of the device context to exclude any area outside the update region. The update region is set by the InvalidateRect function and by the system after sizing, moving, creating, scrolling, or any other operation that affects the client area. Each call to BeginPaint must have a corresponding call to the EndPaint function. 所以相当于连续调用了两次::BeginPaint,而中间没有EndPaint 。MSDN强调了,这么用不允许的。 也就是要么就不添加OnPaint,就在OnDraw里面画。添加了OnPaint,就直接在OnPaint画。或者:
void CFirstSDIView::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	//一些绘图代码
	OnDraw(&dc);
}
hhhh63 2014-07-27
  • 打赏
  • 举报
回复
引用 4 楼 schlafenhamster 的回复:
2. virtual void OnDraw(CDC* pDC)=0; // overridden to draw this view 3. 默认 是 空操作
LZ重写了OnDraw,所以调用是有内容的。
hhhh63 2014-07-27
  • 打赏
  • 举报
回复
以下是MFC自动生成的OnPaint处理方法

void CChildView::OnPaint() 
{
	CPaintDC dc(this); // 用于绘制的设备上下文
	// TODO:  在此处添加消息处理程序代码
	// 不要为绘制消息而调用 CWnd::OnPaint()
}
注意最后一句,不要为绘制消息而调用 CWnd::OnPaint(),意思是不要调用基类的OnPaint(),为什么这样呢?先介绍一个容易被忽略的重要概念,重画区,举例说明,如果程序只要改变窗口中的一小块,调用方法invalidateRect(rc),其中rc就是重画区(经常翻译成失效区),在随后产生的OnPaint事件中即使程序重画了全部窗口,Windows系统也只把rc规定的重画区反映到屏幕上,其它部分不变。言规正传,CPaintDC dc(this)执行后会删除窗口重画区,基类的CView::OnPaint会再次调用CPaintDC dc(this),因为没有重画区,也就不起作用了,所以MFC向导提示“不要为绘制消息而调用 CWnd::OnPaint()”。 根据以上说明分析一下LZ的代码: CPaintDC dc(this); // device context for painting CView::OnPaint(); 第一行,CPaintDC dc(this)已经删除了重画区,第二行CView::OnPaint()里面虽然调用了OnDraw,但因为没有重画区,反映不到屏幕上,所以不起作用。 另一段代码: CPaintDC dc(this); // device context for painting OnDraw(&dc); 在有重画区的窗口中建立的画图dc,所以OnDraw(&dc);能画出来。 再补充说明两点: 1、窗口建立时整个用户区都是重画区,所以能绘出所有元素。 2、重画窗口时先取重画区,然后判断要画的元素是否在重画区中,如果不在就不画,大大提高效率,当然这是高级绘图,显示比较复杂的界面,要求比较高时用。 // 取重画区 CRect rcClip; dc.GetClipBox( rcClip );
schlafenhamster 2014-07-27
  • 打赏
  • 举报
回复
2. virtual void OnDraw(CDC* pDC)=0; // overridden to draw this view 3. 默认 是 空操作
schlafenhamster 2014-07-27
  • 打赏
  • 举报
回复
///CView默认的标准的重画函数 void CView::OnPaint() { CPaintDC dc(this); OnPreparDC(&dc); OnDraw(&dc); //调用了OnDraw } ++
csfranklin 2014-07-27
  • 打赏
  • 举报
回复
可能是因为未释放DC: int ReleaseDC( HWND hWnd, // handle to window HDC hDC // handle to DC ); After painting is complete, the ReleaseDC function must be called to release the device context. Not releasing the window device context has serious effects on painting requested by applications. ------------------------------------------------------------------------ void CFirstSDIView::OnPaint() { CPaintDC dc(this); // 定义的第一个DC变量 CView::OnPaint(); // 运行OnPaint()时,第一个DC变量仍然有效,未释放DC资源。 } ///CView默认的标准的重画函数 void CView::OnPaint() { CPaintDC dc(this); // 定义的第二个DC变量 OnPreparDC(&dc); OnDraw(&dc); // 使用定义的第二个DC变量调用OnDraw,第一个DC变量和第二个DC变量同时占用DC资源,于是产生绘图问题。 } “如果我去掉OnPaint()中的CPaintDC dc(this); 这一行,或者把这一行放到CView::OnPaint()下面就可以画出圆来了。” 在这两种情况下,CView::OnPaint()调用OnDraw()时,使用的仍然是重画函数内部定义的DC变量,但此时并不存在其他DC变量,或其他DC变量在OnPaint()结束后才生成(OnPaint()结束后,其内部定义的DC变量已释放了DC资源),所以不产生绘图冲突问题。 ------------------------------------------------------------------------ void CFirstSDIView::OnPaint() { CPaintDC dc(this); // 定义的一个DC变量 OnDraw(&dc); // 使用定义的同一个DC变量调用OnDraw,只有一个DC变量占用DC资源,不会产生绘图问题。 } ------------------------------------------------------------------------ 参考资料:http://blog.csdn.net/tracing/article/details/2806156 当视图变得无效时(包括大小的改变,移动,被遮盖等等),Windows 将 WM_PAINT 消息发送给它。该视图的 OnPaint 处理函数通过创建 CPaintDC 类的DC对象来响应该消息并调用视图的 OnDraw 成员函数。通常我们不必编写重写的 OnPaint 处理成员函数。 ///CView默认的标准的重画函数 void CView::OnPaint() { CPaintDC dc(this); OnPreparDC(&dc); OnDraw(&dc); //调用了OnDraw } 既然OnPaint最后也要调用OnDraw,因此我们一般会在OnDraw函数中进行绘制。

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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