MFC界面切换

来源:互联网 发布:淘宝来图印花定制内裤 编辑:程序博客网 时间:2024/05/20 12:23

 

 

1.       在MFC中,利用wizard可以很方便的实现对于基本资源管理器的界面,此文章主要是由于要实现一个树型的结构。简单的说是在树型结构对结点响应右边view切换的功能。

对于左边的item的加入,可以用CTreeCtrl的insertItem。

hparent参数可以指定父item项,对于第一个根结点可以直接用TVI_ROOT来指定。

2.       有了item之后,响应item的点击事件,若存在多个item则可以直接响应:

TVN_SELCHANGED事件

对于view的切换可以用Frame来管理,所以在CLeftview中需要调用CFrame的方法。

得到Frame对象可以用函数((CMainFrame*)GetParentFrame())

3.        接下来就是对于view的切换操作了。

首先要有可以进行操作的的view类,可以在资源视图中添加。

但添加一个类之后,对于刚启动程序来说,会提示你要创建哪个view。这个问题该如何解决?

 

答:在app文件发现了InitInstance方法中一个相似的代码出现了两次,而这就是在程序启动时,提示我们创建哪个view的原因。

CSingleDocTemplate* pNewDocTemplate = new CSingleDocTemplate(

//                  IDR_MYFIRSTVIEW_TMPL,

//                  RUNTIME_CLASS(CCTreeCtrImp002Doc),          // document class

//                  RUNTIME_CLASS(CMainFrame),           // frame class

//                  RUNTIME_CLASS(CMyFristView));        // view class

 

CSingleDocTemplate* pNewDocTemplate = new CSingleDocTemplate(

//                  IDR_MYFIRSTVIEW_TMPL,

//                  RUNTIME_CLASS(CCTreeCtrImp002Doc),          // document class

//                  RUNTIME_CLASS(CMainFrame),           // frame class

//                  RUNTIME_CLASS(CListView));              // view class

对于一个单文档的程序来说,有了两人个文档,这是不可行的。程序必定要求你选择一个,这也就是刚开始要我们选择的原因了。注释一个文档的即可解决上述的问题。

4.       接下来,还是谈view的切换。

由于在向导中,已经选择了基于window资源管理器的风格,所以在frame中会有一个CSplitterWnd m_wndSplitter的对象,而利用他则可以很简单的完成复杂的功能。比如说对于view的切换。利用CSplitterWnd的getPane方法可以很容易的得到我们要切换了panel。接下来,要得到我们对于要切换的panel的大小,

CRect rcFrame,rcClient;

       m_wndSplitter.GetClientRect(&rcClient);

       GetClientRect(&rcFrame);

注意到这两个方法,确有不同。

 

这两个的数据还是有差别的,那么为什么呢?

其时从命名中也可以看得出来,rcFrame利用的平台sdk得到的frame的大小。所以bottom的值会更大,而rcClent得到的用户区的大小,由于最底下的状态栏的在在。所以会小一点。

void CMainFrame::changeView()

{

       CView* pView=(CView*)m_wndSplitter.GetPane(0,1);

       BOOL m_bTest=pView->IsKindOf(RUNTIME_CLASS(CView));

       CRect rcFrame,rcClient;

       m_wndSplitter.GetClientRect(&rcClient);

       GetClientRect(&rcFrame);

       if (m_bTest)

       {

              m_wndSplitter.DeleteView(0,1);

             m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(MyFirstView),CSize(rcFrame.Width(),rcFrame.Height()),NULL);

              m_wndSplitter.RecalcLayout();

       }

}

 

而isKindOf是什么意思,是进一步判断对象是不是这样的类型。

对于切换的代码主要就是createView函数了。

一开始我们想实例化一个view但发现,由于构造函数的受保护,即使修改了,也会遇到别的错误,在这里利用runTime_CLASS可以直接得到:

Use this macro to get the run-time class structure from the name of a C++ class。

5.       而面对如果没有CSplitterWnd 对象那我们又该如何。

当然会比较复杂一点。

对于view的控制,依旧在Frame类中。

 CView *pOldActiveView=GetActiveView(); 

 CView *pNewActiveView=(CView*)GetDlgItem(nForm);  

通过这两行代码可以得到当前的view,并且根据nForm的id值,得到我们的view资源。

pNewActiveView=(CView*)new CView1;

同时根据nForm的值,判断应该产生哪个类。

 

在这里,有点迷糊,在生成了pNewActiveView之后,为什么还要new CView1?

在java中,我们对于控制的生成直接new一个控件类就可以了。而MFC中怎么不可以?

跟踪代码,解释如下:

CView *pNewActiveView=(CView*)GetDlgItem(IDD_MYFIRSTVIEW_FORM);  

这个代码是首先在程序找IDD_MYFIRSTVIEW_FORM的view,因为程序如果不是第一次启动,程序中可以已经包含了这个view在切换时,只是隐藏了起来,事实上可以得到,然后显示就是可以切换回来了。这是节约内存的一种手段。

如果返回为空则表示程序还没有此类型的view,这里呢控件更好一点。

没有,我们就要创建一个,还是使用new,可能会显示,类的构造函数与析构函数为空。但没关系。可以修改其访问控制符。

       pNewActiveView=(CView*)new MyFirstView;

这样,就有了一个类对象。

但发现里面句柄实例,还是为空。所以简单的说,不知道能否这样认为,new只是为这个对象申请了一块内存空间,至于里面是什么,还没有任何东西。

hwnd我们要设置,cWnd要设置,m_pDocument也要设置。

CCreateContext context;

  context.m_pCurrentDoc=pOldActiveView->GetDocument();

通过这两行代码可以得到document。然后设置进我们创建的这个类就可以了。可以使用

 pNewActiveView->Create(NULL,NULL,WS_CHILD | WS_BORDER,CFrameWnd::rectDefault,this,nForm,&context);

这个方法,执行之后可以看到:

都有了实例。

SetActiveView(pNewActiveView);

这样就可以设置当前的view了。

然后简单显示一下。

pNewActiveView->ShowWindow(SW_SHOW);

pOldActiveView->ShowWindow(SW_HIDE);

还没有完,这样之后,我们的view还是切换不成功。

pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);

