Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS General Discussion » Menu dropdowns close when you send commands to the frame Collapse All
Subject Author Date
Dylan da Silva Apr 20, 2010 - 2:54 PM

My menus keep closing whenever a thread tries to send a COMMAND message to the frame.  I used ZoomScrollBar to confirm the issue.



I added a resource to resource.h


#define ID_JUNK_COMMAND				 7000

Here are the modified source files:

// MainFrm.h : interface of the CMainFrame class
//
/////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MAINFRM_H__8AD8EDA0_FE43_4657_B159_597A63DD8A98__INCLUDED_)
#define AFX_MAINFRM_H__8AD8EDA0_FE43_4657_B159_597A63DD8A98__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "ChildView.h"

#if !defined(__EXT_TEMPL_H)
    #include <ExtTempl.h>
#endif

// my stuff ///// begin
class CMyThread : public CWinThread
{
public:
    CMyThread(CFrameWnd* wnd) : m_wnd(wnd), m_working(TRUE) {}
    ~CMyThread(void) {}

    CFrameWnd* GetWnd(void) { return m_wnd; }
    BOOL IsNotDone(void) { return m_working; }
    void StopThread(void) { m_working = FALSE; }
private:
    CFrameWnd* m_wnd;
    BOOL m_working;
};
// my stuff ///// end

class CMainFrame : public CExtNCW < CFrameWnd >
{
public:
    CMainFrame();
protected: 
    DECLARE_DYNAMIC(CMainFrame)

// Attributes
public:

private:
    // window placement persistence
    WINDOWPLACEMENT m_dataFrameWP;

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMainFrame)
    public:
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo);
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    virtual BOOL DestroyWindow();
    virtual void ActivateFrame(int nCmdShow = -1);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CMainFrame();
    virtual void RecalcLayout(BOOL bNotify = TRUE);
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

public:  // control bar embedded members
    class CMyStatusBar : public CExtStatusControlBar
    {
    protected:
	    virtual LRESULT WindowProc( UINT message, WPARAM wParam, LPARAM lParam )
	    {
		    if( message == WM_HSCROLL || message == WM_VSCROLL )
			    return GetParent()->GetDlgItem(AFX_IDW_PANE_FIRST)->SendMessage( message, wParam, lParam );
		    return CExtStatusControlBar::WindowProc( message, wParam, lParam );
	    }
    }; // class CMyStatusBar
    CMyStatusBar					  m_wndStatusBar;
    CExtMenuControlBar			    m_wndMenuBar;
    CExtThemeSwitcherToolControlBar   m_wndToolBarUiLook;
    CChildView					    m_wndView;

// Generated message map functions
protected:
    //{{AFX_MSG(CMainFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSetFocus(CWnd *pOldWnd);
    afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
    //}}AFX_MSG
    afx_msg void OnUpdateControlBarMenu(CCmdUI* pCmdUI);
    afx_msg BOOL OnBarCheck(UINT nID);
    // my stuff ///// begin
    afx_msg void OnJunkCommand(void);
    // my stuff ///// end
    DECLARE_MESSAGE_MAP()

    // my stuff ///// begin
private:
    CMyThread* m_thread;
    // my stuff ///// end
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MAINFRM_H__8AD8EDA0_FE43_4657_B159_597A63DD8A98__INCLUDED_)

 


// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "ZoomScrollBar.h"

#include "MainFrm.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// my stuff ///// begin
UINT MyThreadProc( LPVOID pParam );
// my stuff ///// end

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNAMIC( CMainFrame, CFrameWnd )

BEGIN_MESSAGE_MAP( CMainFrame, CFrameWnd )
    //{{AFX_MSG_MAP(CMainFrame)
    ON_WM_CREATE()
    ON_WM_SETFOCUS()
    ON_WM_GETMINMAXINFO()
    //}}AFX_MSG_MAP

    ON_COMMAND_EX(ID_VIEW_MENUBAR, OnBarCheck )
    ON_UPDATE_COMMAND_UI(ID_VIEW_MENUBAR, OnUpdateControlBarMenu)

    // my stuff ///// begin
    ON_COMMAND(ID_JUNK_COMMAND, OnJunkCommand )
    // my stuff ///// end

    ON_COMMAND_EX(ID_VIEW_UI_LOOK_BAR, OnBarCheck )
    ON_UPDATE_COMMAND_UI(ID_VIEW_UI_LOOK_BAR, OnUpdateControlBarMenu)

END_MESSAGE_MAP()

