Строка состояния обновления WTL из дочернего представления

В моем приложении SDI я использую класс CWTLTabViewCtrl из эту статью.

Я хотел бы знать, как обновить строку состояния в главном фрейме из дочернего представления.

Код на 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);

Код в классе CTabViewCtrl:

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

Код в моем классе CTabPeople (с этой точки зрения я хочу обновить строку состояния в 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]
}

Из проведенного мной исследования кажется, что есть два способа обновить строку состояния:

  • Непосредственно из вида CTabPeople с помощью ручки строки состояния
  • Отправив сообщение в цикл mainfrm для обновления строки состояния

У меня вопрос, как реализовать в коде одну из вышеперечисленных опций.

Спасибо.


person Hanan N.    schedule 31.03.2014    source источник


Ответы (2)


Есть несколько способов добиться этого:

  1. Сделайте основной фрейм глобальным и добавьте функцию-член (скажем, 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. Используйте статическую функцию-член с указателем this:

    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. Используйте API SendMessage с настраиваемым идентификатором сообщения. Либо отправьте сообщение родительскому элементу управления (CTabViewCtrl), который, в свою очередь, передает сообщение своим родителям или основному фрейму, либо отправьте его напрямую в основной фрейм. Последний случай требует, чтобы вы знали, сколько существует вложенных окон, или вы можете использовать дескриптор главного окна, как вы уже упоминали.

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

    Добавьте обработчик сообщений в основной фрейм и / или 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. Или вы можете просто отправить сообщение SB_SETTEXT, если у вас есть дескриптор строки состояния как глобальный:

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

Варианты 3 и 4, очевидно, убирают смысл наличия классов (не объектно-ориентированных). Вариант 2, вероятно, наиболее применим.

Я ничего из этого не тестировал, но идея у вас есть. :)

person Midas    schedule 12.04.2014

просто рабочая версия моей:

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");
person sailfish009    schedule 30.03.2017