65.9K
CodeProject 正在变化。 阅读更多。
Home

MDI 用户界面更新扩展

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.35/5 (8投票s)

2003年4月23日

2分钟阅读

viewsIcon

82952

downloadIcon

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 处理程序中执行此操作,或者我们可以向主框架发送消息。请查看示例代码。

现在只有一些小的改动

  1. 从 CRGUpdateUI 派生你的类
  2. 在你的子框架中创建一个 WM_MDIACTIVATE 处理程序
  3. 将消息从子框架转发到视图,而不是调用 PreTranslateMessage
  4. 将 WM_FORWARD 处理程序添加到你的视图
  5. 将你的 updateui_map 和命令处理程序添加到视图

我认为这比以前简单得多。希望你喜欢它 ;)

© . All rights reserved.