Solved

Command routing problem with CMFCMenuBar

Posted on 2008-10-27
11
1,922 Views
Last Modified: 2013-12-14
Hi everybody,

when I create a new MDI application using extended features (especially 'menu bar') from MFC Feature Pack for VS 2008 I encounter following problem: When the view has set the mouse capture (SetCapture) commands from menues with shortcut are executed allthough they shouldn't be available.

For testing this I just created a new MDI project using a menu bar and implemented WM_LBUTTONUP/DOWN handlers to set capture like this:

void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
      CView::OnLButtonDown(nFlags, point);
      ::SetCapture( m_hWnd );
}

void CTestView::OnLButtonUp(UINT nFlags, CPoint point)
{
      CView::OnLButtonUp(nFlags, point);
      ::SetCapture( NULL );
}

Now, while mouse button is pressed the view captures the mouse input, but I'm able to i.e. open the 'Open File' dialog by pressing the shortcut 'CTRL-O'. If I do the same in an application without CMFCMenuBar this doesn't happen (I can simulate this by simply commenting out all lines in my CMainFrame class which handle 'm_wndMenuBar' creation/docking).

I debugged the problem and could see that in both cases a call to '::TranslateAccelerator' is done in 'CMDIFrameWnd::PreTranslateMessage', in case I use CMFCMenuBar this causes the 'ID_FILE_OPEN' handler is called from here, if I don't use CMFCMenuBar this doesn't happen.

Does anybody know about a possibility to get that 'old-style' kind of command routing with use of CMFCMenuBar?

Thanks in advance,

ZOPPO
0
Comment
Question by:Zoppo
  • 5
  • 5
11 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> When the view has set the mouse capture (SetCapture) commands from menues with shortcut are executed allthough they shouldn't be available.

The menu updates (reflecting both on toolbar buttons and menu items) were made in idle times only. In case of mouse capture there might be many many mouse move messages which might prevent from ever entering the idle message handling.

You should capture the mouse only when it is above a control of interest and free it when ever it is outside of the controls. Especially, when clicking at a toolbar button and/or opening a menu, the mouse should not be captured (and so the menu items were up-to-date)
0
 
LVL 30

Author Comment

by:Zoppo
Comment Utility
Hi itsmeandnobodyelse,

thanks for the reply - but, my problem ist not the update-mechanism, my problem is that menu-commands can be called via shortcut while the view has the capture.

My application is a CAD like application with a complex GUI within a CScrollView. There are about 15 or more dialogs which can be opened via shortcuts while no drag operation in the view is active. As soon as the user starts a drag-operation the capture is set to the view, it's released when the drag operation finished.

With 'old' MFC there was no problem since menu commands weren't executed with their shortcuts as long as capture was set. With 'new' MFC (or better said as soon as a CMFCMenuBar is involved) this doesn't work anymore.

Of course now I coul implement a ON_UPDATE_COMMAND_UI handler for each of these menue commands, but I'm searching for a more generic way to get this working as it did in 'old' MFC.

In MSDN about 'SetCapture' there's this statement:
> When the mouse is captured, menu hotkeys and other keyboard accelerators do not work.

This in fact is not the case anymore when using the CMFCMenuBar.


Regards,

ZOPPO
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>> can be called via shortcut while the view has the capture.
Your accelerators (shortcuts) wouldn't be able to invoke menu functions (or toolbar buttons) when the items or buttons were disabled. Enabling/disabling of menu functions happens in the UpdateUI handler functions which were called at idle times, i. e. when the message pump is not busy.

>>>> With 'old' MFC there was no problem since menu commands weren't executed with their shortcuts as long as capture was set.
That seems to have been a bug rather than a feature. I doubt that it makes much sense to ignore shortcuts while the mouse is captured - as a general restriction (though of course it is bad if you have written code which was based on that). Normally the user must not be aware of mouse capturing (though you might have changed cursor shape) and then might be confused if short-cuts were not working.  

>>>> I could implement a ON_UPDATE_COMMAND_UI handler for each of these menue commands, but I'm searching for a more generic way to get this working
Hmm. The UI handlers is the MFC way to do this. You hardly will find a better solution which has not some drawbacks. BTW, you could create the UPDATE_COMMAND_UI handlers  using the wizard and replace then all handler functions in the message map by one single handler. That way the efforts seem to manageable. You even could call the one and only UI handler function when capturing/releasing the mouse, thus regaining the *lost* feature from old MFC.
0
 
LVL 30

Author Comment

by:Zoppo
Comment Utility
Sorry, I don't agree.

>> That seems to have been a bug rather than a feature.
First as told in MSDN exactly this behavior is stated: 'When the mouse is captured, menu hotkeys and other keyboard accelerators do not work'. We started developing that mentioned application with MSVC++ 4.0 and since then that behavior wasn't changed (even with VS 2008 it's the same as long 'normal' menues are used). There we implemented the shortcuts which can be used while the mouse is captured via handling WM_KEYDOWN/WM_KEYUP messages. This worked fine the last 10 years.

