65.9K
CodeProject 正在变化。 阅读更多。
Home

CSplitterWndEx:指示分隔器窗口焦点和自动拆分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (16投票s)

2000 年 1 月 14 日

viewsIcon

290040

downloadIcon

6736

一个教程,展示了如何自动拆分视图,以及如何指示哪个视图具有焦点

Sample Image - AutomaticSplitter.jpg

引言

我正在使用 MFC 和文档/视图框架开发一个 CAD/CAM 应用程序。在此应用程序中,C<MyProject>View 包含一个带有 3D 对象的 3D 视图。在 CAD/CAM 行业,我们需要 3D 窗口的不同视点,因此我实现了一个自动拆分功能,该功能将 3D 视图平铺成 4 个窗格:前面、顶部、左面和轴测图。

不幸的是,我无法提供 CAD/CAM 应用程序的示例(因为它使用了第三方库),但您会找到一个简单的 MFC 项目,展示了自动拆分功能和焦点。

本教程解释了如何将焦点设置在拆分视图的当前窗格上,以及如何自动拆分视图(无需提供 splitterbar 的位置)。

注意:关于焦点,我使用的是在互联网或书籍中找到的代码,但我记不清是在哪里找到的了。如果您编写了此代码,请联系我,我会为您注明。

1. 创建项目

您可以使用 MFC 应用程序向导生成项目代码

  • 打开 Visual C++。
  • 选择文件 | 新建 | MFCAppWizard (EXE)。
  • 为名称(MyProj)和路径命名。
  • 保留默认选项(直接完成)。

您的项目已完成;您可以编译并运行它。

如果您运行它,您会在“窗口”菜单中看到,没有拆分选项。

2. 添加分隔条

为此,您应该使用组件库

  • 选择项目 | 添加到项目 | 组件和控件。
  • 进入 Visual C++ 组件,选择 SplitterBar
  • 选择“全部”/“确定”并关闭。

现在,如果您查看 CChildFrame 类,您会看到 OnCreateClient 方法,该方法创建了 SplitterWnd。打开资源视图,在菜单 IDR_MYPROJTYPE 中,在 *窗口* 下添加条目 &拆分,ID 为 ID_WINDOW_SPLIT

构建可执行文件,并测试“拆分”功能(应该可以工作)。

3. 创建一个继承自 CSplitterWnd 的新类

  • 选择插入 | 新建类
  • 类类型:MFC
  • 名称:CSplitterWndEx
  • 基类:通用 CWnd
  • 编辑文件 SplitterWnd.hSplitterWnd.cpp,并将所有 CWnd 对象替换为 CSplitterWnd 对象
  • 编辑 ChildFrm.h,在包含文件的顶部插入 #include "SplitterWndEx.h",然后替换
    CSplitterWnd m_wndSplitter;

    CSplitterWndEx m_wndSplitter;

现在,您有了一个新的类可以使用。

4. 设置焦点

CSplitterWndEx 添加以下 2 个 public 方法

void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rectArg);
void RefreshSplitBars(void);

void CSplitterWndEx::OnDrawSplitter(CDC* pDC, 
    ESplitType nType, const CRect& rectArg)
{
    int x_ActivePane, y_ActivePane; 
    COLORREF hilightcolor = RGB(255,255,0);
    
    GetActivePane(x_ActivePane, y_ActivePane);
    if( ((GetRowCount()>1) ||(GetColumnCount()>1)) 
                        && (nType == splitBorder))
    {
        int pRow = 0;
        int pCol = 0;
        if(rectArg.top)
        {
            pRow = 1;
        }
        if(rectArg.left)
        {
            pCol = 1;
        }
        if((pCol == y_ActivePane) && (pRow == x_ActivePane)) 
        {
            if (pDC == NULL)
            {
                RedrawWindow(rectArg, NULL, RDW_INVALIDATE|RDW_NOCHILDREN);
                return;
            }
            ASSERT_VALID(pDC);
            CRect rect = rectArg;pDC->Draw3dRect(rect, 
                               hilightcolor, hilightcolor);
            int dx = -GetSystemMetrics(SM_CXBORDER);
            int dy = -GetSystemMetrics(SM_CYBORDER);
            rect.InflateRect(dx,dy);
            pDC->Draw3dRect(rect, hilightcolor, hilightcolor);
            return;
        }
    }
    CSplitterWnd::OnDrawSplitter(pDC,nType,rectArg);
}

此方法会在 ActivePane 周围绘制一个额外的 Rectangle,颜色为 hilightcolor

注意:您可以设置 2 种颜色(请参阅 Draw3dRect 的帮助)。

void CSplitterWndEx::RefreshSplitBars(void)
{
    CRect rectInside;
    GetInsideRect(rectInside);
    DrawAllSplitBars(NULL, rectInside.right, rectInside.bottom);
}

此方法调用刷新分隔条。

您拥有绘制焦点窗口的所有方法。您只需要调用它们

  • 使用类向导,为 CMyProjView 添加 WM_KILLFOCUSWM_SETFOCUS 消息的句柄。
  • 在这两个方法中,都写以下一行
    ((CChildFrame*)GetParentFrame())->m_wndSplitter.RefreshSplitBars();
  • CMyProjView.cpp 中,添加包含:#include "ChildFrm.h"
  • 转到 CChildFrame 包含文件,并更改访问权限
    CSplitterWndEx m_wndSplitter;
    protectedpublic
  • 构建项目并运行它。

