MFC对话框自动调整(Automatic Dialog Resizing) -- 翻译 Dmitry Kochin

Every GUI developer uses dialogs. Sometimes, dialogs should support resizing to make the user's life easier. To create a resizable dialog, you just need to handle a WM_SIZE message and resize/move child controls. But, when a dialog contains many controls, resizing it would be a pain for the developer, and the OnSize function will look like a monster!

每个界面开发人员都会用到对话框,对话框类有时候应该支持动态调整大小,这样可以让用户使用起来稍微方便些。要创建一个可调整大小的对话框,你应该处理一下WM_SIZE消息,并且调整/移动子控件。但是如果一个对话框中包含大量的控件,动态调整就是件痛苦的工作,OnSize函数也会变得异常臃肿。

When I ran into this problem, I wrote a simple resizer that could help significantly in making resizable dialogs. All you have to do now is:

  1. Create a dialog resource and position the controls as you wish.
  2. Make CResizer m_resizer a member of your dialog class.
  3. Initialize the resizer in OnInitDialog (m_resizer.Init(...)).
  4. Move controls in OnSize simply by calling m_resizer.Move()!
当我遇到这个问题时,我写了一个简单的类,这个类可以让调整对话框这项工作更简单些。现在你只要做以下操作:

        1、创建一个对话框资源,然后把它放置到你希望放置到位置。

        2、在需要做动态调整的对话框中加入CResizer m_resizer类。

        3、在OnInitDialog 函数中初始化(m_resizer.Init(...))。

        4、在OnSize 函数中调用m_resizer.Move()!

Let's consider the implementation of CResizer class in detail.

让我们考虑下CResizer 类的具体实现。

The main idea is the following: Each side of a child window (left, top, right, and bottom) is connected to a side of another window, the so-called relative window. The dialog window that owns the children also can be a relative window. When the dialog window is resized, the child window sides are moved after the relative window, thus preserving the connections.

主要思路如下:每个子窗口的边 (左,上, 右,和底)都和另外一个窗口的边相连,这就是所谓的相关窗口。包含子窗口的对话框窗口也可以是相关窗口。当对话框窗口大小变化后,子窗口的边也会相应移动,以此确保相关性。

There are several types of connections:

有以下几种关联方式:

Connection TypeConstant NameDescription
FixedCResizer::eFixedPreserves the distance between the side of the child window and the specified side of the relative window
ProportionalCResizer::eProportionalPreserves the ratio of distance between the side of the child window and left (top) side of the relative window to width (height) of the relative window.
Preserving widthCResizer::eWidthAn auxiliary connection that preserves the width of the specified child window. It also can be implemented with a fixed connection.
Preserving heightCResizer::eHeightAn auxiliary connection that preserves the height of the specified child window. It also can be implemented with a fixed connection.
关联类型常量名称描述
固定值CResizer::eFixed确保子窗口边框和相关窗口的指定边的距离不变
按比例CResizer::eProportional确保子窗口与左(上)方的相关窗口的距离与相关窗口的宽(高)比例不变。
宽度不变CResizer::eWidth这种连接方式比较灵活,指定的子窗口宽度不变。这种方式也可通过固定值方式实现。
高度不变CResizer::eHeight这种连接方式比较灵活,指定的子窗口高度不变。这种方式也可通过固定值方式实现。

The connections for child windows are specified in a static array of the CResizer::CBorderInfo structures. The format is as follows:

这些子窗口的关联关系可以通过CResizer::CBorderInfo 的一个静态结构体来指定格式如下:

 
 
  1. {IDC_CTRL_ID, {(conn type), (rel id), (rel side)}, //Left side
  2. {(conn type), (rel id), (rel side)}, //Top side
  3. {(conn type), (rel id), (rel side)}, //Right side
  4. {(conn type), (rel id), (rel side)}, //Bottom side

Here, (conn type) is one of the connection types listed above, (rel id) is an identifier of the relative window or IDC_MAIN if you want to reference parent dialog, and (rel side) is the side of the relative window to which a connection is bound.

conn type)是上表中列出的关联方式的其中一种, (rel id)是相关窗口的一个标识或 IDC_MAIN IDC_MAIN指定了子窗口与父窗口保持相关性,(rel side)是与相关窗口产生关联的边。