IMO this modified behavior of 'new' MFC is a bug and I'd like to find a workaround to get it working as it did before.

Of course I know about the UpdateUI-handler functionality. As told I would like to get the 'new' MFC work as the 'old' one did to avoid the need of changing/adding UPDATE_COMMAND_UI handlers for all possible shortcuts since there are a lot of these which handles the states in different situations and I fear it would take me (and our testers) a lot of time to find all commands which handlers need to be changed.

BTW: Did you already do something with MFC FeaturePack's new classes? I did and found that there are some bugs inside and even some things which IMO are not designed very well - I implemented lot of 'new' GUI elements in our application and had to implement quite a lot workarounds to get it running as expected ...

Best regards,

ZOPPO
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
The CWnd::PreTranslateMessage runs before accelerators were handled - or better said the CDialog::PreTranslateMessage or CFormView::PreTranslateMessage actually were handling the accelerators. So, you could overload these functions, check for the mouse being captured and call the baseclass function only if you want the accelerators to be handled.

>>>> Did you already do something with MFC FeaturePack's new classes?
No, my current project is a huge MFC based framework which recently was ported from VC6 to VC7. It uses only basic MFC controls (plus list control and tree control) as they have been since the beginning of MFC. So, I know the new features from EE questions only.
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 30

Accepted Solution

by:
Zoppo earned 0 total points
Comment Utility
Hi,

I think I found a workaround which works. I found no way to handle these in PreTranslateMessage, but I found I can skip those shortcuts from working by overriding CMainFrame::OnCommand like this:

BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
      if ( HIWORD( wParam ) == 1 )
      {
            if ( NULL != GetCapture() )
            {
                  HMENU hMenu = m_wndMenuBar.GetHMenu();

                  if ( NULL != hMenu )
                  {
                        MENUITEMINFO mi = { 0 };
                        mi.cbSize = sizeof( MENUITEMINFO );
                        mi.fMask = MIIM_FTYPE;

                        if ( FALSE != GetMenuItemInfo( hMenu, LOWORD( wParam ), FALSE, &mi ) )
                        {
                              return TRUE;
                        }
                  }
            }
      }

      return CMDIFrameWndEx::OnCommand(wParam, lParam);
}

Then I had two more problems:
- i.e. pressing ALT-F opened the File menue
- pressing and releasing ALT set focus to the menu bar

To solve these I derived a class from CMFCMenuBar and added these functions:

// message handler for WM_SETFOCUS
LRESULT CMyMenuBar::OnMessageSetFocus( WPARAM, LPARAM )
{
      if ( NULL == ::GetCapture() )
      {
            CWnd* pWnd = NULL == wParam ? NULL : CWnd::FromHandle( (HWND)wParam );

            CMFCMenuBar::OnSetFocus( pWnd );
      }

      return 0;
}

// overridden from CMFCMenuBar
BOOL CMyMenuBar::TranslateChar( UINT nChar )
{
      if ( NULL != ::GetCapture() )
      {
            return FALSE;
      }

      return CMFCMenuBar::TranslateChar( nChar );
}
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Have you installed Vs2008 Service Pack 1 (released in August)?  A number of bugs were fixed.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
>>>>> I found no way to handle these in PreTranslateMessage
Did you overload CMainFrame::PreTranslateMessage as well?

The accelerators (e. g. Alt-F) were all handled in the TranslateMessage of your message loop (in CWinThread::Run) what leads to (virtual) calls of the PreTranslateMessage of the targeted CWnd derived class.
0
 
LVL 30

Author Comment

by:Zoppo
Comment Utility
Hi,

@DanRollins: Yes, I have installed it ...

@itsmeandnobodyelse: I tried PreTranslateMessage in MainFrame, ChildFrame, App and View classes. As written above I can debug it down to the call of '::TranslateAccelerator' in 'CMDIFrameWnd::PreTranslateMessage'. If this is called the menue pops up without a further call to any of the above mentioned classes PreTranslateMessage.

Regards,

ZOPPO
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
Comment Utility
That is strange. I seriously had the same case where I finally found  the translation in CDialog::PreTranslateMessage.

You might set a breakpoint into CWnd::PreTranslateMessage in order to find the window which translates the shortcuts ...
0
 
LVL 30

Author Comment

by:Zoppo
Comment Utility
Hi again,

I tested around with this a lot now and found no possibility to catch it directly in PreTranslateMessage - as told before as soon as the '::TranslateAccelerator' gets called the WM_COMMAND message is sent directly to my CView derived class, no matter if capture ist set or not.

So I found no other way to solve this as I wrote above. Up to now it seems to work fine ...


Now, I don't really know what to do with this question now - any suggestions?

Regards,

ZOPPO
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.

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

12 Experts available now in Live!

Get 1:1 Help Now