2
votes

I want my Dialog to communicate with my existing view outside of an OK response (so using an apply or similar). I assume Messages are the best way to do this.

I'm sure there are not a lot of MFC questions these days, so I hope someone is able to help.

Creating a new project via the wizard, I add a dialog (let's say a CPropertySheet) that is spawned by the view.

MyPropertiesSheet ps(_T("MyPropertiesSheet"));

if (ps.DoModal() == IDOK) {
    // I don't care about this section
}

At first, I assumed that when I click 'apply' I would be able to send a message to the view and have it do something (as it was spawned in the view); however, I cannot pass messages directly to the view.

From the Dialog I use:

GetParent()->SendMessage(WM_BUTTON1, 0, 0);

I can catch the message within my MainFrm (a CmainFrame) which will launch the specified Button1() function, but I cannot catch the message in the view using the same code (below).

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWndEx)
    ...
    ON_MESSAGE(WM_BUTTON1, Button1)
END_MESSAGE_MAP()

It makes sense as I guess the View is a child of the MainFrm and the Dialog belongs to the MainFrm, not the View.

My Programming Windows with MFC (2nd ed), by Jeff Prosise, uses a custom OnCreate to get a reference to the View by creating it manually, but I really don't want to have to do this as it seems rather complex. I am sure I will end up creating a lot of problems that way. The default OnCreate seems to have no obvious reference to my view (included for example, but feel free to skip this).

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
        return -1;

    BOOL bNameValid;

    CMDITabInfo mdiTabParams;
    mdiTabParams.m_style = CMFCTabCtrl::STYLE_3D_ONENOTE; // other styles available...
    mdiTabParams.m_bActiveTabCloseButton = TRUE;      // set to FALSE to place close button at right of tab area
    mdiTabParams.m_bTabIcons = FALSE;    // set to TRUE to enable document icons on MDI taba
    mdiTabParams.m_bAutoColor = TRUE;    // set to FALSE to disable auto-coloring of MDI tabs
    mdiTabParams.m_bDocumentMenu = TRUE; // enable the document menu at the right edge of the tab area
    EnableMDITabbedGroups(TRUE, mdiTabParams);

    if (!m_wndMenuBar.Create(this))
    {
        TRACE0("Failed to create menubar\n");
        return -1;      // fail to create
    }

    m_wndMenuBar.SetPaneStyle(m_wndMenuBar.GetPaneStyle() | CBRS_SIZE_DYNAMIC | CBRS_TOOLTIPS | CBRS_FLYBY);

    // prevent the menu bar from taking the focus on activation
    CMFCPopupMenu::SetForceMenuFocus(FALSE);

    if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
        !m_wndToolBar.LoadToolBar(theApp.m_bHiColorIcons ? IDR_MAINFRAME_256 : IDR_MAINFRAME))
    {
        TRACE0("Failed to create toolbar\n");
        return -1;      // fail to create
    }

    CString strToolBarName;
    bNameValid = strToolBarName.LoadString(IDS_TOOLBAR_STANDARD);
    ASSERT(bNameValid);
    m_wndToolBar.SetWindowText(strToolBarName);

    CString strCustomize;
    bNameValid = strCustomize.LoadString(IDS_TOOLBAR_CUSTOMIZE);
    ASSERT(bNameValid);
    m_wndToolBar.EnableCustomizeButton(TRUE, ID_VIEW_CUSTOMIZE, strCustomize);

    // Allow user-defined toolbars operations:
    InitUserToolbars(nullptr, uiFirstUserToolBarId, uiLastUserToolBarId);

    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));

    // TODO: Delete these five lines if you don't want the toolbar and menubar to be dockable
    m_wndMenuBar.EnableDocking(CBRS_ALIGN_ANY);
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndMenuBar);
    DockPane(&m_wndToolBar);


    // enable Visual Studio 2005 style docking window behavior
    CDockingManager::SetDockingMode(DT_SMART);
    // enable Visual Studio 2005 style docking window auto-hide behavior
    EnableAutoHidePanes(CBRS_ALIGN_ANY);

    // Load menu item image (not placed on any standard toolbars):
    CMFCToolBar::AddToolBarForImageCollection(IDR_MENU_IMAGES, theApp.m_bHiColorIcons ? IDB_MENU_IMAGES_24 : 0);

    // create docking windows
    if (!CreateDockingWindows())
    {
        TRACE0("Failed to create docking windows\n");
        return -1;
    }

    m_wndFileView.EnableDocking(CBRS_ALIGN_ANY);
    m_wndClassView.EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndFileView);
    CDockablePane* pTabbedBar = nullptr;
    m_wndClassView.AttachToTabWnd(&m_wndFileView, DM_SHOW, TRUE, &pTabbedBar);
    m_wndOutput.EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndOutput);
    m_wndProperties.EnableDocking(CBRS_ALIGN_ANY);
    DockPane(&m_wndProperties);

    // set the visual manager and style based on persisted value
    OnApplicationLook(theApp.m_nAppLook);

    // Enable enhanced windows management dialog
    EnableWindowsDialog(ID_WINDOW_MANAGER, ID_WINDOW_MANAGER, TRUE);

    // Enable toolbar and docking window menu replacement
    EnablePaneMenu(TRUE, ID_VIEW_CUSTOMIZE, strCustomize, ID_VIEW_TOOLBAR);

    // enable quick (Alt+drag) toolbar customization
    CMFCToolBar::EnableQuickCustomization();

    if (CMFCToolBar::GetUserImages() == nullptr)
    {
        // load user-defined toolbar images
        if (m_UserImages.Load(_T(".\\UserImages.bmp")))
        {
            CMFCToolBar::SetUserImages(&m_UserImages);
        }
    }

    // enable menu personalization (most-recently used commands)
    // TODO: define your own basic commands, ensuring that each pulldown menu has at least one basic command.
    CList<UINT, UINT> lstBasicCommands;

    lstBasicCommands.AddTail(ID_FILE_NEW);
    lstBasicCommands.AddTail(ID_FILE_OPEN);
    lstBasicCommands.AddTail(ID_FILE_SAVE);
    lstBasicCommands.AddTail(ID_FILE_PRINT);
    lstBasicCommands.AddTail(ID_APP_EXIT);
    lstBasicCommands.AddTail(ID_EDIT_CUT);
    lstBasicCommands.AddTail(ID_EDIT_PASTE);
    lstBasicCommands.AddTail(ID_EDIT_UNDO);
    lstBasicCommands.AddTail(ID_APP_ABOUT);
    lstBasicCommands.AddTail(ID_VIEW_STATUS_BAR);
    lstBasicCommands.AddTail(ID_VIEW_TOOLBAR);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2003);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_VS_2005);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLUE);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_SILVER);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_BLACK);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_OFF_2007_AQUA);
    lstBasicCommands.AddTail(ID_VIEW_APPLOOK_WINDOWS_7);
    lstBasicCommands.AddTail(ID_SORTING_SORTALPHABETIC);
    lstBasicCommands.AddTail(ID_SORTING_SORTBYTYPE);
    lstBasicCommands.AddTail(ID_SORTING_SORTBYACCESS);
    lstBasicCommands.AddTail(ID_SORTING_GROUPBYTYPE);

    CMFCToolBar::SetBasicCommands(lstBasicCommands);

    // Switch the order of document name and application name on the window title bar. This
    // improves the usability of the taskbar because the document name is visible with the thumbnail.
    ModifyStyle(0, FWS_PREFIXTITLE);

    return 0;
}

