Split view in two with a menu button? (MFC)

1.4k views Asked by At

I have an MFC program written that reads files, stores the data, and draws it as text on the client view.

I want to make a menu button View->Split that splits the client area into two, separately scrolling views displaying the same data.

I saw some things about CWndSplitter online and read through some documentation but none of it has proved to be useful because they talk about using OnCreate and deleting the default view to get it to work. This is not an option. I want to keep the default view, but split it in two if the user clicks the button.

I've currently created a CWndSplitter member variable and defined a menu button event handler in my SDI-1View.cpp. When called, it does absolutely nothing but cause the screen to flicker and a second click crashes the program.

void CSDI1View::OnViewSplit32778()
{
// TODO: Add your command handler code here

/*
int rows = 2;
int columns = 1;
if (!m_wndSplitter.CreateStatic(this, rows, columns))
    return;

if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CSDI1View), CSize(100, 100), NULL) ||
    (!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CSDI1View), CSize(100, 100), NULL)))
{
    m_wndSplitter.DestroyWindow();
    return;
}
*/
}

Can anyone tell me what the normal approach to splitting a client view in half is? I just want to integrate that into an event handler. Any help would be greatly appreciated.

Thanks.

--------------------------------EDIT----------------------------------

I now have the following code in my Split button event handler, thanks to the outline provided by xMRi, but it is still not working properly...

void CMainFrame::OnViewSplit()
{
// TODO: Add your command handler code here
//calculate client size 
CRect cr;
GetClientRect(&cr);

if (!m_mainSplitter.CreateStatic(this, 2, 1))
{
    MessageBox(_T("Error setting up splitter frames! (CreateStatic)"),
        _T("Init Error!"), MB_OK | MB_ICONERROR);
    return;
}

// Set the parent of the splitter window to the current view
CSDI1View * view = CSDI1View::GetView();
m_mainSplitter.SetParent(this);
view->SetParent(&m_mainSplitter);

// Create a CCreateContext
CCreateContext cc;
CRuntimeClass* prt = RUNTIME_CLASS(CSDI1View);
cc.m_pNewViewClass = prt;
cc.m_pCurrentDoc = view->GetDocument();
cc.m_pNewDocTemplate = NULL;
cc.m_pLastView = NULL;
cc.m_pCurrentFrame = this;

if (!m_mainSplitter.CreateView(0, 0,
    cc.m_pNewViewClass,
    CSize(cr.Width(), cr.Height()/2), &cc))
{
    MessageBox(_T("Error setting up splitter frames! (CreateView 1)"),
        _T("Init Error!"), MB_OK | MB_ICONERROR);
    return;
}

if (!m_mainSplitter.CreateView(1, 0,
    cc.m_pNewViewClass,
    CSize(cr.Width(), cr.Height()/2), &cc))
{
    MessageBox(_T("Error setting up splitter frames! (CreateView 2)"),
        _T("Init Error!"), MB_OK | MB_ICONERROR);
    return;
}

m_bInitSplitter = TRUE;
}

Upon clicking the view->split button, I get a "Debug Assertion Error" popup and the first call to CreateView returns FALSE, displaying my messagebox: "Error setting up splitter frames! (CreateView 1)"

2

There are 2 answers

8
xMRi On

If you want to use a splitter window on demand, you need to change the parent of the current view. So the steps are:

  • create a simple SDI application
  • On demand create a splitter window
  • Use SetParent to the current view and set it as a parent in the splitter window.
  • Create another window in the second splitter.

And the way back.

  • Use SetParent and attach the normal view back to the main frame.
  • Destroy the splitter

There are active samples how to switch a view in a current document (MSDN). It helps you how IDs must be replaced and changed.

1
Jerry Coffin On

A static splitter is for a static split--i.e., a window that's always split. You usually use it when you want to have a different view in each pane of the split (e.g. display a column of numbers in one pane, and a graph in the other pane).

For a situation like yours that you want to be able to have the window, then later split it and have two essentially identical views, you (at least normally) want to use a dynamic splitter.

At least normally, you create the splitter when you create the view. That will create a window that has a handle at the top, right-hand corner that the user pulls down to split the view:

enter image description here

To split the window, the user pulls down on the handle:

enter image description here

When the split is where they want it, they release the mouse button, and the view splits into two separately scrollable sections:

enter image description here

Since you didn't specify whether you wanted vertical or horizontal splitting, I set this one up to allow either or both:

enter image description here

The code for this looks something like this:

BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext) {
    return my_splitter.Create(this,
        2, 2,               // 2 rows, 2 columns
        CSize(20, 20),      // minimum pane size
        pContext);
}

where my_splitter is defined something like this:

CSplitterWnd my_splitter;