Each child window has four sides, so it has four connections—for left, top, right, and bottom. They are listed subsequently in the CResizer::CBorderInfo initialization above.

Let's consider the connection format (CResizer::CBorder structure) in more detail.

每个子窗口都有四个边,因此它包含四个关联边--即左,上,右,下。在 CResizer::CBorderInfo中已经列出。让我们看看这种结构 (CResizer::CBorder 结构体)的细节。

 
 
  1. {(conn type) , (rel id) , (rel side) }
  2.  
  3. CResizer::eFixed CResizer::eLeft
  4. CResizer::eProportional IDC_MAIN CResizer::eTop
  5. {CResizer::eWidth , IDC_CTRL_ID , CResizer::eRight }
  6. CResizer::eHeight CResizer::eBottom
  7. CResizer::eXCenter
  8. CResizer::eYCenter

A fixed connection can bind the child window side not only to a side of the relative window, but also to its horizontal (CResizer::eXCenter) or vertical (CResizer::eYCenter) center line.

一个固定关联值不仅可以被绑定到子窗口的边,而且可以被应用到它的水平(CResizer::eXCenter)或垂直(CResizer::eYCenter)中心点。

A proportional connection needs only the width or height as relative window side. To specify width, use one of the horizontal sides (eLeft, eRight, or eXCenter); to specify height, use one of vertical sides (eTop, eBottom, or eYCenter).

一种连接属性只需要宽度或高度作为相关窗口的边。如果指定宽度,使用水平边的一种就好(eLeft, eRight, 或eXCenter);如果要指定高度,使用垂直方向的一种就好 (eTop, eBottom, 或 eYCenter)。

Connections preserving width or height don't require relative window info. So, there can be any.

保留宽度和高度的关联性不需要相关窗口信息。因此,啥都行。

Typical Usage

典型用法

  1. Make CResizer m_resizer a member variable of your dialog class.在你的对话框类中添加 CResizer m_resizer变量。
  2. Add the following code to OnInitDialog(), replacing the control IDs to your specific ones and adjusting connections as you wish (described above).添加以下代码到你的 OnInitDialog()函数中,用你的ID替换数据结构体中的数据,用上面的方法调整你想要的关联关系。
       
       
    1. static const CResizer::CBorderInfo s_bi[] = {
    2. {IDC_CTRL1, {CResizer::eFixed, IDC_MAIN, CResizer::eLeft}, //l
    3. {CResizer::eFixed, IDC_MAIN, CResizer::eTop}, //t
    4. {CResizer::eFixed, IDC_MAIN, CResizer::eRight}, //r
    5. {CResizer::eFixed, IDC_MAIN, CResizer::eBottom}},//b
    6. {IDC_CTRL2, {CResizer::eFixed, IDC_MAIN, CResizer::eLeft},
    7. {CResizer::eFixed, IDC_MAIN, CResizer::eBottom},
    8. {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
    9. {CResizer::eFixed, IDC_MAIN, CResizer::eBottom}},
    10. };
    11. const nSize = sizeof(s_bi)/sizeof(s_bi[0]);
    12. m_resizer.Init(m_hWnd, NULL, s_bi, nSize);
  3. Add the following code to the OnSize() handler:添加以下代码到OnSize()函数中。
       
       
    1. m_resizer.Move();
  4. Everything should work now.欧了

Some Final Tips(小提示)

  1. Resizer resizes controls in the order they are defined in the array, so (rel id) should always be defined (and, therefore, moved by the resizer) before it is used as relative window. Otherwise, resizer ASSERTs.
  2. Windows that are defined earlier in the array have higher priority. When windows have too small a size and child windows overlap, the window with lower priority is hidden.
  3. If you need to limit minimum or maximum window size, you can handle the WM_GETMINMAXINFO message.

小提示

  1. 这个类可以动态调整数组内的所有控件,因此 (rel id) 应该在它们被当作相对窗口使用之前就应该被定义(这样才可以被resizer移动),否则就会报错。
  2. 在数组中先被定义的窗口具有较高的优先级。当窗口太小并且定义中有窗口重贴情况,低优先级的窗口就会被隐藏。
  3. 如果你需要限制窗口最大和最小化尺寸,你需要处理 WM_GETMINMAXINFO消息.

Downloads(代码和示例下载地址)

Download demo project - 103 Kb
Download source - 5 Kb




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值