• 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

  • 相关阅读:
    Ubuntu 14.04 LTS 系统空间不足,输入密码后,无法进入桌面的解决办法
    语言代码表
    在WPS中删除整行的快捷键是什么?
    Google浏览器&插件
    Linux命令大全
    Python下载安装
    Tiobe最新编程语言排行
    windows 清理利器
    如何用VBA实现格式刷的功能?
    武侠音乐精装
  • 原文地址:https://www.cnblogs.com/diysoul/p/5878291.html
Copyright © 2020-2023  润新知