Solved

Adding a menu to MDI Chile Window

Posted on 2004-08-26
14
483 Views
Last Modified: 2013-11-20
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
0
Comment
Question by:osi-sys
  • 7
  • 5
  • 2
14 Comments
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
 

Author Comment

by:osi-sys
Comment Utility
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
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
 
LVL 6

Expert Comment

by:nabehs
Comment Utility
Why not just try:

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

::SetMenu(myview.m_hWnd, hmenu);

Just wondering why this should not work.

0
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 6

Expert Comment

by:nabehs
Comment Utility
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
 
LVL 30

Expert Comment

by:Zoppo
Comment Utility
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
 

Author Comment

by:osi-sys
Comment Utility
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
 

Author Comment

by:osi-sys
Comment Utility
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
 

Author Comment

by:osi-sys
Comment Utility
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
 
LVL 30

Accepted Solution

by:
Zoppo earned 125 total points
Comment Utility
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
 

Author Comment

by:osi-sys
Comment Utility
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

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now