在开发图形用户界面(GUI)应用程序时,对话框的灵活性和易用性至关重要。然而,之前遇到的一个问题是,对话框的大小被固定,导致用户在使用过程中需要滚动条来查看隐藏的部分,这大大降低了用户体验。为了解决这个问题,决定对现有的分割器控件进行改进,使其能够自动调整与分割条关联的控件位置。
在之前的工作中,负责一个拥有多个对话框的GUI应用程序。但是,之前的开发者将所有对话框的大小都设置为了固定值。虽然在某些情况下这是可以接受的,但在其他情况下,这会导致界面难以使用。例如,用户需要移动滚动条才能查看被固定大小对话框遮挡的部分。因此,当有机会修改它时,决定进行一些改变,并从那时起开始寻找可以在对话框中使用的分割器控件。
以下是实现改进的分割器控件的步骤:
在对话框上放置一个图片控件,并赋予它ID IDC_SPLITTER1。调整控件的大小,使其看起来像一个水平条。双击控件,更改属性如下。同样的方法添加一个垂直的分割器,赋予它ID IDC_SPLITTER2。实际上,上述所有操作只是为了避免计算分割器的大小。可以通过 CSplitterControl::Create 函数指定分割器的大小。
将 SplitterControl.h 和 SplitterControl.cpp 添加到项目中。在对话框类的 .h 文件中插入 #include "splittercontrol.h"。然后,添加成员变量:
    private:
        CSplitterControl    m_wndSplitter1;
        CSplitterControl    m_wndSplitter2;
    
在 OnInitDialog 函数中创建分割器。这里有一些注意事项:
使用 SPS_VERTICAL 或 SPS_HORIZONTAL 来指定分割器的风格。可以为分割器线指定一个RGB颜色(默认是RGB(120, 120, 120))。可以指定分割器线的宽度(默认是1)。分割器的宽度(对于垂直分割器)或高度(对于水平分割器)取决于调用 Create 函数时的矩形宽度。
分割器可以自动移动关联窗口的位置,当用户改变分割器的位置时。因此,应该指定哪个窗口需要改变位置。CSplitterControl::RegisterLinkedWindow 函数负责这项工作。检查以下示例:
    // 注册分割器的窗口
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_LEFT,     GetDlgItem(IDC_TREE));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    GetDlgItem(IDC_LIST));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    GetDlgItem(IDC_EDIT));
    this->m_wndSplitter1.RegisterLinkedWindow(SPLS_LINKED_RIGHT,    &m_wndSplitter2);
    this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_UP,       GetDlgItem(IDC_LIST));
    this->m_wndSplitter2.RegisterLinkedWindow(SPLS_LINKED_DOWN,     GetDlgItem(IDC_EDIT));
    // 重新布局分割器以使其看起来更好
    this->m_wndSplitter1.Relayout();
    this->m_wndSplitter2.Relayout();
    
记住,SPLS_LINKED_LEFT 表示控件在分割器的左侧。SPLS_LINKED_RIGHT 表示右侧。这两个用于垂直分割器。如果控件与垂直分割器的 SPLS_LINKED_LEFT 相关联,那么控件的右侧位置将由分割器改变。SPLS_LINKED_RIGHT 与 SPLS_LINKED_LEFT 类似。SPLS_LINKED_UP 和 SPLS_LINKED_DOWN 如名称所示,控件将位于水平分割器的上方。
通常,需要设置分割器的移动范围。在基于文档-视图的应用程序中,这并不是很重要。但在基于对话框的应用程序中,必须自己处理窗口的边缘。因此,分割器的限制位置对于基于对话框的应用程序非常重要。
在可调整大小的对话框中,分割器的限制位置不是固定的。一旦改变对话框的大小,就必须改变分割器的新限制位置。这并不理想。在分割器控件中,它们在每次准备改变分割器位置之前都会向父窗口发送一个通知消息。因此,如果想使用这个功能,设置限制位置,只需处理通知消息即可。通知消息名为 SPN_MAXMINPOS。这里有一些示例代码。
并非所有事情都会自动完成。分割器需要在对话框初始化时将所有关联的控件注册到它。因此,如果一个控件在那时尚未创建,就不能将其注册到分割器。因此,分割器提供了另一种方式来改变这些控件的位置。核心函数是 CSplitterControl::ChangePos。这个函数接受一个参数 dwLinkedSide 来指定控件在分割器的哪一侧。而 lDelta 通常来自通知消息 SPN_DELTA。
感谢 Hung Nguyen 及其文章 Another splitter control for dialog 以及示例项目。这对帮助很大,示例代码也是基于此的。