首页 | 社区 | 博客 | 招聘 | 文章 | 新闻 | 下载 | 读书 | 代码
亲,您未登录哦! 登录 | 注册

文档与视图的建立和关联

打印文章

分享到:
作者:王家文  湖南大学计算机通信系  

本文讨论一下单文档与多视的问题,主要介绍一下笔者在学习VC++6.0过程中探索出的一些个人经验,并给出了实现它们的的主要程序框架。

一.建立文档与视图:

在Projects选MFC Appwizard(exe),键入工程名,点ok后选单文档,选择默认值,在第四步时,在Advanced…里的Window Styles的Use split window前打√。

二.文档多视:

当我们需要从不同的角度来看单文档的内容时,这就要我们实现多视了,多视有两种实

现方案:静态的和动态的。这就根据不同的需要来实现了。动态的方法一般在程序中已经实现了,自动生成的源代码:

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,

       CCreateContext* pContext)

{

       return m_wndSplitter.Create(this,

              2, 2,               // TODO: adjust the number of rows, columns

              CSize(10, 10),      // TODO: adjust the minimum pane size

              pContext);

}

而静态的方法却麻烦一点,下面我详细介绍一下:

首先我们先插入一个类:class CMyView : public ClistView

在MainFrm.cpp加入#include "MyView.h"

在MainFrm.h中声明CSplitterWnd m_splitterWnd; //声明分隔器对象,已经自动生成了。

在函数CMainFrame::OnCreateClient里加入下面一些代码,只要把原有的代码删去。

If(!m_splitterWnd.CreateStatic(this,1,2,WS_CHILD | WS_VISIBLE))//一行两列

return false;//不成功就返回

else

{

m_splitterWnd.CreateView(0,1,RUNTIME_CLASS(CMyEditView),CSize(100,300),pContext);
m_splitterWnd.CreateView(0,0,RUNTIME_CLASS(CMyView),CSize(100,100),pContext);

// CmyEditView和CmyView是自己定义的视图类。
//CSize(100,300)是初始化时的大小
return TRUE;//成功的返回值

}

这样程序一旦执行就会把窗口分割成左右两部分。

但是有时我们需要实现这样的功能:一旦发出一个消息时,我要使其中的一个视图消失,研究一下,可以删去或者隐藏这个视图,相对应恢复时就要重建或者显示这个视图,具体的实现在后面再详解吧!

由于我们有时分割视图,不是很有规则的1*2,3*1,2*2等形式,而是在分割的视图里再分割,如图:

视图二CviewView2
视图一CviewView1

视图三CviewView3


声明一个分隔器对象:在MainFrm.h中声明CSplitterWnd m_splitterWnd1

代码为:
  m_wndSplitter1.CreateStatic( &m_wndSplitter,2, 1, WS_CHILD | WS_VISIBLE,
  m_wndSplitter.IdFromRowCol(0, 0));  // 创建各个视
  m_wndSplitter.CreateView(0, 1,RUNTIME_CLASS(CviewView1), CSize(0, 0), pContext);
  m_wndSplitter1.CreateView(0, 0,RUNTIME_CLASS(CviewView2), CSize(0, 0), pContext);
  m_wndSplitter1.CreateView(1, 0, RUNTIME_CLASS(CviewView3), CSize(0, 0), pContext);

......

如果使用自由定义的视图大小来实现呢?

       SIZE size;                                  

       CRect rect;     

       GetClientRect(&rect);  

       size.cx = rect.right/2 ;

       size.cy = rect.bottom/2;

只要把size放在Csize(0,0)所在的位置就可以了,用这个方法我们就可以在程序中自由地实现视图窗口大小的变化了。
到了这里又有一个问题提出来了,我们不光删去或者隐藏视图,有时只是简单的视图之间的切换,也可以说有的一个框架包含多个视,而在任何时刻只能显示其中的一个视,还是后面再说吧!

三.获得视图指针。

获取视图的指针作为单独的一点来讲,自然有它的重要性,视图与文档,特别在文档与不相干的视图间,视图与视图间的消息传递及互相之间的调用函数或者变量的时候就要获取视图或文档的指针。

a)         获取文档的指针:

与文档相连的视图可以直接获取,这里就不说了,但与文档一点关系都没有的视图要想获取文档的指针就不可以直接获取了。

              CMyEditDoc * pDoc=(class CMyEditDoc *)GetDocument();    

上面就是获取的方法。

b)        获得视图指针:

                         i.              在其中的一个视图中获取其它视图的指针:

CMainFrame* MainFrame1=(CMainFrame*)this->GetParent()->GetParent();

        CMyEditView* pView=(CMyEditView*)MainFrame1->m_wndSplitter.GetPane(0,1);

This指针代表当前视图的指针,它通过获取CmainFrame的指针再去获取视图的指针,这样有个好处在于可以获得任意视图的指针。

还有一种方案可以实现,但是它有个缺点,你只能把获得的指针作为Cview的指针看待,这样你就不能实现特殊视图的功能了。

          CMyEditDoc * pDoc=GetDocument();

       CView * pView;

          POSITION pos=pDoc->GetFirstViewPosition();

          if(pos!=NULL)

       {

              pView=pDoc->GetNextView(pos);

}

在{}里加入多个pView=pDoc->GetNextView(pos);(原因我也说不出来)就可以获得你所要得视图。

如果你在开始就定义你所接触视图的指针,只不过没有初始化而已,就方面了好多。

m_pEditViewright =( CMyEditView* )m_splitterWnd.GetPane(0,1);// m_pEditViewright已定义。

                       ii.              在CmainFrame获取视图的指针:

CMyEditView* pView=(CMyEditView*)this->m_wndSplitter.GetPane(0,1);

这样就行了,同样在文档里获取视图的方法也可以类似,这里就不重复了,但我强调一点要注意头文件的加入。

四.    单文档多视的实现:

a)         单框架窗口中的多视:

获取你要处理的两个视图指针: pViewAdd,pViewRemove:

// 显示活动视而隐藏非活动视
  pViewAdd->ShowWindow(SW_SHOW);
  pViewRemove->ShowWindow(SW_HIDE);
  // 将新的活动视连接到文档,并断开原来的视与文档的连接。
  pDoc->AddView(pViewAdd);
  pDoc->RemoveView(pViewRemove);
  SetActiveView(pViewAdd);
  RecalcLayout();//告知当前分割窗口,重新显示。

方法二你可以把其中的一个视图移到看不到的地方,活动视变换时就是移动的变换。

方法三你可以先删去其中的一个视图,需要时再重建,然后删去那个视图。

这两个方案在下面再详细说明吧!

b)        单文档分割视图的某分割区的多视的实现

                         i.              移动视图

获取视图指针pView

   pView->SetWindowPos(0, 0, 1000, 1500, 1000, SWP_SHOWWINDOW);

这样移动后在当前窗口下你一般是看不到你移动的视图,但当你改变窗口的大小时,有什么情况发生,没有达到你的期望吧,行,你在OnSize这个函数里加入一些代码,比较简单我就不说了。

                       ii.              删除视图

m_wndSplitter1.DeleteView(0,0); // 删除原来的视

运行一下程序还可以,发现我们删除的视图上点击无事件发生,而且出现中断程序。显然只有这句话还不行的,这就要按照你的要求把窗口重新分割一下或者使用一个新的视图来填充一下。

重新分割可以用CMainFrame::OnCreateClient里的代码的方法实现它,在这里我就不重复了。

五.视图间的通信:
在前面我已经把获取视图的指针作为一大点来解释,其实在不要讲很多大家就会明白的,因为你不管是关联还是不关联的视图,你只要获取它的指针就可以对它进行任意的操作,甚至它的私有函数(当然不能直接了)。下面我就举一例。

在CviewView1里加入一个公共函数void simple();

如果你要实现的一些功能就可以写在这个函数里

void CviewView1::simple()

{

       CClientDC dc(this);

dc.TextOut(0,0,"我要实现视图间的通信,我一定会成功的!");

}

void CviewView2::OnLButtonDblClk(UINT nFlags, CPoint point)

{

CMainFrame* MainFrame1=(CMainFrame*)this->GetParent()->GetParent();

CMyEditView* pView=(CMyEditView*)MainFrame1->m_wndSplitter.GetPane(0,1);

pView->simple();

}

六.CSplitterWnd的扩展:

为什么要扩展CSplitterWnd呢?这是从整个界面来想的,当你分割窗口之后就不希望拖动分割条时它位置的改变,你也需要在分割条上有希望的颜色的出现,这时就要扩展CSplitterWnd一下,其实扩展东西很多,我也只简单说一下吧!

不希望通过拖动分割条来调节窗口的大小,你就要锁定分割条,根据你的需要让CWnd窗口蔽掉这些消息

void CSplitterWnd::OnMouseMove(UINT nFlags,CPoint point)
{
  CWnd::OnMouseMove (nFlags,point);
}

七.使用视图控件。

在VC++里的MFC提供了三个视图控件:图象列表控件,列表视图控件,树型视图控件。
图象列表控件和列表视图控件都使用的类:CListCtrl。

树型视图控件使用的类:CTreeCtrl。

具体的控件功能不是本文所讨论的范围。

使用视图控件我们可以不向上面那样要分割窗口,你能够在视图里直接添加视图控件,但是很显然有个缺陷:只有上面三个控件,这样它的使用范围很小,有时很难达到多种视图的混用。

CListCtrl  m_ListView;

CTreeCtrl  m_TreeView;

在你向导生成的视图里定义这两个变量,然后在你需要的地方使用下面的函数:

BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

const RECT& rect就是它的位置,选择适当的地方就能实现分割条的一些功能。

其实这些方面不是难点,关键在于如何获得它,产生消息映射是最为困难的地方。在这里你就无法使用以前的消息传递方法,首先我们来简单地理解一下消息机制,由于不同的消息是由操作系统的不同部分或是有应用程序来控制的。当鼠标左键在单击时,这个点击区窗口就收到WM_LBUTTONDOWN消息并处理它,通常它产生一个消息发送给它的主窗口(包含它的窗口),报告“我被单击了。”Win32常用控件将WM_NOTIFY通知发送给它的父窗口,作为应答,MFC调用CWnd::OnNotify()方法来处理这些消息,可以为控件的所有者类重载CWnd::OnNotify()方法。

virtual BOOL CWnd::OnNotify( WPARAM wParam, LPARAM lParam, LRESULT* pResult );

l         wParam参数是发送消息的控件ID,如果不来自控件,那么该参数为NULL。

l         LParam是指向通知消息结构(NMHDR)的指针,它包含当前的通知代码和某些其他信息。

l         PResult参数是指向LRESULT变量的指针,如果消息被处理,该变量用于存放结果代码。

BOOL CView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

{

   // TODO: Add your specialized code here and/or call the base class

   TV_DISPINFO *tv_dispinfo=(TV_DISPINFO *)lParam;

   if (tv_dispinfo->hdr.code==NM_CLICK)

   {

}

}

这就是一个这种消息机制的简单的例子,你要实现的功能就放在if后的{}里。

八.文档与视图的一种常见出错的处理:

error C2143: syntax error : missing ';' before '*'

error C2501: 'CMyViewDoc' : missing storage-class or type specifiers

error C2501: 'GetDocument' : missing storage-class or type specifiers

这种错误一般出现在视图的公共变量函数的定义处:

CMyViewDoc* GetDocument();

我刚学VC不久,而且不习惯看英文,所以也不知道它出错的原因在哪里,有谁知道不忘告诉我一声了。

我一般这样处理之后就好了,先在定义前面加“//”,把它屏蔽掉,按F7,再把“//”去掉,按F7,一看没错了,真的很神了。我在怀疑是不是VC++6.0的一个小小的BUG。

九.结束语:

终于在这里和大家说声再见了,上面所写是不算经验的经验,基于有感而发,有感而写,当然错误在所难免的。我真心欢迎大家与我联系。

E-MAIL:passmatlab@etang.com

青草园:http://passmatlab.myetang.com

本栏文章均来自于互联网,版权归原作者和各发布网站所有,本站收集这些文章仅供学习参考之用。任何人都不能将这些文章用于商业或者其他目的。( Pfan.cn )

编程爱好者论坛

本栏最新文章