Sets the window ID or control ID for the window to a new value. The window can be any child window, not only a control in a dialog box. The window cannot be a top-level window.

AFX_IDW_PANE_FIRST:

对于SDI,那么你的view窗口的ID就是这个值。(你在CMainFrame中通过GetDlgItem(AFX_IDW_PANE_FIRST)就能得到你的视图窗口的指针。对于MDI,那么你的MDI客户区窗口的ID就是这个值。你在CMainFrame中通过GetDlgItem(AFX_IDW_PANE_FIRST)就能得到这个窗口的指针。注意,MDI客户区就是在MDI下,没有打开任何文档时工具条下面的那个窗口。

实际上AFX_IDW_PANE_FIRST是为了解决多个VIEW的情况下消息转发的问题,这是MFC内部使用的一个固定的值,所有当前的活动视图都会切换到AFX_IDW_PANE_FIRST这个ID,主窗口收到的一些消息(比如命令、通知等等消息)会转发给活动视图来处理,框架通过这个ID来定位活动视图。(为了一个事件)

当在某个SDI应用程序中使用多个视图并支持视图切换时,很容易忽略这点,造成某些消息得不到正确的响应,因此当激活某个视图时也要把这个视图的ID改成AFX_IDW_PANE_FIRST。

最后

 RecalcLayout();

Called by the framework when the standard control bars are toggled on or off or when the frame window is resized. The default implementation of this member function calls the CWnd member function RepositionBars to reposition all the control bars in the frame as well as in the main client window (usually a CView or MDICLIENT).

Override this member function to control the appearance and behavior of control bars after the layout of the frame window has changed. For example, call it when you turn control bars on or off or add another control bar.

也是必不可少的。

 

这天又发现了一个问题,就是在对界面的切换,RUNTIME_CLASS()中是不可以传入CSring类型的,这导致我们要自己写if语句,或者switch语句,当然switch中是只能接收int这些数字类型的和char类型的。还要作一定的转换。

查了一下,也没有很好的解决方案。如果此方不通,能否换种方法来实现,正如我们没有split时的界面切换的方法。

 

而利用这种方法,则必须能够从类名到类的转换。当然在java中,很简单,有一个类的静态方法可以直接得到。

public static void main(String[] args) {

           try {

                   // Class.forName中如果类在其他包下,则些全路径

                   Class xmlmenu = Class.forName("testB");

                   // newInstance()创建此 Class 对象所表示的类的一个新实例。

                   testB b = (testB) xmlmenu.newInstance();

                   b.getStr();

           } catch (Exception e) {

                   e.printStackTrace();

           }

    }

那在MFC中是否也提供了这样的一个方法。