MDI 用户界面更新扩展






4.35/5 (8投票s)
2003年4月23日
2分钟阅读

82952

1156
WTL MDI 用户界面更新扩展。
引言
通常你只有一个菜单、一个工具栏和一个状态栏。现在你想在你的视图中更新这些,这些视图是 CMDIChildWindow
的子窗口。CUpdateUI
不适用于这些结构。所以我考虑了一种实现,它可以处理来自视图类的菜单更新。
背景
我们可以在主框架和每个视图中拥有更新处理程序。我们如何知道调用哪个处理程序?由于 CUpdateUIBase
类没有适用于这些扩展的成员,我实现了一个新的更新处理程序类,称为 CRGUpdateUIBase
。
我们还需要知道 HWND
的类。所以我实现了一个辅助类来从 HWND
获取这些类。它有点像 MFC。
实现
class CWndHandleMap { typedef CAtlMap<HWND CWindow,*> CHandleMap; CHandleMap m_map; public: static CWndHandleMap& GetHandleMap(); CWindow* FromHandle( HWND hWnd); void Add( CWindow* pWnd); void Remove( HWND hWnd); }; ///// in source code file CWndHandleMap& CWndHandleMap::GetHandleMap() { static CWndHandleMap mapStatic; // the only one handle map object return mapStatic; } CWindow* CWndHandleMap::FromHandle( HWND hWnd) { CWindow* pWnd; if( m_map.Lookup( hWnd, pWnd)) return pWnd; return NULL; } void CWndHandleMap::Add( CWindow* pWnd) { ATLASSERT( pWnd->IsWindow()); if( pWnd->IsWindow()) m_map.SetAt( pWnd->m_hWnd, pWnd); } void CWndHandleMap::Remove( HWND hWnd) { m_map.RemoveKey( hWnd); }
现在是一些来自 CRGUpdateUIBase
的代码片段。首先,CRGUpdateUIBase
使用映射以更快地查找命令 ID。
// element data struct _AtlUpdateUIElement { HWND m_hWnd; WORD m_wType; // compare operator for searching UI elements bool operator==(const _AtlUpdateUIElement& e) const { return ((m_hWnd == e.m_hWnd) && (m_wType == e.m_wType)); } }; // instance data struct _AtlUpdateUIData { WORD m_wType; WORD m_wState; void* m_lpData; _AtlUpdateUIData( WORD wType) : m_wType( wType), m_lpData( NULL), m_wState( wType) {} ~_AtlUpdateUIData() { free( m_lpData); } }; // dynamic map which adds and removes entries on MDI (de)activation CAtlMap<WORD _AtlUpdateUIData,*> m_UIUpdateMap; // only hold pointers // the static map initialized once CMapToAutoPtr<WORD _AtlUpdateUIData,> m_UITempMap; // calls delete automatically
接下来的两个函数在当前活动窗口更改时使用
// this function initializes the update map with the static update data void Init() { // remove all appended ui elements m_pAppended = NULL; for( int i= m_UIElements.GetSize()-1;i>m_nAppend; i--) m_UIElements.RemoveAt( i); m_nAppend = -1; if( m_UITempMap.GetCount()) { m_UIUpdateMap.RemoveAll(); for( POSITION pos= m_UITempMap.GetStartPosition(); pos; ) {
WORD wKey;
_AtlUpdateUIData* puiData;
m_UITempMap.GetNextAssoc( pos, wKey,puiData);
puiData->m_wState |= puiData->m_wType; // force update m_UIUpdateMap.SetAt( wKey, puiData); } } // first initialization call else for( const _AtlUpdateUIMap* pMap= m_pUIMap; pMap->m_nID!=(WORD)-1; pMap++) { _AtlUpdateUIData* pData = new _AtlUpdateUIData(pMap->m_wType);
m_UITempMap[pMap->m_nID].Attach(pData);
m_UIUpdateMap[pMap->m_nID] = pData; } // force update m_wDirtyType |= UPDUI_MENUBAR | UPDUI_CHILDWINDOW | UPDUI_STATUSBAR | UPDUI_TOOLBAR; } // appending the currently active update data void Append( CRGUpdateUIBase* updateBase) { m_pAppended = updateBase; m_nAppend = m_UIElements.GetSize()-1; // appending UI elements for( int i=0; i<UPDATEBASE->m_UIElements.GetSize(); i++) // only add elements not found if( m_UIElements.Find( updateBase->m_UIElements[i]) == -1) m_UIElements.Add( updateBase->m_UIElements[i]); // append to the dynamic update map for( POSITION pos=updateBase->m_UIUpdateMap.GetStartPosition(); pos; ) { WORD wKey; _AtlUpdateUIData* pUIData; updateBase->m_UIUpdateMap.GetNextAssoc( pos, wKey, pUIData); pUIData->m_wState |= pUIData->m_wType; // force update m_UIUpdateMap.SetAt( wKey, pUIData); } // force update m_wDirtyType |= UPDUI_MENUBAR | UPDUI_CHILDWINDOW | UPDUI_STATUSBAR | UPDUI_TOOLBAR; }
使用代码
首先,从 CRGUpdateUI<>
派生具有更新处理程序的类,并像使用 CUpdateUI<>
一样链式传递消息。
class CMainFrame : // ... public CRGUpdateUI<CMAINFRAME> { public: BEGIN_MSG_MAP(CMainFrame) // some messages... CHAIN_MSG_MAP(CRGUpdateUI<CMAINFRAME>) // better message routing if( uMsg == WM_COMMAND) { HWND hWnd = MDIGetActive(); if( hWnd) { CChildFrame* pChild = (CChildFrame*)CWndHandleMap::GetHandleMap().FromHandle( hWnd); // Update because MDI child windows doesn't // handle command messages if( pChild) { hWnd = pChild->GetActiveView(); // couldn't be NULL CMyView* pView = (CMyView*)CWndHandleMap::GetHandleMap().FromHandle( hWnd); if( pView && pView->ProcessWindowMessage( hWnd, uMsg, wParam, lParam, lResult)) return TRUE; } } // COMMAND_ID_HANDLERs whithin CMainFrame } END_MSP_MAP() };
由于每个 HWND
都应该在句柄映射中有一个条目,我们需要添加和删除它。
LRESULT CChildFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled) { LRESULT lRes = DefWindowProc(); // first create the window CWndHandleMap::GetHandleMap().Add( this); // ... return lRes; } LRESULT CChildFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { bHandled = FALSE; CWndHandleMap::GetHandleMap().Remove( m_hWnd); return 0; }
接下来,我们需要在 MDI 子窗口被激活或停用时通知我们的主窗口。
LRESULT CChildFrame::OnMDIActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT lRes = DefWindowProc(); bHandled = FALSE; MSG msg; msg.hwnd = ((HWND)lParam == m_hWnd) ? GetActiveView() : NULL; msg.lParam = lParam; msg.message = uMsg; msg.wParam = wParam; ::SendMessage( GetMainWnd(), WM_FORWARDMSG, 0, (LPARAM)&msg); return lRes; }
MDI 激活的处理方式如下
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) { // ... if(pMsg->message == WM_MDIACTIVATE) { HWND hWnd = pMsg->hwnd; if( hWnd) // activated { // append ui handlers CMyView* pView = (CMyView*)CWndHandleMap::GetHandleMap().FromHandle( hWnd); ATLASSERT( pView); CRGUpdateUI<CMAINFRAME>::Append( pView); } else // deactivated { // remove appended ui handler CRGUpdateUI<CMAINFRAME>::Init(); } } return FALSE; }
最后但并非最不重要的是,在你的视图中实现你的更新处理程序。
class CMyView : // ... public CRGUpdateUI<CDPPVIEW> { public: BEGIN_UPDATE_UI_MAP(CMyView) // ... END_UPDATE_UI_MAP() };
更新 V1.1:使用没有句柄映射的代码
在这个新版本中,主框架不需要知道活动视图的类型。如果我们要更新主框架中的 updateui_map,我们的子框架必须在 WM_MDIACTIVATE 处理程序中执行此操作,或者我们可以向主框架发送消息。请查看示例代码。现在只有一些小的改动
- 从 CRGUpdateUI 派生你的类
- 在你的子框架中创建一个 WM_MDIACTIVATE 处理程序
- 将消息从子框架转发到视图,而不是调用 PreTranslateMessage
- 将 WM_FORWARD 处理程序添加到你的视图
- 将你的 updateui_map 和命令处理程序添加到视图
我认为这比以前简单得多。希望你喜欢它 ;)