Click here to Skip to main content
15,867,835 members
Articles / Desktop Programming / WTL
Article

MDI update user interface extension

Rate me:
Please Sign up or sign in to vote.
4.35/5 (8 votes)
30 Apr 20032 min read 81.5K   1.2K   14   11
A WTL MDI update user interface extension

Introduction

Mostly you only have one menu, one toolbar and one status bar. Now you want to update these within your views, which are child windows of CMDIChildWindow. CUpdateUI doesn't work with these constructs. So I thought about an implementation, which can handle menu updates from view classes.

Background

We can have update handlers within the mainframe and within each view. How do we know which handler to call? Because the CUpdateUIBase class doesn't have members, which will work with these extensions, I implemented a new update handler class called CRGUpdateUIBase.

We also need to know the classes for the HWNDs. So I implemented a helper class for getting these classes from HWNDs. It's a little bit like in MFC.

Implementation

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);
}

Now some code snippets from CRGUpdateUIBase. At first CRGUpdateUIBase uses maps for faster finding of command IDs.

// 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

The next 2 functions are used when the currently active window changes

// 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;
        )
            { <BR>            WORD wKey; <BR>            _AtlUpdateUIData* puiData; <BR>            m_UITempMap.GetNextAssoc( pos, wKey,puiData);<BR>            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); <BR>            m_UITempMap[pMap->m_nID].Attach(pData);<BR>            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;
}

Using the code

First, derive your classes which have update handlers from CRGUpdateUI<> and chain messages like it is done with 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()
};

Because each HWND should have an entry within the handle map, we need to add and remove it.

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;
}

Next we need to notify our main window when an MDI child window has been activated or deactivated.

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;
}

The MDI activation is handled like the following:

 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;
}

Last but not least implement your update handlers within your views.

class CMyView :  // ...
        public CRGUpdateUI<CDPPVIEW>
{
public:
BEGIN_UPDATE_UI_MAP(CMyView)
  // ...
END_UPDATE_UI_MAP()

};

Update V1.1: Using the code without a handle map

In this new version the mainframe doesn't need to know the type of the active view. If we want to update the updateui_map within the mainframe, our childframes have to do this within the WM_MDIACTIVATE handler or we could send a message to the mainframe. Please look at the sample code.

Now there are only a few changes:

  1. derive your classes from CRGUpdateUI
  2. create a WM_MDIACTIVATE handler within your childframe
  3. forward the messages from childframe to view instead of calling PreTranslateMessage
  4. add a WM_FORWARD handler to your view
  5. add your updateui_map and command handlers to the view

It's much simpler than before I think. Hope you like it ;)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralWorking Sample Pin
Paul Selormey26-Apr-03 17:24
Paul Selormey26-Apr-03 17:24 
GeneralRe: Working Sample Pin
René Greiner27-Apr-03 22:45
René Greiner27-Apr-03 22:45 
GeneralRe: Working Sample Pin
Paul Selormey28-Apr-03 3:32
Paul Selormey28-Apr-03 3:32 
GeneralRe: Working Sample ... and I forgot Pin
René Greiner30-Apr-03 22:56
René Greiner30-Apr-03 22:56 
GeneralRe: Working Sample ... and I forgot Pin
Paul Selormey1-May-03 0:42
Paul Selormey1-May-03 0:42 
Thanks for the information. It also a good reminder that the 1.1 is now posted Poke tongue | ;-P

Best regards,
Paul.


Jesus Christ is LOVE! Please tell somebody.
GeneralWTL and MFC Pin
TW23-Apr-03 23:26
TW23-Apr-03 23:26 
GeneralRe: WTL and MFC Pin
René Greiner24-Apr-03 0:05
René Greiner24-Apr-03 0:05 
GeneralRe: WTL and MFC Pin
TW27-Apr-03 2:27
TW27-Apr-03 2:27 
GeneralCAtlMap and Others Pin
Paul Selormey23-Apr-03 18:08
Paul Selormey23-Apr-03 18:08 
GeneralRe: CAtlMap and Others Pin
René Greiner23-Apr-03 21:03
René Greiner23-Apr-03 21:03 
GeneralRe: CAtlMap and Others Pin
Paul Selormey26-Apr-03 5:15
Paul Selormey26-Apr-03 5:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.