平台/工具链/工具包:Windows 10、Visual Studio 2019、MFC。
我想要做的是能够在事件处理程序(如双击处理程序或菜单或按钮处理程序)中将我的程序中的界面在单个 CChildView(派生自 CWnd)到包含两个窗格的 CSplitterWnd 之间来回更改(一个 CChildView 和一个基于 CListview 的类)。
这是对文件查看器的重写,我省略了所有特定于应用程序的内容。这个想法是,在一种模式下,用户只查看文件,而在另一种模式下,用户正在查看左侧的文件系统目录列表窗格和右侧的窗格,其中包含在文件列表中选择的任何文件。所以很常见的成语。
我有下面的代码。
两种 UI 模式(如果您愿意的话)是:
CSplitterWnd m_wndSplitter;
CChildView m_simpleView;
我所做的是创建一个隐藏窗口,它成为未显示视图的父级,而我的 CMainFrame 是显示的任何视图的父级。为了隐藏 m_wndSplitter,我将其父级设置为隐藏窗口,然后隐藏它及其窗格的视图,最后将简单的 m_simpleView 父级设置为 CMainFrame 并显示它。切换以显示拆分器,步骤相反。
一对事件处理程序连接到视图菜单中的条目。一个隐藏简单的 m_simpleView 并显示 m_wndSplitter;另一个处理程序隐藏 m_wndSplitter 并显示简单的 m_simpleView。
我正在寻找有关我的事件处理程序的实现、我对 SetParent() 和 ShowWindow() 的使用的反馈。以我的经验,MFC 可以在释放和咬人之前的最后一秒跳出深奥的微妙之处。而这种切换 UI 视图的解决方案似乎太容易了……
所以,问题:
- 这个方案合理吗?
- MFC 框架在事件处理程序执行后不知道默认或活动视图是什么时,是否存在一些微妙之处?
- 我是不是很简单(太聪明了)?
摘自股票发行的新项目向导为具有拆分窗口的 MFC、SDI、非文档/视图项目生成的代码:
主框架.h:
class CMainFrame : public CFrameWnd {
public:
CMainFrame() noexcept;
protected:
DECLARE_DYNAMIC(CMainFrame)
// Attributes
protected:
CSplitterWnd m_wndSplitter;
CChildView m_simpleView;
CWnd offscreen_window;
// ... generated boilerplate code ...
// ... generated boilerplate code ...
// ... generated boilerplate code ...
public:
afx_msg void OnViewSplitter();
afx_msg void OnViewSingle();
};
主框架.cpp:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
offscreen_window.Create(L"STATIC", _T("Hi"), WS_CHILD, CRect(0, 0, 20, 20), this, 1234);
if (!m_simpleView.Create(nullptr, nullptr, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, nullptr)) {
TRACE0("Failed to create view window\n");
return -1;
}
if (!m_wndStatusBar.Create(this)) {
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT));
return 0;
}
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext) {
if (!m_wndSplitter.CreateStatic(this, 1, 2, WS_CHILD | WS_VISIBLE))
return FALSE;
if ( !m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CChildListView), CSize(100,0), pContext)
|| !m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CChildView), CSize(0, 0), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
m_wndSplitter.SetParent(&offscreen_window);
return TRUE;
}
void CMainFrame::OnViewSplitter() {
// show the splitter with a pair of views
m_simpleView.ShowWindow(SW_HIDE);
m_simpleView.SetParent(&offscreen_window);
m_wndSplitter.SetParent(this);
m_wndSplitter.ShowWindow(SW_SHOW);
m_wndSplitter.GetPane(0, 0)->ShowWindow(SW_SHOW);
m_wndSplitter.GetPane(0, 1)->ShowWindow(SW_SHOW);
}
void CMainFrame::OnViewSingle() {
// show the single view
m_wndSplitter.GetPane(0, 0)->ShowWindow(SW_HIDE);
m_wndSplitter.GetPane(0, 1)->ShowWindow(SW_HIDE);
m_wndSplitter.ShowWindow(SW_HIDE);
m_wndSplitter.SetParent(&offscreen_window);
m_simpleView.SetParent(this);
m_simpleView.ShowWindow(SW_SHOW);
}
CChildView.h(.cpp 是项目向导的所有样板):
class CChildView : public CWnd
{
DECLARE_DYNCREATE(CChildView)
// Construction
public:
CChildView();
// Attributes
public:
// Operations
public:
// Overrides
protected:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// Implementation
public:
virtual ~CChildView();
// Generated message map functions
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};
CChildListView.h
#pragma once
#include <afxcview.h>
// CChildListView view
class CChildListView : public CListView
{
DECLARE_DYNCREATE(CChildListView)
protected:
CChildListView(); // protected constructor used by dynamic creation
virtual ~CChildListView();
public:
#ifdef _DEBUG
virtual void AssertValid() const;
#ifndef _WIN32_WCE
virtual void Dump(CDumpContext& dc) const;
#endif
#endif
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};