MFC單文檔多視圖程序設計與Splitter拆分窗口


1. 創建不同的子frame.

在文檔視圖程序中 CMainFrame(class CMainFrame : public CMDIFrameWndEx) 繼承自 CMDIFrameWnd (CMDIFrameWndEx 為 CMDIFrameWnd子類). 因此可以將 m_pMainWnd 轉為 CMDIFrameWnd 的指針, 再調用 CreateNewChild 創建一個子frame. 此時可根據傳入的具體的類名, 強制轉換為所需的對象.

    CMDIFrameWnd* pMdiFrmWnd = reinterpret_cast<CMDIFrameWnd*>(m_pMainWnd);
    m_pChildFrm = reinterpret_cast<CChildFrm*> (pMdiFrmWnd->CreateNewChild(RUNTIME_CLASS(CChildFrm), IDR_MAINFRAME));
    m_pChildFrm->ShowWindow(SW_SHOW);

2. CSplitterWnd 拆分窗口. 

拆分窗口在 CMDIChildWnd 子類的 OnCreateClient 方法中進行. 首先使用 CreateStatic 可以將窗口進行拆分, 最大支持 16 x 16.
然后 CreateView 對不同的區域設置不同的 CView, 完成之后使用 CSplitterWnd 對象的 GetPane 方法可以取得不同區域的CWnd指針, 將其強轉為設置的 CView 子類即可.
注意: (1) CreateStatic 創建的每一個區域都必須使用 CreateView 設置一個 CView的子類, 或者使用另一個 CSplitterWnd 填充進行繼續拆分.
        (2) CreateView 傳入的必須是 CView 的子類, 不能使用Ctrl或Dialog, 對於控件如CEdit, 可以使用 CEditView 代替, 其它如CCtrlView類等. 對於使用資源的對話框類, 可以使用 CFormView.

BOOL CChildFrm::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
{
    // 創建拆分窗口
    if (!m_wndSplitter.CreateStatic(this, 2, 1))
        return FALSE;

    if (!m_wndSplitterTop.CreateStatic(&m_wndSplitter, 1, 3, WS_CHILD | WS_VISIBLE, m_wndSplitter.IdFromRowCol(0, 0)))
    {
        return FALSE;
    }

    if (!m_wndSplitterTop.CreateView(0, 0, RUNTIME_CLASS(CLeftView), CSize(0, 0), pContext))
    {
        return FALSE;
    }

    if (!m_wndSplitterTop.CreateView(0, 1, RUNTIME_CLASS(CSpliteDemoView), CSize(0, 0), pContext))
    {
        return FALSE;
    }

    if (!m_wndSplitterTop.CreateView(0, 2, RUNTIME_CLASS(CSpliteDemoView), CSize(0, 0), pContext))
    {
        return FALSE;
    }

    if (!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CCtrlsView), CSize(0, 0), pContext))
    {
        return FALSE;
    }

    m_pLeftView = reinterpret_cast<CLeftView*>(m_wndSplitterTop.GetPane(0, 0));
    m_pMidView = reinterpret_cast<CSpliteDemoView*>(m_wndSplitterTop.GetPane(0, 1));
    m_pRightView = reinterpret_cast<CSpliteDemoView*>(m_wndSplitterTop.GetPane(0, 2));

    m_pCtrls = reinterpret_cast<CCtrlsView*>(m_wndSplitter.GetPane(1, 0));

    return TRUE;
}

 

3. CSplitterWnd 大小調整

窗口大小改變后會調用 OnSize 方法(需在消息映射表中添加ON_WM_SIZE()), 此時一般需要修改 splitter及各個子 view的大小和位置. 其中 splitter 調整后需要調用 SetRowInfo 和 SetColumnInfo 來重新設置分隔條的位置.
注意,初始化過程中 OnSize 方法會被多次調用, 部分窗口可能還沒有創建, 因此需要作判斷.

BEGIN_MESSAGE_MAP(CChildFrm, CMDIChildWndEx)
    ON_WM_SIZE()
    ON_MESSAGE(UM_INPUT_TEXT, &CChildFrm::OnInputText)
END_MESSAGE_MAP()

void CChildFrm::OnSize(UINT nType, int cx, int cy)
{
    CMDIChildWndEx::OnSize(nType, cx, cy);

    if (::IsWindow(m_wndSplitterTop))
    {
        CRect rect;
        GetClientRect(rect);
        m_wndSplitter.MoveWindow(rect);

        int nHeight = rect.Height() - 50;
        if (nHeight < 0)
            nHeight = 0;
        m_wndSplitterTop.MoveWindow(rect.left, rect.top, rect.right, nHeight);

        m_wndSplitter.SetRowInfo(0, nHeight, 0);
        m_wndSplitter.RecalcLayout();

        m_wndSplitterTop.GetClientRect(rect);
        int nWidth = rect.Width() / 3;
        m_wndSplitterTop.GetPane(0, 0)->MoveWindow(rect.left, rect.top, nWidth, rect.bottom);
        m_wndSplitterTop.GetPane(0, 1)->MoveWindow(nWidth, rect.top, nWidth * 2, rect.bottom);
        m_wndSplitterTop.GetPane(0, 2)->MoveWindow(nWidth * 2, rect.top, rect.right, rect.bottom);

        m_wndSplitterTop.SetColumnInfo(0, nWidth, 0);
        m_wndSplitterTop.SetColumnInfo(1, nWidth, 0);
        m_wndSplitterTop.RecalcLayout();

        m_wndSplitter.GetClientRect(rect);

        m_pCtrls->OnSize(nType, cx, cy);
    }
}

 