static UINT indicators[] =
{
    ID_SEPARATOR,		   // status line indicator
    ID_INDICATOR_LOCATION,
    ID_ZOOM_VALUE_PANE,
    ID_ZOOM_SCROLL_BAR_PANE,
//    ID_INDICATOR_CAPS,
//    ID_INDICATOR_NUM,
//    ID_INDICATOR_SCRL,
};

void CMainFrame::OnUpdateControlBarMenu(CCmdUI* pCmdUI)
{
    CExtControlBar::DoFrameBarCheckUpdate( this, pCmdUI, true );
}

BOOL CMainFrame::OnBarCheck(UINT nID)
{
    return CExtControlBar::DoFrameBarCheckCmd( this, nID, true );
}

// my stuff ///// begin
void CMainFrame::OnJunkCommand(void)
{
    // do Fibanacci
    unsigned long long garbage = 24;
    for(int i=0;i<1000;++i)
	    garbage *= (unsigned long long)i;
}
// my stuff ///// end

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
CWinApp * pApp = ::AfxGetApp();
    ASSERT( pApp != NULL );
    ASSERT( pApp->m_pszRegistryKey != NULL );
    ASSERT( pApp->m_pszRegistryKey[0] != _T(’\0’) );
    ASSERT( pApp->m_pszProfileName != NULL );
    ASSERT( pApp->m_pszProfileName[0] != _T(’\0’) );

    if( ! g_PaintManager.PaintManagerStateLoad(
		    pApp->m_pszRegistryKey,
		    pApp->m_pszProfileName,
		    pApp->m_pszProfileName
		    )
	    )
	    g_PaintManager.InstallPaintManager(
		    RUNTIME_CLASS(CExtPaintManagerOffice2007_R2_LunaBlue)
		    );

//    if( ! g_PaintManager.PaintManagerStateLoad(
//		    pApp->m_pszRegistryKey,
//		    pApp->m_pszProfileName,
//		    pApp->m_pszProfileName
//		    )
//	    )
//    {
//	    CExtPaintManagerSkin * pPM = new CExtPaintManagerSkin;
//	    bool bLoaded = true;
//	    if( ! pPM->m_Skin.SearchAndLoadSkinFile( _T("Aqua.Skin"), false ) )
//	    {
//		    if( ! pPM->m_Skin.SearchAndLoadSkinFile( _T("..\\..\\Skins\\Binary\\Aqua.Skin"), false ) )
//		    {
//			    bLoaded = false;
//			    ::AfxMessageBox( _T("Failed to load initial skin.") );
//			    delete pPM;
//		    }
//	    }
//	    if( bLoaded )
//		    g_PaintManager.InstallPaintManager( pPM );
//    }

    CExtPopupMenuWnd::g_bMenuExpanding = false;
    CExtPopupMenuWnd::g_bMenuHighlightRarely = false;
    // window placement persistence
    ::memset( &m_dataFrameWP, 0, sizeof(WINDOWPLACEMENT) );
    m_dataFrameWP.length = sizeof(WINDOWPLACEMENT);
    m_dataFrameWP.showCmd = SW_HIDE;
}

CMainFrame::~CMainFrame()
{
}

