“分析对话框和文档/视图结构运行机制及心得体会。”这是我们作业,回答的越多越好 200

 我来答
戈绮兰28
2013-06-12 · TA获得超过1266个赞
知道小有建树答主
回答量:588
采纳率:50%
帮助的人:142万
展开全部
在MFC中,CView及其派生类将显示及其相关的操作做的很好,比如在程序中
如果我们的视图类继承CScrollView了那么我们不用写任何代码该视图就能够
具有滚动的响应机制,能够自动根据设置的滚动范围和窗口实际大小确定是
否显示滚动条(水平或者竖直的),CEditView,CRichEditView等也提供了
相应的在不同实用范围内的一些基本操作处理。
通常在程序中,我们需要处理并且显示一些数据,将显示部分如果放到视图中
的话,我们将可以忽略大部分的窗口交互的细节,将注意力关注在数据的显示
上。
本文是在处理如下的情况中提出的:在数据采集和分析的时候,常常需要观看很
多的数据。我们不可能在程序的主界面上显示。我们需要一个弹出窗口来显示,
可以随时的关闭之,也就是说这样的窗口应该动态的生成,数量不定(这样限制
了我们使用切分视图的选择)。对于这些弹出窗口,我们可以自己写一个CWnd的
派生类,自然这是完全可以的,但是这样要求你可能处理滚动等等的一些列消息(我
曾经在一个显示图像灰度直方图、以及二值图像投影等中遇到过要求这样显示的
情况,当时就是自己从CWnd派生了一个类来显示数据,由于图像比较大,一屏显
示不完整,所以要给窗口加滚动条,这样在处理这些消息上花费了我大量的时间,
而真正显示数据的代码却很少)。因此我也在一直想使用MFC已经封装的非常好的
CView类及其派生类来显示数据。由于我主要用到的就是CScrollView,因此我也
以它为列来讲述怎么用视图。

基于对话框和SDI结构的实现上有不同细节, 必须分开了说:

1、如果你的程序是一个基于对话框的程序的话(指的是如利用MFC向导生成的对话
框程序之类),那么事情就很简单:
首先,很自然的在Insert/New Class中插入一个类,选择CScrollView做基类
假设你自己的视图类命名为CMyScrollView。
在对话框(不论是主界面的对话框还是新弹出的对话框,都一样)的OnInitDialog
中加上如下的代码:

CRect rectWndClient;
GetClientRect(&rectWndClient);
CRuntimeClass *pViewRuntimeClass=RUNTIME_CLASS(CMyScrollView);
CMyScrollView *pView=(CMyScrollView*)pViewRuntimeClass->CreateObject();
pView->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,
rectWndClient,
this,123,NULL);
pView->OnInitialUpdate();

说明:
1)、由于CView及其派生类的构造函数是保护成员,所以采用RuntimeClass方式来构造
对对象,
2)、在Create函数中第一个参数必须有WS_CHILD属性,表明视图是一个子窗口
3)、第四个参数rectWndClient可以视情况,改变,因为你可能只想在对话框的某个区域
创建视图。
4)、第五个参数表明把对话框作为视图的父窗口。
5)、第六个参数是视图的ID可以任意指定一个整数

在创建之后,如果不掉用OnInitialUpdate的话会有错误,这是因为CScrollView类有个保护成员
m_nMapMode,它在CScrollView的构造函数中被置为 0,如下
m_nMapMode = MM_NONE;//在VIEWSCRL.CPP的115行,MM_NONE是宏, 定义为0

下面是错误的分析原因:
通常窗口是在响应WM_PAINT的时候绘制客户区。所有的视图都派生自CView,他们的的WM_PAINT响应函数如下

void CView::OnPaint() //在VIEWCORE.CPP 176行
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}

可见,在绘图之前虚函数OnPrepareDC会被调用,在CScroolView中重写了这个虚函数,这个虚函数的
开头部分如下

ASSERT_VALID(pDC);

