Adding a menu to MDI Chile Window

Hello,
 
I am developing an MDI application in VC++. I need to create an  MDI child menu attached to the Child Window without replacing the mainframe's menu. To be more specific my MDI Child window is using CFormView. I should be able to access both the child window menu and the main frame menu both simultaneously from the window at a time. Can you please suggest any solution to this.Please email to me if you have any useful relevant information on the above.
 
Thanks
Madhavi
osi-sysAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ZoppoCommented:
Hi osi-sys,

well, IMO a MDI frame window can only have one menu at a time ... by default you have at least two menu resources, one
called IDM_MAINFRAME which is the menu which is shown when no MDI child frame is open and one for each view
class, i.e. IDM_MY_TYPE for CMyView ... so what you can do is simply adding the menus/menu items from IDM_MAINFRAME
you need to have with open child frame to the child frame's menu.

hope that helps,

ZOPPO
0
osi-sysAuthor Commented:
Hello ZOPPO,

Thanks for the inputs.

But I am unable to get you----"so what you can do is simply adding the menus/menu items from IDM_MAINFRAME
you need to have with open child frame to the child frame's menu" ---- Can you please explain more on your comment.

My requirement is I should be able to access CMYView menu(IDM_MY_TYPE ) fram CMYView window's menu bar and CMainFrame menu( IDM_MAINFRAME ) from the CMainFrame menu bar. I do not want the mainframe menu to be replaced with CMyview's menu when child window is shown. Hope you got what I am trying to say.

Can you please suggest a solution to my problem.
Thanks in anticipation of information.
0
ZoppoCommented:
well, ok, that's quite difficult ... in Windows it's not possible that a child window has a menu!

I managed after a while to do it like you want, but there are some more steps to do:
(In following I assume the AppWizard created class are CMyApp, CMyView, CMyDoc, CMainFrame, CChildFrame a.s.o.)

1. In CMyApp::InitInstance() search the lines
>      CMultiDocTemplate* pDocTemplate;
>      pDocTemplate = new CMultiDocTemplate(
>            IDR_MYTYPE,
>            RUNTIME_CLASS(CMyDoc),
>            RUNTIME_CLASS(CChildFrame), // custom MDI child frame
>            RUNTIME_CLASS(CMyView));
and replace the 'IDR_MYTYPE' with 'IDR_MAINFRAME' ... this will ensure the child frame uses the same main menu as the main frame
>      CMultiDocTemplate* pDocTemplate;
>      pDocTemplate = new CMultiDocTemplate(
>            IDR_MAINFRAME,
>            RUNTIME_CLASS(CMyDoc),
>            RUNTIME_CLASS(CChildFrame), // custom MDI child frame
>            RUNTIME_CLASS(CMyView));

2. Create a new dialog resource without caption, border and with style 'Popup'
3. Create a new CDialog-derived class (i.e. CMyMenuBar) with the resource ID of the new dialog resource from 2.
4. In MyView.h iadd lines like this:
>            class CMyMenuBar; // forward declaration
>            
>            class CMyView : public CFormView
>            {
>              CMyMenuBar* m_pWndMenuBar;
>            public:
>              void      UpdateMenuBarPos();
>             ...
>            };
5. In constructor CMyView::CMyView() set m_pWndMenuBar = NULL;
6. Add a 'WM_CREATE' and a 'WM_DESTROY' message handler via ClassWizard in CMyView
7. In MyView.cpp implement the CMyView::UpdateMenuBarPos, CMyView::OnCreate and CMyView::OnDestroy like this:
>            #include "MyMenuBar.h"
>
>            int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
>            {
>                  if (CFormView::OnCreate(lpCreateStruct) == -1)
>                        return -1;
>
>                  m_pWndMenuBar = new CMyMenuBar;
>                  m_pWndMenuBar->Create( CMyMenuBar::IDD, GetParent() );
>                  m_pWndMenuBar->ShowWindow( SW_SHOW );
>
>                  m_pWndMenuBar->SetParent( GetParent() );
>
>                  return 0;
>            }
>
>            void
>            CMyView::UpdateMenuBarPos()
>            {
>                  if ( NULL == m_pWndMenuBar )
>                        return;
>
>                  CRect rect;
>                  GetParent()->GetClientRect( &rect );
>
>                  int iTmp = rect.bottom;
>                  rect.bottom = rect.top + min( rect.Height(), GetSystemMetrics( SM_CYMENU ) );
>
>                  m_pWndMenuBar->MoveWindow( &rect );
>
>                  rect.top = rect.bottom;
>                  rect.bottom = iTmp;
>
>                  MoveWindow( &rect );
>            }
>
>            void CMyView::OnDestroy()
>            {
>                  m_pWndMenuBar->DestroyWindow();
>                  delete m_pWndMenuBar;
>                  m_pWndMenuBar = NULL;
>
>                  CFormView::OnDestroy();
>            }
8. In childfrm.cpp implement a new function UpdateViewMenuBar like this:
>            #include "FormMenuDoc.h"
>            #include "FormMenuView.h"
>            
>            void CChildFrame::UpdateViewMenuBar()
>            {
>                  CView* pView = GetActiveView();
>
>                  if ( NULL == pView || FALSE == pView->IsKindOf( RUNTIME_CLASS( CMyView ) ) )
>                        return;
>
>                  ((CMyView*)GetActiveView())->UpdateMenuBarPos();      
>            }
9. Add via ClassWizard message handlers for 'WM_SIZE' and 'WM_MOVING' in CChildFrame and implement them like this:
>            void CChildFrame::OnMoving(UINT fwSide, LPRECT pRect)
>            {
>                  CMDIChildWnd::OnMoving(fwSide, pRect);
>
>                  UpdateViewMenuBar();
>            }
>
>            void CChildFrame::OnSize(UINT nType, int cx, int cy)
>            {
>                  CMDIChildWnd::OnSize(nType, cx, cy);
>                  
>                  UpdateViewMenuBar();
>            }