void CMainFrame::RecalcLayout(BOOL bNotify)
{
    CExtNCW < CFrameWnd > :: RecalcLayout( bNotify );
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if( CExtNCW < CFrameWnd > :: OnCreate( lpCreateStruct ) == -1 )
	    return -1;

    m_wndStatusBar.m_bDrawPaneSeparatorsInsteadOfBorders = true;
    if(	    (! m_wndStatusBar.Create( this ) )
	    ||    (! m_wndStatusBar.SetIndicators(
			    indicators,
			    sizeof(indicators)/sizeof(UINT)
			    ) )
	    )
    {
	    TRACE0("Failed to create status bar\n");
	    return -1;	  // fail to create
    }
    m_wndStatusBar.SetPaneWidth( 0, 10 );
    m_wndStatusBar.GetStatusBarCtrl().SetMinHeight( 25 );

    // create a view to occupy the client area of the frame
    if( ! m_wndView.Create(
		    NULL,
		    NULL,
		    AFX_WS_DEFAULT_VIEW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
		    CRect( 0, 0, 0, 0 ),
		    this,
		    AFX_IDW_PANE_FIRST,
		    NULL
		    )
	    )
    {
	    TRACE0("Failed to create view window\n");
	    return -1;
    }
CString strTitleOld = GetTitle(), strTitleNew;
    strTitleNew.Format(
	    _T("%s - [%d objects loaded]"),
	    LPCTSTR( strTitleOld ),
	    m_wndView.GetObjectCount()
	    );
    SetTitle( LPCTSTR( strTitleNew ) );
    OnUpdateFrameTitle( FALSE );
    
CWinApp * pApp = ::AfxGetApp();
    ASSERT( pApp != NULL );
    ASSERT( pApp->m_pszRegistryKey != NULL );
    ASSERT( pApp->m_pszRegistryKey[0] != _T(’\0’) );
    ASSERT( pApp->m_pszProfileName != NULL );
    ASSERT( pApp->m_pszProfileName[0] != _T(’\0’) );
    ASSERT( pApp->m_pszProfileName != NULL );

HICON hIcon = pApp->LoadIcon( IDR_MAINFRAME );
    ASSERT( hIcon != NULL );
    SetIcon( hIcon, TRUE );
    SetIcon( hIcon, FALSE );
    
    g_CmdManager->ProfileSetup(
	    pApp->m_pszProfileName,
	    GetSafeHwnd()
	    );
    VERIFY(
	    g_CmdManager->UpdateFromMenu(
		    pApp->m_pszProfileName,
		    IDR_MAINFRAME
		    )
	    );

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

    if(	    (! m_wndToolBarUiLook.Create( NULL, this, ID_VIEW_UI_LOOK_BAR ) )
	    ||    (! m_wndToolBarUiLook.ThemeSwitcherInit() )
	    )
    {
	    TRACE0("Failed to create the m_wndToolBarUiLook toolbar\n");
	    return -1;	  // fail to create
    }

    m_wndToolBarUiLook.EnableDocking( CBRS_ALIGN_ANY );

    if( ! CExtControlBar::FrameEnableDocking( this ) )
    {
	    ASSERT( FALSE );
	    return -1;
    }
    
    if( ! CExtControlBar::ProfileBarStateLoad(
		    this,
		    pApp->m_pszRegistryKey,
		    pApp->m_pszProfileName,
		    pApp->m_pszProfileName,
		    &m_dataFrameWP
		    )
	    )
    {
	    DockControlBar( &m_wndToolBarUiLook, AFX_IDW_DOCKBAR_LEFT );
	    RecalcLayout();
    }
    
    // my stuff ///// begin
    m_thread = new CMyThread((CFrameWnd*)this);
    AfxBeginThread(MyThreadProc, m_thread);
    // my stuff ///// end


    return 0;
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
    if( ! CExtNCW < CFrameWnd > :: PreCreateWindow( cs ) )
	    return FALSE;
    cs.dwExStyle &= ~(WS_EX_CLIENTEDGE|WS_EX_LAYOUTRTL);
    cs.style |= WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
    cs.lpszClass = AfxRegisterWndClass( CS_HREDRAW|CS_HREDRAW|CS_DBLCLKS );
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics

#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
    CExtNCW < CFrameWnd > :: AssertValid();
}

void CMainFrame::Dump(CDumpContext& dc) const
{
    CExtNCW < CFrameWnd > :: Dump( dc );
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
void CMainFrame::OnSetFocus(CWnd* pOldWnd)
{
    pOldWnd;
    // forward focus to the view window
    if( m_wndView.GetSafeHwnd() != NULL )
	    m_wndView.SetFocus();
}

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
    // let the view have first crack at the command
    if( m_wndView.GetSafeHwnd() != NULL )
    {
	    if( m_wndView.OnCmdMsg( nID, nCode, pExtra, pHandlerInfo ) )
		    return TRUE;
    }
    // otherwise, do default handling
    return CExtNCW < CFrameWnd > :: OnCmdMsg( nID, nCode, pExtra, pHandlerInfo );
}


BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) 
{
    if( m_wndToolBarUiLook.PreTranslateMessage( pMsg ) )
	    return TRUE;
    if( m_wndMenuBar.TranslateMainFrameMessage(pMsg) )
	    return TRUE;
    return CExtNCW < CFrameWnd > :: PreTranslateMessage(pMsg);
}