#ifdef _DEBUG
if (m_nMapMode == MM_NONE)
{
TRACE0("Error: must call SetScrollSizes() or SetScaleToFitSize()");
TRACE0(" before painting scroll view. ");
ASSERT(FALSE);
return;
}
#endif //_DEBUG

可见如果m_nMapMode是0(在构造函数中被默认置为0)的话将在调试程序的时候错误
所以通常应该在调用OnInitialUpdate的时候(注意:OnInitialUpdate被声明为CView的public成员函数,但是用MFC向导产生自己派生类的OnInitialUpdate函数的时候被声明成了protect,你可以将它改成public,然后调用OnInitialUpdate来初始化自己的视图,或者在构造函数中设置m_nMapMode为自己想要的映射模式)初始化一些东西,并且设置m_nMapMode的正确值,m_nMapMode的一个典型值就是 MM_TEXT(关于映射模式可以看有关文章或者书详细介绍)。

上面讲述了如何在对话框中创建视图和相关函数调用的原因。 有一点要讲的是如果你的对话框加上了
最大化按钮的话,你可能想要在最大话对话框的时候让视图也跟着变化,所以你要处理对话框的
WM_SIZE消息。在其中你依据当前对话框的实际大小来调整视图大小。这样你就需要视图的指针
因此你可能的程序应该是这样的

// 在对话框头文件中,声明一个指向你的视图的指针
CMyScrollView * m_pView;
// 在构造函数中将指针赋值NULL
m_pView = NULL;

//在OnInitDialog创建视图

CRect rectWndClient;
GetClientRect(&rectWndClient);

CRuntimeClass *pViewRuntimeClass=RUNTIME_CLASS(CMyScrollView);

m_pView=(CMyScrollView*)pViewRuntimeClass->CreateObject();
m_pView->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,
rectWndClient,
this,123,NULL);
m_pView->OnInitialUpdat();
// 在OnSize中改变视图大小
if (m_pView) //注意在第一次调用OnSize之前视图还没有创建,这也是要在构造函数中将
{ // m_pView)置NULL的原因
m_pView->SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER);
}

// 注意我们在上面用new的方法【具体是在pViewRuntimeClass->CreateObject()的时候】产生了一个
CMyScrollView对象,我们的程序并没有调用delete的语句,这样会不会产生内存泄漏呢?
答案是否定的:窗口接受最后一个消息是WM_NCDESTROY,在OnNcDestroy()中CView调用了PostNcDestory这个虚函数
我们的类和CScrolView都没有重写PostNcDestory,因此他调用CView的PostNcDestory函数 在这个函数中调用了
delete this; CView自己删除了自己 所以不会有内存泄漏。
2、在SDI(单文档)程序中的对话框中使用CView
在SDI中的弹出对话框中创建视图,如果你用,上面的方法1做的话,只要你的鼠标不点击视图,就不会有问题(这样的要求
恐怕是没有人能够接受的^_^)。
我们先分析这样原因。在视图上点击鼠标会执行如下函数
int CView::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
int nResult = CWnd::OnMouseActivate(pDesktopWnd, nHitTest, message);
if (nResult == MA_NOACTIVATE || nResult == MA_NOACTIVATEANDEAT)
return nResult; // frame does not want to activate
CFrameWnd* pParentFrame = GetParentFrame(); // 这里 **********(A)
if (pParentFrame != NULL)
{
// eat it if this will cause activation
ASSERT( pParentFrame == pDesktopWnd
|| pDesktopWnd->IsChild(pParentFrame)); //还有这里********(B)
// 省略
……
}
return nResult;
}

上面的A B 两处是我们找到的问题所在,我们最后出错的地方是在B处,但原因是在A.
当是对话框程序的时候如前面1,返回值pParentFrame是0,不会执行B的断言,也就不会有错。
但是在上面SDI况下,A返回的pParentFrame是应用程序主框架窗口而pDesktopWnd是视图所在的对话框窗口,
我们分析B的代码,发现此时pParentFrame == pDesktopWnd 不成立。而且pParentFrame(此时是主窗口)
也不是pDesktopWnd(此时是对话框)的子窗口,所以ASSERT断言为FALSE ,产生了错误。
单看上面的代码,及其原因我们还是不能决定如何使用CScrollView。
但是观察A处GetParentFrame的实现,却为我们打开了希望之门:
GetParentFrame是从CWnd继承而来