That's it (hopefully, if I didn't forget something) ... works fine in my testapp ... if you have problems implementing it please post me
a email address where I can send you my test app.


Hope that helps,

ZOPPO
0
Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

ZoppoCommented:
ahh ... sorry, indeed I forgot something:

In the dialog resource you should create from point 2. you'll have to assign the menu resource 'IDR_MYTYPE' in the dialog properties
in 'General' -> 'Menu'

ZOPPO
0
ZoppoCommented:
and, one more thing:

MFC has a bug in handling WM_UPDATE_COMMAND_UI messages in dialogs ... this leads to behavior
that a menu item, which is disabled by a OnUpdateCommandUI-handler won't be shown as disabled
allthough it won't be executed when clicked.

To workaround this simply add a function
      void HandleMenuUpdate(CMenu* pMenu, BOOL bSysMenu);
to CMyMenuBar class and message handlers for 'WM_INIT_MENU' and 'WM_INIT_MENU_POPUP'
and implement these three functions like this:

>            void CMyMenuBar::OnInitMenu(CMenu* pMenu)
>            {
>                  CDialog::OnInitMenu(pMenu);
>                  
>                  HandleMenuUpdate( pMenu, FALSE );
>            }
>
>            void CMyMenuBar::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
>            {
>                  CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
>
>                  HandleMenuUpdate( pPopupMenu, bSysMenu );
>            }
>
>            void CMyMenuBar::HandleMenuUpdate(CMenu* pMenu, BOOL bSysMenu)
>            {
>                  if (bSysMenu)
>                        return;     // don't support system menu
>                  
>                  ASSERT(pMenu != NULL);
>                  // check the enabled state of various menu items
>                  
>                  CCmdUI state;
>                  state.m_pMenu = pMenu;
>                  ASSERT(state.m_pOther == NULL);
>                  ASSERT(state.m_pParentMenu == NULL);
>                  
>                  // determine if menu is popup in top-level menu and set m_pOther to
>                  //  it if so (m_pParentMenu == NULL indicates that it is secondary popup)
>                  HMENU hParentMenu;
>                  if (AfxGetThreadState()->m_hTrackingMenu == pMenu->m_hMenu)
>                        state.m_pParentMenu = pMenu;    // parent == child for tracking popup
>                  else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL)
>                  {
>                        CWnd* pParent = GetTopLevelParent();
>                        // child windows don't have menus -- need to go to the top!
>                        if (pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL)
>                        {
>                              int nIndexMax = ::GetMenuItemCount(hParentMenu);
>                              for (int nIndex = 0; nIndex < nIndexMax; nIndex++)
>                              {
>                                    if (::GetSubMenu(hParentMenu, nIndex) == pMenu->m_hMenu)
>                                    {
>                                          // when popup is found, m_pParentMenu is containing menu
>                                          state.m_pParentMenu = CMenu::FromHandle(hParentMenu);
>                                          break;
>                                    }
>                              }
>                        }
>                  }
>                  
>                  state.m_nIndexMax = pMenu->GetMenuItemCount();
>                  for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
>                  {
>                        state.m_nID = pMenu->GetMenuItemID(state.m_nIndex);
>                        if (state.m_nID == 0)
>                              continue; // menu separator or invalid cmd - ignore it
>                        
>                        ASSERT(state.m_pOther == NULL);
>                        ASSERT(state.m_pMenu != NULL);
>                        if (state.m_nID == (UINT)-1)
>                        {
>                              // possibly a popup menu, route to first item of that popup
>                              state.m_pSubMenu = pMenu->GetSubMenu(state.m_nIndex);
>                              if (state.m_pSubMenu == NULL || (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || state.m_nID == (UINT)-1)
>                              {
>                                    continue;       // first item of popup can't be routed to
>                              }
>                              state.DoUpdate(this, FALSE);    // popups are never auto disabled
>                        }
>                        else
>                        {
>                              // normal menu item
>                              // Auto enable/disable if frame window has 'm_bAutoMenuEnable'
>                              //    set and command is _not_ a system command.
>                              state.m_pSubMenu = NULL;
>                              state.DoUpdate(this, state.m_nID < 0xF000);
>                        }
>                        
>                        // adjust for menu deletions and additions
>                        UINT nCount = pMenu->GetMenuItemCount();
>                        if (nCount < state.m_nIndexMax)
>                        {
>                              state.m_nIndex -= (state.m_nIndexMax - nCount);
>                              while (state.m_nIndex < nCount && pMenu->GetMenuItemID(state.m_nIndex) == state.m_nID)
>                              {
>                                    state.m_nIndex++;
>                              }
>                        }
>                        state.m_nIndexMax = nCount;
>                  }
>            }


