本文主要是介绍2409wtl,切换视图,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
原文
介绍
我从一个基于SDI
(单文档接口)WTL
向导的应用开始,添加了一些从控件继承
的窗口
和一些对话框窗口
(表单视图
),然后才发现我必须,使SDI
框架动态加载和卸载子窗口
.
本文演示了两个
可用来完成的技术:在SDI
应用中的视图
间动态切换
.这是我使用的两个
.
技术
1
技术:第一个方法涉及按需析构和重建
视图实例.这更简单,且在不介意析构和重建窗口
对象时效果很好
.
2
:按需创建视图
,然后用Win32
函数SetWindowLongPtr
直接更改内部标识
,在后续选择
时保留这些视图
.
现已弃用SetWindowLong
.
背景
回到SDI
,在叫m_hWndClient
的公共变量中,主框架存储
子视图的(HWND)
句柄.
不要
一开始可能会想重新赋值
新的子窗口给框架的m_hWndClient
然后更新布局
来解决问题.
//在你的`框架`类中的某个位置
this->m_hWndClient = m_hWndNewView; //
//不管用!!`m_hWndNewView`是我想切换到的视图的句柄
UpdateLayout();
可惜,这不管用,主要是因为框架
不知道提供句柄的窗口
.
“把戏”
解决的技巧,是了解窗口
隐式引用框架窗口
中的第一个
不是控制栏但恰好是子视图
的"面板"
.
在MFC
中,此面板标识为AFX_IDW_PANE_FIRST
.如果你在ATL(atlres.h)
中查看
,有一个名字相似
的定义,叫ATL_IDW_PANE_FIRST
.但两者
有相同"0xE900"
值.
如前,可析构当前的子"视图"
,创建新视图
,然后再赋值新视图的句柄
(隐式设置第一个面板ID
)–技术#1
;
或,可显式更改
两个视图的ID
,这样ATL_IDW_PANE_FIRST
不再是当前视图
的ID
,然后用一些直接的窗口
调用给新视图
赋值此ID
.
有趣的是,技术#1
不需要切换ID
,所以我猜测,当你创建第二个视图
时,它肯定有不同内部ID
,框架或窗口
会重新给ID
赋值"0xE900"
.
如果不切换ID
,而只按其父窗柄
用该框架
创建第二个视图
,则只要它存在
,该框架就会继续按其子视图
引用第一个视图
.
技巧#1
:析构并重建视图
在BitmapView
示例中查找名为TogglePrintPreview()
的函数.
我使用
不同版本,这样示例代码
与演示和源码
匹配.我还扩展它,这样可支持多个视图
.步骤大致为:
1,创建新视图
2,给框架的m_hWndClient
赋值新视图
的窗柄
3,显示
新视图
4,析构
旧视图
5,更新窗口
6,可选:更新框架的预翻译消息
方法以包含新视图的覆盖
.
//`视图`只是一个枚举,这使得更加方便定位`视图`.对想切换到的`每个视图/对话框类`,都有个定义的`视图`枚举.
//可用简单的整数,成员窗口句柄或区分请求视图和当前视图.enum VIEW {BASIC, DIALOG, EDIT, NONE};
//成员视图,,
CBasicView m_view; //向导继承的基本视图
CEditView m_edit; //向导继承的基本对话框,
CBasicDialog m_dlg; //向导继承的基本编辑控件视图
...
void SwitchView(VIEW view)
{//旧视图和新视图的指针CWindow *pOldView, *pNewView;//取当前窗口/视图,在下面定义pOldView = GetCurrentView(); ////取/创建请求的视图pNewView = GetNewView(view); //在下面定义//检查请求的视图是当前视图还是默认视图if(!pOldView || !pNewView || (pOldView == pNewView))return; //闲着显示/隐藏隐藏旧窗口//显示新窗口删除旧视图pOldView->ShowWindow(SW_HIDE); //pNewView->ShowWindow(SW_SHOW); //pOldView->DestroyWindow();//要求`框架`更新客户UpdateLayout();
}
GetCurrentView()
是一个比较m_hWndClient
与每个视图
的句柄,然后返回匹配的视图
,并转换为CWindow
的助手函数.这样:
//取当前视图的助手方法~不可用`MFC`的`GetActiveView()`!
CWindow* GetCurrentView()
{if(!m_hWndClient)return NULL;if(m_hWndClient == m_view.m_hWnd)return (CWindow*)&m_view;else if(m_hWndClient == m_dlg.m_hWnd)return (CWindow*)&m_dlg;else if(m_hWndClient == m_edit.m_hWnd)return (CWindow*)&m_edit;elsereturn NULL;
}
GetNewView(VIEWview)
是一个返回
请求视图,并转换为CWindow
的助手函数
.
在此过程中,它会按需创建视图
对象,并给框架的m_hWndClient
赋值其句柄.这样:
//取/创建新视图的助手方法
CWindow* GetNewView(VIEW view)
{CWindow* newView = NULL;//现在设置请求的视图switch(view){case BASIC://如果不存在,则创建它,并按`框架`的`m_hWndClient`设置`引用`if(m_view.m_hWnd == NULL)m_view.Create(m_hWnd);m_hWndClient = m_view.m_hWnd;newView = (CWindow*)&m_view;break;case DIALOG:if(m_dlg.m_hWnd == NULL)m_dlg.Create(m_hWnd);m_hWndClient = m_dlg.m_hWnd;newView = (CWindow*)&m_dlg;break;case EDIT:if(m_edit.m_hWnd == NULL)m_edit.Create(m_hWnd);m_hWndClient = m_edit.m_hWnd;newView = (CWindow*)&m_edit;break;}return newView;
}
1,上面函数
是直接的.SwitchView(VIEWview)
首先调用GetCurrentView()
以取当前视图的引用
.
2,然后,它调用GetNewView(VIEWview)
来取请求视图的引用
,并在必要
时创建该视图
.它还给框架的m_hWndClient
赋值新视图的句柄
.
3,如果新视图或旧视图
为无效
或彼此相等
(即用户已按自身更改当前视图
),则它不会操作.
4,然后SwitchView(VIEWview)
隐藏旧视图
,并显示
新视图.
5,最后,它析构了旧的视图
.最后一步隐式按ATL_IDW_PANE_FIRST
更改新视图
的内部ID
.
如上,还应该考虑更新框架
的预翻译消息
覆盖,以确保视图
可处理消息自己的预翻译消息
.
预翻译消息
实质上允许框架和/或视图
预览消息,并在翻译和分发消息
前处理它们.返回真
以避免翻译和分发消息
.
大多数应用不会覆盖预翻译消息
,除非需要特殊处理消息
,如子类化许多控件
时.
也即,WTL
向导自动在CWindowImpl
和CDialogImpl
视图中生成预翻译消息
函数,且还会添加从主机的预翻译消息
消息路由
到它们
的期望代码
,这是必须确保从主机路由消息
到视图
的另一个原因
.
以下是我修改框架
的预翻译消息
以使视图
可查看消息
的方法:
//在`CMainFrame`中实现
virtual BOOL PreTranslateMessage(MSG* pMsg)
{if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))return TRUE;if(m_hWndClient != NULL){//为当前视图调用`预翻译消息`取当前视图(转换为`CWindow*`)~函数,如上所示CWindow* pCurrentView = GetCurrentView(); //if(m_view.m_hWnd == pCurrentView->m_hWnd)return m_view.PreTranslateMessage(pMsg);else if(m_dlg.m_hWnd == pCurrentView->m_hWnd)return m_dlg.PreTranslateMessage(pMsg);else if(m_edit.m_hWnd == pCurrentView->m_hWnd)return m_edit.PreTranslateMessage(pMsg);}return FALSE;
}
在此,我首先确保该框架
有有效的子句柄
,然后调用GetCurrentView()
函数来返回CWindow*
.然后,我用每个视图
比较该CWindow*
的窗柄
成员.
这样用视图
来调用视图
自己的预翻译消息
.我不能CWindow
来调用它,因为它没有实现预翻译消息
.
你不需要路由消息
到未实现预翻译消息
的视图
.
即,预翻译消息
是CMessageFilter
接口中的唯一方法
,也是主框架
实现的方法.但是,它不在CWindowImpl
或CDialogImpl
的继承层次
中.
即它不是隐式可用
的,也不一定都需要.
技巧#2
:析构并重建视图
此时,只需简单的更改SwitchView
方法,即可在切换
间持久保存视图
,而不是析构它们
.
//删除旧视图
pOldView->DestroyWindow();
//用如下替换:
C++
//更改当前视图的`ID`,使它不是框架中的第一个子项pOldView->SetWindowLongPtr(GWL_ID, 0);
//按框架的第一个/子项,设置`面板新视图`
pNewView->SetWindowLongPtr(GWL_ID, ATL_IDW_PANE_FIRST);
就是这样!如上,框架
使用第一个面板ID
来更新其客户视图
,因此需要按ATL_IDW_PANE_FIRST
以外的其他内容
更改当前视图
的GWL_ID
,然后按ATL_IDW_PANE_FIRST
更改新视图的GWL_ID
.
使用代码
可在任意地方调用SwitchView
,只需使用与要求视图
对应的视图
枚举调用
它即可.如SwitchView(BASIC)
或SwitchView(EDIT)
.
如果想使用实现,需要:
1,更新enum VIEW{}
以包含每个视图的标识
.
2,在每个视图的主框架
中,添加成员变量
.如CMyView m_myView
.
3,更新GetNewView(VIEW)
中的猜
语句,以包含每个视图
枚举和视图
成员的例子.
4,更新GetCurrentView()
以返回每个视图成员
的CWindow*
引用.
5,(可选)更新框架的预翻译消息
方法,以调用视图
自己的预翻译消息
方法.
实际上,一般想使用一个方法或另一个方法,但这为你提供了在同一个应用
中同时使用这两个方法的选项
.有效更改
包括:
void SwitchView(VIEW view, BOOL bPreserve = FALSE) //默认析构
{...if(bPreserve){//使用技巧`#2`}else{//使用技巧`#1`}
}
结束语
见,SDIMultiView_src
.
这篇关于2409wtl,切换视图的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!