Question

In my SDI application i am using the CWTLTabViewCtrl class from this article.

I would like to know how to update the status bar at the main frame from the child view.

The code at the mainfrm.h:

CreateSimpleStatusBar();

// create tabctrl
CTabViewCtrl m_MainTabCtrl;
m_hWndClient = m_MainTabCtrl.Create(
            m_hWnd, rcDefault, NULL,
            WS_CHILD | WS_VISIBLE, WS_EX_STATICEDGE );
m_MainTabCtrl.AddPeopleTab(L"People);

The code at the CTabViewCtrl class:

class CTabViewCtrl : public CWTLTabViewCtrl
{
public:

    CTabViewCtrl()
    {
    }

    virtual ~CTabViewCtrl()
    {
    }
    void AddPeopleTab(LPCTSTR inTabName)
    {
        auto tabPeople = CTabPeople;
        tabPeople->Create(*this, rcDefault, nullptr, WS_CHILD, WS_EX_STATICEDGE);
        AddTab(inTabName, *tabPeople, FALSE, 0, (LPARAM)theProcessesView);
    }
public:
    DECLARE_WND_SUPERCLASS(NULL, CWTLTabViewCtrl::GetWndClassName())

    BOOL PreTranslateMessage(MSG* pMsg)
    {
            pMsg;
            return FALSE;
    }

    BEGIN_MSG_MAP_EX(CTabViewCtrl)
        REFLECT_NOTIFICATIONS()
        CHAIN_MSG_MAP(CWTLTabViewCtrl)
    END_MSG_MAP()
};

The code at my CTabPeople class (from this view i want to update the status bar at the mainfrm.h):

class CTabPeople : public CWindowImpl<CTabPeople, CListViewCtrl>,
                            public CCustomDraw<CTabPeople>
{
[snip]

public:
    DECLARE_WND_SUPERCLASS(NULL, CListViewCtrl::GetWndClassName())

    BOOL PreTranslateMessage(MSG* pMsg)
    {
        pMsg;
        return FALSE;
    }

    BEGIN_MSG_MAP(CTabPeople)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
        COMMAND_ID_HANDLER(IDM_PROCESSTAB_REFRESH, OnMenuRefresh)
        REFLECTED_NOTIFY_CODE_HANDLER(LVN_COLUMNCLICK, OnColumnClick)
        CHAIN_MSG_MAP_ALT(CCustomDraw, 1)
    END_MSG_MAP()

    LRESULT OnMenuRefresh(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL&bHandled)
    {
        // Here i would like to update the status bar created at the mainfrm.h
        // something like UISetText(0, L"Updating..");
    }

  [snip]
}

From the research i have done it seems that there is two ways to update the status bar:

  • Directly from the CTabPeople view using the handle of the status bar
  • By sending a message to the mainfrm loop to update the status bar

My question is how to implement one of the above options in my code.

Thanks.

Was it helpful?

Solution

There are a number of ways to achieve this:

  1. Make the main frame a global and add a member function (say SetStatusText):

    void CMainFrame::SetStatusText(CString strText)
    {
        CStatusBarCtrl sb(m_hWndStatusBar);
        sb.SetText(SB_SIMPLEID, strText);
    }
    
    LRESULT CTabPeople::OnMenuRefresh(...)
    {
        g_pMainFrame->SetStatusText(_T("Status text"));
    }
    
  2. Use a static member function with a 'this pointer':

    class CMainFrame;// Forward declaration
    class CMainFrame : public ...
    {
    public:
        CMainFrame() { this_ptr = this; }
    
        static void SetStatusText(CString strText)
        {
            CStatusBarCtrl sb(this_ptr->m_hWndStatusBar);
            sb.SetText(SB_SIMPLEID, strText);
        }
    
        static CMainFrame* this_ptr;
    };
    
    // Initialization (in .cpp file)
    CMainFrame* CMainFrame::this_ptr = NULL;
    
    LRESULT CTabPeople::OnMenuRefresh(...)
    {
        CMainFrame::SetStatusText(_T("Status text"));
    }
    
  3. Use the SendMessage API with a custom message identifier. Either send the message to the parent control (CTabViewCtrl), which in turn passes the message to its parents or the main frame, or send it to the main frame directly. The last case requires that you know how many nested windows there are, or you can use the main window handle as you already mentioned.

    LRESULT CTabPeople::OnMenuRefresh(...)
    {
        // Parent control processes the message
        ::SendMessage(GetParent(), MY_STATUS_MSG, (WPARAM) _T("Status text"), 0);
        // Main frame processes the message
        ::SendMessage(::GetParent(GetParent()), MY_STATUS_MSG, (WPARAM) _T("Status text"), 0);
        ::SendMessage(g_hWndMain, MY_STATUS_MSG, (WPARAM) _T("Status text"), 0);
    }
    

    Add a message handler in the main frame and/or CTabViewCtrl:

    BEGIN_MSG_MAP(CMainFrame)
        ...
        MESSAGE_HANDLER(MY_STATUS_MSG, OnSetStatusMsg)
    END_MSG_MAP()
    
    LRESULT CMainFrame::OnSetStatusMsg(UINT, WPARAM wParam, LPARAM, BOOL&)
    {
        CStatusBarCtrl sb(m_hWndStatusBar);
        sb.SetText(SB_SIMPLEID, (LPCTSTR) wParam);
        return FALSE;
    }
    
  4. Or you can simply send the SB_SETTEXT message if you have the status bar handle as a global:

    LRESULT CTabPeople::OnMenuRefresh(...)
    {
        ::SendMessage(g_hWndStatus, SB_SETTEXT, MAKEWPARAM(SB_SIMPLEID, 0), (LPARAM) _T("Status text"));
    }
    

Option 3 and 4 obviously take away the point of having classes (not object-oriented). Option 2 is probably the most applicable.

I haven't tested anything of this, but you have the idea. :)

OTHER TIPS

simply working version of mine:

MainFrm.h:

class CMainFrame : 
    public CFrameWindowImpl<CMainFrame>, 
    public CUpdateUI<CMainFrame>,
    public CMessageFilter, public CIdleHandler
{
public:
  DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
  CMainFrame() { this_ptr = this; }
  static CMainFrame* this_ptr;
  void SetStatusText(std::wstring strText);
  //...
}

MainFrm.cpp

CMainFrame* CMainFrame::this_ptr = nullptr;

// m_hWndStatusBar is from atlframe.h
void CMainFrame::SetStatusText(std::wstring strText)
{
  ::SetWindowText(m_hWndStatusBar, strText.c_str());
}

other.cpp

CMainFrame::this_ptr->SetStatusText(L"program ready");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top