I assume there must be a way to get a handle to my View from MainFrm.

I've tried:

auto pView = GetActiveView();
if (pView == NULL) {
    std::string error = "Unable to get Active View\n";
    TRACE(error.c_str());
}
else {
    pView->SendMessage(WM_BUTTON1, 0, 0);
}

but this is returning NULL (so I can't use that to send a message).

I'm not even sure I need this, but I am interested in why this is not working and why I can't get a handle to my View from the MainFrm.

2
Modal dialogs capture the message pump. I think what you will need to do is use your dialog as non-modal. - lakeweb
If you use SendMessage() and not PostMessage() the message will get there irregardless. For MFC dialogs, they have their own message pump...they disable the main frame, run the MFC dialog as modeless with a message pump, and so simulate the Win32 way of doing modal dialogs. If you're using property sheets/pages, it might be a little different...been too long since I looked.You can derive your own CPropertySheet based class and have a member that points to the CView and initialize it before you call DoModal(). If you're in the same thread, you can call member funcs or SendMessage(). - Joseph Willcoxson
Hi @Joseph Willcoxson But the view can to nothing with the message as I understand it. So why send a message? Modals are self contained and collect state, usually with the data exchange mechanism. So, OK will say the data is valid. But the window can not update with the message pump blocked. It has been a while for my since I've been there also... - lakeweb
This may help... - lakeweb
Why do you say the message pump is blocked? There is a message pump. Messages are pumped. Besides, there is also UpdateWindow() where WM_PAINT can be sent (not posted) directly to the window procedure. He should try it and verify it does or does not work... - Joseph Willcoxson

2 Answers

1
votes

For a simple solution, I would post your command WM_BUTTON1 with WM_COMMAND. Then the command is routed the MFC way MSDN ( MDI: Main frame, active child frame, active view, active document, application).
No need to handle and forward it in CMainframe. It does automatically for you.

In your CDialog:

AfxGetMainWnd()->PostMessage(WM_COMMAND, WM_BUTTON1, 0);

Add your handler in your CView

ON_COMMAND(WM_BUTTON1, &CMyView::OnButton)
0
votes

No guarantees...from memory mostly. I haven't used CMDIFrameWndEx, only CMDIFrameWnd, but I assume it should work for the derived Ex variant...seemed like that was your main frame class.

// pseudo code

CMDIFrameWndEx* pFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, AfxGetMainWnd()); // doesn't always work for OLE servers
if (pFrame)
{
   CMDIChileWnd* pMDIChild = pFrame->MDIGetActive();
   if (pMDIChild)
   {
      CYourView* pYourView = DYNAMIC_DOWNCAST(CYourView, pMDIChild->GetActiveView());
      if (pYourView)
      {
         // do something
      }
   }
}