Platform/toolchain/toolkit: Windows 10, Visual Studio 2019, MFC.
What I want to do is be able in an event handler (like a double click handler or menu or button handler) to change the interface in my program back and forth between a single CChildView (derived from CWnd) to a CSplitterWnd containing two panes (a CChildView and a CListview based class).
This is a rewrite of a file viewer, I've elided all the app-specific stuff. The idea is that in one mode the user is looking just at a file, and in the other mode the user is looking at a filesystem directory listing pane on the left and a pane on the right containing whatever file is selected in the file list. So a pretty common idiom.
I have the code below.
The two UI modes (if you will) are:
CSplitterWnd m_wndSplitter;
CChildView m_simpleView;
What I've done is create a hidden window that gets to be the parent of views not displayed, and my CMainFrame is the parent of whatever view(s) is displayed. To hide the m_wndSplitter I set its parent to be the hidden window and then hide it and its pane's views, & finally set the simple m_simpleView parent to be CMainFrame and show it. Switching to show the splitter the steps are reversed.
A pair of event handlers are wired to entries in the View menu. One hides the simple m_simpleView and shows the m_wndSplitter; the other handler hides the m_wndSplitter and shows the simple m_simpleView.
I am looking for feedback on my implementation of the event handlers, my use of SetParent() and ShowWindow(). In my experience, MFC can have esoteric subtleties that leap out at the last second before release and bite. And this solution to switching the UI view seems too easy...
So, questions:
- Is this scheme reasonable?
- Is there some subtlety involved where the MFC framework is not going to know what the default or active view is after the event handlers execute?
- Am I being simple (too clever)?
Excerpted from stock-issued new Project Wizard generated code for an MFC, SDI, non-document/view project with a split window:
MainFrame.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();
};
MainFrame.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 (the .cpp is all boilerplate fro the project wizard):
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);
};
CodePudding user response:
For your reference, Adding Multiple Views to a Single Document. Hope it helps.