在“窗口”菜单中选择*拆分*。单击一个窗格时,会绘制一个黄色边框。它显示哪个窗格具有焦点。

自动拆分

自动拆分是无需鼠标事件即可将视图拆分成 4 个视图的功能。这意味着 CView 被拆分成 4 个视图,其中 newWidth = oldWidth / 2newHeight = oldHeight / 2

首先,向 CSplitterWnd 类添加两个布尔值

Public:
    bool m_bSplittingDone;
protected :
    bool m_bIsAutomaticSplit;

在构造函数中,初始化这些成员

CSplitterWndEx::CSplitterWndEx()
{
    m_bIsAutomaticSplit = false;
    m_bSplittingDone = false;
}

要调用拆分函数,您需要使用 CSplitterWnd::DoKeyboardSplit();

我们向类写入 4 个新方法

public:
    BOOL DoAutomaticSplit();          // new method
    BOOL DoKeyboardSplit();           // overload the CSplitterWnd::DoKeyboardSplit();
protected:
    void StartTracking(int ht);       // overload the CSplitterWnd::StartTracking
    void StopTracking(BOOL bAccept);  // overload the CSplitterWnd::StopTracking

void CSplitterWndEx::StartTracking(int ht)
{
    //save the current cursor ...
    HCURSOR theCurrentCursor = GetCursor();

    CSplitterWnd::StartTracking(ht); 

    if ( m_bIsAutomaticSplit )
    {
        //...and restore it immediately if in AutomaticSplit mode
        SetCursor(theCurrentCursor);
    }
}

void CSplitterWndEx::StopTracking(BOOL bAccept)
{
    CSplitterWnd::StopTracking(bAccept);

    //now the is splitting done
    m_bIsAutomaticSplit = false;
    m_bSplittingDone = true;
}

BOOL CSplitterWndEx::DoAutomaticSplit()
{
    //save the current mouse position
    POINT theInitialMousePosition;
    GetCursorPos(&theInitialMousePosition);

    //set the splitting done to false ( of course )
    m_bSplittingDone = false;

    //and automatic to true 
    m_bIsAutomaticSplit = true;

    //do the split
    BOOL RetVal = CSplitterWnd::DoKeyboardSplit();

    //restore immediately the old mouse position
    SetCursorPos(theInitialMousePosition.x,theInitialMousePosition.y);
    return RetVal;
}

BOOL CSplitterWndEx::DoKeyboardSplit()
{
    m_bSplittingDone = false;
    m_bIsAutomaticSplit = false;
    return CSplitterWnd::DoKeyboardSplit();
}

我们还需要重载 OnMouseMove 事件

void CSplitterWndEx::OnMouseMove(UINT nFlags, CPoint pt)
{
    if ( m_bIsAutomaticSplit )
    {
        //if AutomaticSplit mode : We are not able 
        //to choose the position of the splitters
        // -> Exiting to next step
        StopTracking(TRUE);
        return;
    }
    CSplitterWnd::OnMouseMove(nFlags,pt);
}

注意,您将需要调用 DoAutomaticSplit 方法。您可以在 CChildFrame 类中调用它

void CChildFrame::AutomaticSplit()
{
    // set the timer ( just a trick make an action once the splitting is done )
    KillTimer(1);  // Destroy the old timer
    SetTimer(1, 10 , NULL); // Creates a new timer (index = 1 ; time = 10ms)

    m_wndSplitter.DoAutomaticSplit();
}

void CChildFrame::OnTimer(UINT nIDEvent) 
{
    if ( m_wndSplitter.m_bSplittingDone ) // if the splitting is done
    {
        KillTimer(1); // kill the timer
        // ( just a trick make an action once the splitting is done ) 

        //loop on the row & columns
        int nbRow = m_wndSplitter.GetRowCount();
        int nbCol = m_wndSplitter.GetColumnCount(); 
        for ( int r = 0; r < nbRow ; r++ ) 
        { 
            for  ( int c=0  ; c <  nbCol ; c++ )
            {
                CMyProjView* theView = (CMyProjView*)m_wndSplitter.GetPane(r,c);
                if ( r==0 && c== 0)
                    //top-lefttheView->DoSomething(RGB(255,0,0));
                else if ( r==0 && c== 1)
                    //top-righttheView->DoSomething(RGB(0,255,0));
                else if ( r==1 && c== 0)
                    //bottom-lefttheView->DoSomething(RGB(0,0,255));
                else if ( r==1 && c== 1)
                  //bottom-righttheView->DoSomething(RGB(255,255,0));
            }
        }
    }
}

这里,我使用一个定时器技巧在拆分完成后执行一些操作。

现在,您可以调用 CChildFrame::AutomaticSplit() 方法来拆分 CView

例如,使用类向导,您可以在*窗口*菜单中创建一个新条目,并添加对 AutomaticSplit 的调用

void CChildFrame::OnWindowAutomaticsplit() 
{
    AutomaticSplit();
}

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.