BOOL CMainFrame::DestroyWindow() 
{
CWinApp * pApp = ::AfxGetApp();
    ASSERT( pApp != NULL );
    ASSERT( pApp->m_pszRegistryKey != NULL );
    ASSERT( pApp->m_pszRegistryKey[0] != _T(’\0’) );
    ASSERT( pApp->m_pszProfileName != NULL );
    ASSERT( pApp->m_pszProfileName[0] != _T(’\0’) );

    VERIFY(
	    CExtControlBar::ProfileBarStateSave(
		    this,
		    pApp->m_pszRegistryKey,
		    pApp->m_pszProfileName,
		    pApp->m_pszProfileName
		    )
	    );
    VERIFY(
	    g_PaintManager.PaintManagerStateSave(
		    pApp->m_pszRegistryKey,
		    pApp->m_pszProfileName,
		    pApp->m_pszProfileName
		    )
	    );

    g_CmdManager->ProfileWndRemove( GetSafeHwnd() );

    // my stuff ///// begin
    m_thread->StopThread();
    m_thread->Delete();
    // my stuff ///// end
    
    return CExtNCW < CFrameWnd > :: DestroyWindow();
}

void CMainFrame::ActivateFrame(int nCmdShow) 
{
    // window placement persistence
    if( m_dataFrameWP.showCmd != SW_HIDE )
    {
	    SetWindowPlacement( &m_dataFrameWP );
	    CExtNCW < CFrameWnd > :: ActivateFrame( m_dataFrameWP.showCmd );
	    m_dataFrameWP.showCmd = SW_HIDE;
	    return;
    }
    CExtNCW < CFrameWnd > :: ActivateFrame( nCmdShow );
}

void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
    CFrameWnd::OnGetMinMaxInfo( lpMMI );
    lpMMI->ptMinTrackSize.x = g_PaintManager->UiScalingDo( 700, CExtPaintManager::__EUIST_X );
    lpMMI->ptMinTrackSize.y = g_PaintManager->UiScalingDo( 300, CExtPaintManager::__EUIST_Y );
}

// my stuff ///// begin
UINT MyThreadProc( LPVOID pParam )
{
    CMyThread* pThread = (CMyThread*)pParam;

    if (pThread == NULL)
	    return 1;

    while (pThread->IsNotDone())
    {
	    pThread->GetWnd()->PostMessage(WM_COMMAND,MAKEWPARAM(ID_JUNK_COMMAND,0));
	    Sleep(1000);
    }

    return 0;   // thread completed successfully
}
// my stuff ///// end





Looks like a bug but if it’s not then how would I modify the ZoomScrollBar project to fix the issue I’m having?

Dylan da Silva Apr 21, 2010 - 4:30 AM

And the pasted header and source file were taken directly from the Prof-UIS Sample "ZoomScrollBar" so you can paste them right in.

Technical Support Apr 22, 2010 - 5:25 AM

The problem is not with the WM_COMMAND message. The WM_CANCELMODE message is somehow becomes posted to the main UI thread and that’s why the popup menus become closed. We just verified this message is not sent by MFC and Prof-UIS source code. The following version of the WM_COMMAND method allows you to avoid menu closing:

void CMainFrame::OnJunkCommand(void)
{
    // do Fibonacci
    unsigned long garbage = 24;
    for(int i=0;i<1000;++i)
                garbage *= (unsigned long)i;
MSG msg;
            while( ::PeekMessage( &msg, NULL, WM_CANCELMODE, WM_CANCELMODE, PM_NOREMOVE ) )
            {
                        if( ! ::PeekMessage( &msg, NULL, WM_CANCELMODE, WM_CANCELMODE, PM_REMOVE ) )
                                    break;
            }
}

Dylan da Silva Jul 26, 2010 - 1:14 PM

Any movement on this issue?  We still have a problem with the menus closing.

Technical Support Jul 28, 2010 - 2:27 AM

Unfortunately, no. The WM_CANCELMODE message is extremely important both for Prof-UIS and Win32 menus. This is the classic approach to close the Win32 menus. If your app needs to ensure there are no menus displayed on the screen, then it should send the WM_CANCELMODE message to the main window. The same is supported by Prof-UIS and we cannot ignore the WM_CANCELMODE messages.

Dylan da Silva Jul 28, 2010 - 5:57 AM

I know there is a lot of text in this message and that you are not likely to be the original tech support representative with whom I originally dealt with, so please take the time to read the issue before answering.  I’m aware of what WM_CANCELMODE does and how it should be used.  However, our application did not have problems with menus being unusable before we started using Prof-UIS 2.88.  Just to save you some time, I’ll recap.


Our application periodically sends messages to the active frame resulting in the frame title text changing (one line of code that does this: wnd->SetWindowText() ).  It’s a very simple operation and occurs at a 1 second interval.  Since switching to Prof-UIS we now cannot keep a menu drop down menu open for longer than 1 second.  The previous conversation (see above) resulted in a "fix" that was in fact a hack and changed the behavior of the menus such that they would open and stay open but moving the mouse across the menubar did not sequentially open the drop down menus and often introduced a redraw issue where the menu item tab would be drawn solid white and illegible.  This behavior is also not normal and undesirable.


We would really like this issue resolved properly.  Others who wish to show the time ticking away in title bar or do some other frequent SetWindowText operation via a threaded process will no doubt encounter the same issue.  We have been unable to reproduce the issue in a normal VS2003 MFC application such as this application was before we started using Prof-UIS.  We were also unable to reproduce the issue in the VS2008 built-in skinning library (which incidentally matches the Prof_UIS look-and-feel very closely).

Dylan da Silva Apr 22, 2010 - 7:19 AM

I’ve implemented the work around and the menu stays open but it is no longer behaving like every other menu I’ve ever seen.  For example, open Visual Studio, click the File button and then drag your mouse back and forth along the menu bar.  You’ll see that each menu option drops down when you hover over it.  Our application no longer does this.  Once you click file and the menu drops down, the file menu dropdown persists but none of the other menu items drop down when you hover over them.  If you click on another menu item it drops down but then it persists and no others drop down.  Also there are frequent redraw issues where the text and the button itself on the menu bar are not drawn, leaving a white square where the button used to be, but the menu dropdown is drawn correctly.

Dylan da Silva Apr 22, 2010 - 6:38 AM

In fact, I’m thinking if you take a closer look at that message structure you might be able to determine the origin of the WM_CANCELMODE message and fix the issue.

Technical Support Apr 27, 2010 - 10:29 AM

We are still trying to catch the source of this WM_CANCELMODE message. But let us take another look at this very interesting task. The WM_COMMAND messages are classically sent by menu items and any kind of buttons. These command messages are typically the main user actions in the most of applications. The user actions can cause displaying dialog boxes and other windows. What is the reason for sending WM_COMMAND messages between threads? Why not to use some other messages?

Dylan da Silva Apr 27, 2010 - 10:43 AM

We need to communicate with a child frame (MDI application) but all we give to the thread is the mainframe.  Any Registered Message sent to the mainframe is never received by the Childframe.  So we opted to use commands to the document which can get access the child frame.  This worked fine before we started using Prof-UIS.  Something introduced by Prof-UIS is rerouting or handling commands differently resulting in the menu misbehaving.  As a work around we put a Relay() message handler into the mainframe that sends a received message to all descendants. It’s looks like this:



frame->PostMessage(UM_MAINFRAME_RELAY_MESSAGE,static_cast<WPARAM>(UM_CHILDFRAME_MESSAGE));

And all the handler does is this:

this->SendMessageToAllDescendants(static_cast<UINT>(wparam));

So we made it work without breaking the application.  But this seems to be an issue we shouldn’t have had to work around.

Dylan da Silva Apr 22, 2010 - 6:28 AM

Just so you can understand why I believe this is a Prof-UIS issue.  We have not changed this application and it was working before.  All we’ve done is replace controls and dialogs with the Prof-UIS library equivalents and wrapped CFrameWnd or CWnd objects in the Prof-UIS templates (eg. CExtNCW <T>)  The only obvious changes that could lead to this issue is the addition of the CExtMenuControlBar, changing our class CChildFrameWnd : public CExtNCW <CMDIFrameWnd>, and overriding the CChildFrameWnd::PreTranslateMessage(MSG*) function so it passes messages to the m_menuControlBar object.


The work around that you have provided should get us through in the meantime (provided that it actually works) but we suspect this will impact other things indirectly.  We’d like to see a real fix and not have to resort to hacking.

Dylan da Silva Apr 22, 2010 - 5:58 AM

Thank you for your reply.  However, I think there is more to this that simply adding this to 25 or 30 command handlers.  I realize that using Prof-UIS requires overriding 1 or 2 MFC window message functions namely PreTranslateMessage() and OnCmdMsg().  These functions intercept a command or message and pass it to the Prof-UIS objects you specify so they can handle the message BEFORE anything else does.  In this case I have reason to believe that any command sent to the frame is passed to the menu bar on the MainFrame before it reaches it’s target in the MainFrame.  Could this be related to the CommandManager somehow?  I have not registered the message with the command manager.  Could this be causing the problem?

Dylan da Silva Apr 20, 2010 - 8:07 PM

BTW, I’m using Visual Studio 2008 and Prof-UIS v2.88.