CFrameWnd* CWnd::GetParentFrame() const
{
if (GetSafeHwnd() == NULL) // no Window attached
return NULL;
ASSERT_VALID(this);
CWnd* pParentWnd = GetParent(); // start with one parent up
while (pParentWnd != NULL)
{
if (pParentWnd->IsFrameWnd())
return (CFrameWnd*)pParentWnd;
pParentWnd = pParentWnd->GetParent();
}
return NULL;
}
分析这段代码,发现他的目的就是在他的祖先窗口中上溯,直到找到一个是FrameWnd类型的窗口之后, 返回这个窗口对象的指针(如果没有的话,返回NULL),在SDI中这样的窗口是一定存在的(主框架窗口便是!)。
我们从前面的分析中知道,pDesktopWnd是对话框窗口,他一定不是一个框架窗口,所以断言的前一部分不能满足 只有考虑后一个,即 pDesktopWnd->IsChild(pParentFrame),先前已经说了主框架不可能是对话框的子窗口,所以要是 pDesktopWnd->IsChild(pParentFrame)满足,即要存在一个CFrameWnd类型的窗口是对话框的子窗口,也是视图的 父窗口(因为GetParentFrame是从视图开始上溯寻找的),因此我们可以在对话框和视图中间嵌入一个CFrameWnd或者起派生类 的对象。 由于CFrameWnd本身不用什么操作,我们就不用派生一个自己的CMyFrameWnd了,直接用CFrameWnd, 因此可能的代码是这样的
CRect rectWndClient;
GetClientRect(&rectWndClient);
CFrameWnd *pFrame= new CFrameWnd();
pFrame->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,rectWndClient,this);
CRuntimeClass *pViewClass=RUNTIME_CLASS(CMyScrollView);
CMyScrollView *pView=(CMyScrollView*)pViewClass->CreateObject();
pView->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,
rectWndClient,
pFrame,123);
pView->OnInitialUpdate();

为了能够动态的随对话框的大小改变视图的大小,可以把pFrame和pView作为对话框的成员
我自己的某此使用过程中实现如下
// 在对话框头文件中,声明一个指向你的视图的指针
CFrameWnd * m_pFrame ;
CMyScrollView * m_pView;

// 在构造函数中将指针赋值NULL
m_pFrame = NULL;
m_pView = NULL;

//在OnInitDialog创建视图

CRect rectWndClient;
GetClientRect(&rectWndClient);
m_pFrame= new CFrameWnd();
m_pFrame->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,rectWndClient,this);

CRuntimeClass *pViewClass=RUNTIME_CLASS(CMyScrollView);
m_pView=(CMyScrollView*)pViewClass->CreateObject();

m_pView->Create(NULL,NULL,WS_VISIBLE|WS_CHILD,
rectWndClient,
m_pFrame,123);
m_pView->OnInitialUpdate();

// 在OnSize中改变视图大小
//注意在第一次调用OnSize之前视图还没有创建,这也是要在构造函数中将
// m_pFrame,m_pView 置NULL的原因
if (m_pFrame && m_pView)
{
m_pFrame->SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER);
m_pView->SetWindowPos(NULL,0,0,cx,cy,SWP_NOZORDER);
}
追问
好像和我的问题没什么关系
刘庆宇19900517
2014-02-19
知道答主
回答量:1
采纳率:0%
帮助的人:1349
展开全部
这个问题我最近也遇到过
解决办法:

在相你的的视图类中重写基类CSrollView的Create函数,在Create函数中
添加SetScrollSizes ( MM_TEXT, CSize(100, 100));重新编译就OK了

具体操作如下; 1、 右键你的视图类
2、 添加重写函数create
3、 在create'函数中添加SetScrollSizes ( MM_TEXT, CSize(100, 100));
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式