4. Frame中的消息分發

一個 frame 中通常包含多個子 view. 某個子 view 的消息通常需要傳遞到其它的子 view 中, 亦或者某些耗時操作需要到子線程中處理后更新到界面, 此時都需要涉及消息處理.
某個子 view 通知到其它的子 view時, 通常時先傳遞到 frame中, 再進行分發處理. 然后其它感興趣的子 view 再響應此消息.
對於子線程的處理結果, 最好是 PostMessage 返回一個 new 創建的對象, 由 frame 使用 SendMessage 通知到各個子 view 處理后, 再釋放.

void CCtrlsView::OnBnClickedButtonConfirm()
{
    CString* pStrText = new CString();
    CWnd* pWnd = GetDlgItem(IDC_EDIT);
    pWnd->GetWindowTextW(*pStrText);
    pWnd->SetWindowText(_T(""));
    GetParentFrame()->SendMessage(UM_ADD_TEXT, (WPARAM) pStrText, 0);
}

LRESULT CChildFrm::OnInputText(WPARAM wParam, LPARAM lParam)
{
    m_pLeftView->SendMessage(UM_ADD_TEXT, wParam, lParam);
    m_pMidView->SendMessage(UM_ADD_TEXT, wParam, lParam);
    m_pRightView->SendMessage(UM_ADD_TEXT, wParam, lParam);

    CString* pStr = (CString*) wParam;
    if (pStr)
    {
        delete pStr;
        pStr = NULL;
    }
    return 0;
}

LRESULT CSpliteDemoView::OnInputText(WPARAM wParam, LPARAM lParam)
{
    CString str = *(CString*)(wParam);
    CListCtrl* pListCtrl = &GetListCtrl();
    pListCtrl->InsertItem(pListCtrl->GetItemCount(), str);
    return 0;
}

 

5. 工作線程的設計

  創建線程, 在需要此工作線程事件觸發時創建即可.

void CChildFrm::CreateWorkThread()
{
    if (!m_hWorkThread)
    {
        m_hWorkThread = CreateThread(NULL, 0, WordThreadFun, this, 0, &m_dwWordThreadId);
        Sleep(10); // 稍微等一下,切換一下線程,等待線程創建
    }
}

  框架中, 創建一個事件用於等持線程退出. 在析構函數或其它不需要此工作線程的地方, 發送一個退出的消息.

CChildFrm::CChildFrm()
{
    m_hWorkThreadExit = ::CreateEvent(NULL, TRUE, TRUE, _T(""));

    CreateWorkThread();
}

CChildFrm::~CChildFrm()
{
    if (m_hWorkThreadExit)
    {
        ::PostThreadMessage(m_dwWordThreadId, WM_QUIT, 0, 0);
        WaitForSingleObject(m_hWorkThreadExit, 2000);
        CloseHandle(m_hWorkThreadExit);
        m_hWorkThreadExit = NULL;
    }
}

  工作線程函數體中處理不同的消息, 其它線程使用 PostThreadMessage 通知工作線程工作. 工作線程退出時, 設置事件為有信號狀態. 

DWORD WINAPI CChildFrm::WordThreadFun(LPVOID lpParam)
{
    CChildFrm* pMain = (CChildFrm*)lpParam;
    ::ResetEvent(pMain->m_hWorkThreadExit);

    BOOL isRun = TRUE;
    MSG msg = {0};
    ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
    while (isRun)
    {
        GetMessage(&msg, NULL, 0, 0);
        switch (msg.message)
        {
        case WM_QUIT:
            isRun = FALSE;
            break;
        case WM_GETTIME:
            {
                SYSTEMTIME systime = {0};
                ::GetLocalTime(&systime);
                CString* pStr = new CString();
                pStr->Format(_T("%04d-%02d-%02d %02d:%02d:%02d"), systime.wYear, systime.wMonth, systime.wDay, systime.wHour, systime.wMinute, systime.wSecond);
                
                pMain->PostMessage(UM_ADD_TEXT, (WPARAM) pStr, 0);
            }
            break;
        default:
            break;
        }
    }

    if (pMain->m_hWorkThread)
    {
        CloseHandle(pMain->m_hWorkThread);
        pMain->m_hWorkThread = NULL;
    }
    
    ::SetEvent(pMain->m_hWorkThreadExit);
    return 0;
}

 

源碼: 

http://download.csdn.net/detail/diysoul/9631904


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM