BOOKS i'm reading | Control Client area minimum size (WM_GETMINMAXINFO) with MFC in C++Contents
The codeThis section is optional. If you are only interested in using the code and do
not care about the implementation, you can feel free to skip to next section.
Are you still there? Excellent! Good programmers are curious. Before describing
the different tasks that the class
Whenever I work with MFC, I impose myself this small design pattern where I
decouple everything that is not related to MFC in a class called
Now that you know what the code must do, I will present the code itself. The code contains plenty of comments and should be self-explanatory but I will probably have some more insights to provide as well. First, here are the classes declarations: /* * class CMinMaxLogic * * It is used with the class CMinMaxFrame. Its purpose is to isolate * everything that is not related to MFC to ease an eventual porting * to another framework (ie.: WTL). * * Note: This class assumes that the associated frame has a menu and the * following Window Styles: * * - WS_BORDER * - WS_CAPTION * - WS_THICKFRAME * * This condition should always be met since the MFC AppWizard * generated code is using WS_OVERLAPPEDWINDOW that includes all 3 styles * to create the frame window. */ class CMinMaxLogic { public: CMinMaxLogic(LONG x, LONG y); ~CMinMaxLogic(void); /****************************************************************************** * * Name : setClientMin * * Purpose : Compute the minimum frame size from the provided minimum client * area size. It is called at construction and can be recalled anytime * by the user. * * Parameters: * x (LONG) Minimum client horizontal size. * y (LONG) Minumum client vertical size. * * Return value : None. * ****************************************************************************/ void setClientMin(LONG x, LONG y ); /****************************************************************************** * * Name : OnGetMinMaxInfo * * Purpose : Set the minimum size to the minimum frame size and make * adjustments based on the toolbar and status bar visibility * state and their sizes. * * Parameters: * lpMMI (MINMAXINFO FAR*) MinMax info structure pointer. * * Return value : None. * ****************************************************************************/ void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); BOOL m_sbVisible; /* Status bar visibility */ LONG m_sbHeight; /* Status bar height */ BOOL m_tbVisible; /* Toolbar visibility */ int m_tbPos; /* Toolbar position (left, right, top, bottom) */ int m_tbSize; /* Toolbar size */ private: LONG m_cxMin; /* Minimum client size */ LONG m_cyMin; LONG m_fyMin; /* Minimum frame size that includes borders, the frame, the toolbar */ LONG m_fxMin; /* and the status bar to have a client area of m_cxMin*m_cyMin */ }; #define DEFAULTMINCLIENTSIZE 350 class CMinMaxFrame : public CFrameWnd { public: CMinMaxFrame( LONG minX = DEFAULTMINCLIENTSIZE, LONG minY = DEFAULTMINCLIENTSIZE ); /****************************************************************************** * * Name : setClientMin * * Purpose : Recompute the minimum frame size from the newly provided minimum * client area size. It can be called anytime by the user. * * Parameters: * x (LONG) Minimum client horizontal size. * y (LONG) Minumum client vertical size. * * Return value : None. * ****************************************************************************/ void setClientMin(LONG x, LONG y ) { m_MinMaxLogic.setClientMin(x,y); } /****************************************************************************** * * Name : setToolBar * * Purpose : Register the toolbar to monitor for adjusting the minimum frame * size to respect the requested the minimum client area size. * * Note : Currently only 1 toolbar is supported but more could be * supported with the help of a toolbar list. * * Parameters: * pTB (CToolBar *) Toolbar to register. * * Return value : None. * ****************************************************************************/ void setToolBar( CToolBar *pTB ) { m_pTB = pTB; if( pTB ) { m_MinMaxLogic.m_tbPos = TBFLOAT; } else { m_MinMaxLogic.m_tbPos = TBNOTCREATED; } } /****************************************************************************** * * Name : setStatusBar * * Purpose : Register the status bar to monitor for adjusting the minimum * frame size to respect the requested the minimum client area * size. * * Parameters: * pST (CStatusBar *) Status bar to register. * * Return value : None. * ****************************************************************************/ void setStatusBar( CStatusBar *pST ) { // Compute the status bar height if( pST ) { m_MinMaxLogic.m_sbHeight = pST->CalcFixedLayout(TRUE,TRUE).cy; } else { m_MinMaxLogic.m_sbHeight = 0; } } // Overrides /****************************************************************************** * * Name : RecalcLayout * * Purpose : This function is called by the MFC framework whenever a * toolbar status is changing (is attached or detached to/from * the frame). It is used as a hook to maintain this class * internal state concerning the toolbar position and size. * It should not be called directly. * * Parameters: * bNotify (BOOL) Not used. * * Return value : None. * ****************************************************************************/ virtual void RecalcLayout(BOOL bNotify = TRUE); protected: afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI); afx_msg BOOL OnBarCheck(UINT nID); DECLARE_MESSAGE_MAP() private: CMinMaxLogic m_MinMaxLogic; CToolBar *m_pTB; // TB Functions void triggerGetMinMaxInfoMsg(void); int getTBSize(int pos); int findDockSide(void); }; The first task (Keep track of the status bar size and visibility state) is
the easiest task so lets get rid of this one first. Since a status bar vertical
size does not usually change, all that is needed is to store its value by
calling /****************************************************************************** * * Name : setStatusBar * * Purpose : Register the status bar to monitor for adjusting the minimum * frame size to respect the requested the minimum client area * size. * * Parameters: * pST (CStatusBar *) Status bar to register. * * Return value : None. * ****************************************************************************/ void setStatusBar( CStatusBar *pST ) { // Compute the status bar height if( pST ) { m_MinMaxLogic.m_sbHeight = pST->CalcFixedLayout(TRUE,TRUE).cy; } else { m_MinMaxLogic.m_sbHeight = 0; } } The visibility state is acquired by handling the view menu item
/* * CMinMaxFrame::OnBarCheck function * * Purpose : MFC defined message handler. It is called whenever a toolbar * or a status bar visibility state change. It is used to trigger * a WM_GETMINMAXINFO since the minimum frame size to maintain a * minimum client area size has changed. */ BOOL CMinMaxFrame::OnBarCheck(UINT nID) { BOOL res = CFrameWnd::OnBarCheck(nID); // TODO: Add your command handler code here if( nID == ID_VIEW_STATUS_BAR ) { m_MinMaxLogic.m_sbVisible = !m_MinMaxLogic.m_sbVisible; if( m_MinMaxLogic.m_sbVisible ) { triggerGetMinMaxInfoMsg(); } } else if( nID == ID_VIEW_TOOLBAR ) { m_MinMaxLogic.m_tbVisible = !m_MinMaxLogic.m_tbVisible; if( m_MinMaxLogic.m_tbVisible ) { triggerGetMinMaxInfoMsg(); } } return res; } The same function is also used for tracking the toolbar visibility state.
There is an assumption that is made here. The code assumes that at startup both
bars are visible which might not always be the case. This aspect should be
improved someday. In the case that one of the bar becomes visible, the frame
minimum size must be recomputed and this is what /* * CMinMaxFrame::triggerGetMinMaxInfoMsg function */ void CMinMaxFrame::triggerGetMinMaxInfoMsg() { /* * Trigger a WM_MINMAXINFO message by calling the function MoveWindow() * with the current frame size. The purpose of generating a call to the * WM_GETMINMAXINFO handler is to verify that the new client area size * still respect the minimum size. */ RECT wRect; GetWindowRect(&wRect); MoveWindow(&wRect); } Now, the hardest part that is to keep track of the position and size of the
toolbar. Even if it is not documented, the /* * CMinMaxFrame::RecalcLayout function * * Purpose : This function is called by the MFC framework whenever a * toolbar status is changing (is attached or detached to/from * the frame). It is used as a hook to maintain this class * internal state concerning the toolbar position and size. * It should not be called directly. */ void CMinMaxFrame::RecalcLayout(BOOL bNotify) { CFrameWnd::RecalcLayout(bNotify); // TODO: Add your specialized code here and/or call the base class if( m_MinMaxLogic.m_tbPos != TBNOTCREATED ) { if( !m_pTB->IsFloating() ) { int newPos = findDockSide(); if( m_MinMaxLogic.m_tbPos != newPos ) { m_MinMaxLogic.m_tbPos = newPos; m_MinMaxLogic.m_tbSize = getTBSize(m_MinMaxLogic.m_tbPos); triggerGetMinMaxInfoMsg(); } } else { m_MinMaxLogic.m_tbPos = TBFLOAT; m_MinMaxLogic.m_tbSize = 0; } } } /* * CMinMaxFrame::findDockSide function * * Note: This function is using AFXPRIV. It might not be working anymore * with a future MFC version. */ #include "afxpriv.h" int CMinMaxFrame::findDockSide() { // dwDockBarMap static const DWORD dwDockBarMap[4] = { AFX_IDW_DOCKBAR_TOP, AFX_IDW_DOCKBAR_BOTTOM, AFX_IDW_DOCKBAR_LEFT, AFX_IDW_DOCKBAR_RIGHT }; int res = TBFLOAT; for( int i = 0; i < 4; i++ ) { CDockBar *pDock = (CDockBar *)GetControlBar(dwDockBarMap[i]); if( pDock != NULL ) { if( pDock->FindBar(m_pTB) != -1 ) { res = i; break; } } } return res; } /* * CMinMaxFrame::getTBSize function * * Purpose : Returns the horizontal or the vertical toolbar size based on the * toolbar position. */ int CMinMaxFrame::getTBSize(int pos) { int res; CSize cbSize = m_pTB->CalcFixedLayout(FALSE, (pos==TBTOP||pos==TBBOTTOM)?TRUE:FALSE); if( pos == TBTOP || pos == TBBOTTOM ) { res = cbSize.cy; } else { res = cbSize.cx; } return res; } There is a potential problem in the function
|