ZOPPO
0
nabehsCommented:
Why not just try:

HMENU hmenu = ::LoadMenu(AfxGetInstanceHandle( ), MAKEINTRESOURCE(IDR_MYMENU));

::SetMenu(myview.m_hWnd, hmenu);

Just wondering why this should not work.

0
ZoppoCommented:
Hi nabehs,

have you tried it?

I did, and SetMenu() returns FALSE and GetLastError() returns 1436 which is
ERROR_CHILD_WINDOW_MENU which is described in MSDN with comment
'Child windows cannot have menus.'!

ZOPPO
0
nabehsCommented:
then that means there is no way to put a menu on a child view, because in any way you try it will end up calling SetMenu
0
ZoppoCommented:
yes, exactly ... therefor I developed a workaround using a modeless popup dialog (so no WS_CHILD style) and
fit it into the child frame as if it was a menu bar ... it's the only way I found to do what osi-sys wants to have ...
0
osi-sysAuthor Commented:
Hello Zoppo

Many Thanks for all your inputs and help.
Can you please post me the test application that you have developed. This will help me to easily understand the solution given by you. My email id is: jmadhavi@yahoo.com.

Thanks once again.
Madhavi.
0
osi-sysAuthor Commented:
Hello Zoppo

Many Thanks for all your inputs and help.
Can you please post me the test application that you have developed. This will help me to easily understand the solution given by you. My email id is: jmadhavi@yahoo.com.

Thanks once again.
Madhavi (osi-sys).


0
osi-sysAuthor Commented:
Hello Zoppo,

I am Madhavi, Hope you remember me. You had helped me in adding a menu to Form view and you had also sent me a test application. I have one problem in and I'm unable to solve. Tyr clicking on minimise and maximize button of Form view, the view is not getting minimized and maximized. If I comment out OnSize and OnMoving part of the code, it's working fine. Can you please help me to solve this problem.

Thanks
Madhavi
0
ZoppoCommented:
Hi Madhavi,


well, it's a little bit strange ... I'm not exaclty sure what the reason is and why it sometimes seems to work ... I suspect the problem is that some messages sent by the system are handled by the overlapped menubar dialog instead, as wanted, by the main frame ...

The workaround I found was to set the WS_EX_TOOLWINDOW style to the menubar dialog.

To do this simply open the dialog resource and check the 'Tool window' in the 'Extended styles' tab of the dialogs properties dialog.


I hope that helps,

ZOPPO
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
osi-sysAuthor Commented:
Hello Zoppo,

Thanks for your help.. One more query...
Since dialog is attached to form view, the menu appears to be disabled i.e grayed. It appears to be enabled only when clicked on it.  Is there option to make the menu attached to the view  appear enabled always irrespective of the focus. Can you please suggest any solution to this?

Thanks
Madhavi.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
System Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.