xrea 发表于 2026-8-25 10:16:05

视窗组件自适应类库

本程序为开源MFC类库 MFC-Dialog-Control-Resizer 的火山版移植功能: 在对话框/窗口尺寸改变时自动调整全部或指定子控件的位置及尺寸
原项目地址: GITHUB.com类型开源项目, 遵循MIT许可证
<火山程序 类型 = "通常" 版本 = 1 />

包 火山.布局管理器 <注释 = "本程序为开源MFC类库 MFC-Dialog-Control-Resizer 的火山版移植" 注释 = "功能: 在对话框/窗口尺寸改变时自动调整全部或指定子控件的位置及尺寸"
      注释 = "原项目地址: GITHUB.com类型开源项目, 遵循MIT许可证">

# =====================================================
# 窗口布局API: 封装布局管理器内部用到的user32窗口API
# 对应MFC源码中的 ::GetWindow / ::IsWindow / ::GetWindowRect /
# ::ScreenToClient / ::MoveWindow / ::GetDlgItem 原生API调用
# =====================================================

类 窗口布局API <公开 注释 = "封装布局管理器内部所使用的Windows API, 一般无需直接使用" @类用途 = 访问静态成员>
{
    常量 子窗口 <公开 类型 = 整数 值 = 5 注释 = "GetWindow关系命令值: 取第一个子窗口">
    常量 下一个兄弟窗口 <公开 类型 = 整数 值 = 2 注释 = "GetWindow关系命令值: 取下一个兄弟窗口">

    方法 取关联窗口 <公开 静态 类型 = 变整数 注释 = "GetWindow: 返回与所指定窗口具有指定关系的窗口句柄" 返回值注释 = "返回所找到窗口的句柄, 失败返回0" @输出名 = "GetWindow"
            @视窗.输入 = "user32.dll">
    参数 窗口句柄 <类型 = 变整数 注释 = "提供原窗口句柄">
    参数 关系命令 <类型 = 整数 注释 = "提供欲取关系的命令值, 应为本类中的常量值">

    方法 窗口是否有效 <公开 静态 类型 = 逻辑型 注释 = "IsWindow: 判断所指定窗口句柄是否为一个有效窗口" 返回值注释 = "有效返回真, 否则返回假" @输出名 = "IsWindow"
            @视窗.输入 = "user32.dll">
    参数 窗口句柄 <类型 = 变整数>

    方法 取窗口屏幕矩形 <公开 静态 类型 = 逻辑型 注释 = "GetWindowRect: 返回所指定窗口在屏幕坐标系中的位置矩形" 返回值注释 = "成功返回真, 否则返回假" @输出名 = "GetWindowRect"
            @视窗.输入 = "user32.dll">
    参数 窗口句柄 <类型 = 变整数>
    参数 所取矩形 <类型 = 矩形结构 注释 = "所取回的屏幕矩形数据将被填入到此参数中" @视窗.输出参数 = 真>

    方法 屏幕坐标到用户区 <公开 静态 类型 = 逻辑型 注释 = "ScreenToClient: 将所指定点的坐标由屏幕坐标转换到所指定窗口用户区坐标" 返回值注释 = "成功返回真, 否则返回假"
            @输出名 = "ScreenToClient" @视窗.输入 = "user32.dll">
    参数 窗口句柄 <类型 = 变整数 注释 = "提供目标窗口的句柄">
    参数 所欲转换的坐标 <类型 = 坐标结构 注释 = "提供转换前的坐标, 转换后的结果将被写回到本参数中" @视窗.输出参数 = 真>

    方法 移动窗口 <公开 静态 类型 = 逻辑型 注释 = "MoveWindow: 改变所指定窗口的位置和尺寸" 返回值注释 = "成功返回真, 否则返回假" @输出名 = "MoveWindow" @视窗.输入 = "user32.dll">
    参数 窗口句柄 <类型 = 变整数>
    参数 左边 <类型 = 整数 注释 = "提供新的左边位置, 相对父窗口用户区左边">
    参数 顶边 <类型 = 整数 注释 = "提供新的顶边位置, 相对父窗口用户区顶边">
    参数 宽度 <类型 = 整数 注释 = "提供新的宽度">
    参数 高度 <类型 = 整数 注释 = "提供新的高度">
    参数 是否重画 <类型 = 逻辑型 注释 = "指定是否重画窗口">

    方法 取控件句柄 <公开 静态 类型 = 变整数 注释 = "GetDlgItem: 返回对话框中具有所指定标识值的控件窗口句柄" 返回值注释 = "返回所找到控件的句柄, 失败返回0" @输出名 = "GetDlgItem"
            @视窗.输入 = "user32.dll">
    参数 父窗口句柄 <类型 = 变整数 注释 = "提供对话框/父窗口的句柄">
    参数 控件标识 <类型 = 整数 注释 = "提供控件的标识值">
}

# =====================================================
# 矩形结构 / 坐标结构: 用作与Windows API交互的C结构对应类
# =====================================================

类 矩形结构 <公开 注释 = "对应Windows API中的RECT结构" @视窗.结构类 = 0>
{
    变量 左边 <公开 类型 = 整数>
    变量 顶边 <公开 类型 = 整数>
    变量 右边 <公开 类型 = 整数>
    变量 底边 <公开 类型 = 整数>
}

类 坐标结构 <公开 注释 = "对应Windows API中的POINT结构" @视窗.结构类 = 0>
{
    变量 横向 <公开 类型 = 整数>
    变量 纵向 <公开 类型 = 整数>
}

# =====================================================
# 控件信息类: 对应MFC源码中的 CLayoutManager::ControlInfo 结构
# =====================================================

类 控件信息类 <公开 注释 = "记录单个控件的初始布局信息, 对应MFC源码中的ControlInfo结构">
{
    变量 控件句柄 <公开 类型 = 变整数 注释 = "控件的窗口句柄">
    变量 初始矩形 <公开 类型 = 矩形类 注释 = "控件的初始位置矩形, 基于父窗口用户区左上角">
    变量 横向位置比例 <公开 类型 = 小数 注释 = "初始左边 / 初始客户区宽度">
    变量 纵向位置比例 <公开 类型 = 小数 注释 = "初始顶边 / 初始客户区高度">
    变量 宽度比例 <公开 类型 = 小数 注释 = "初始宽度 / 初始客户区宽度">
    变量 高度比例 <公开 类型 = 小数 注释 = "初始高度 / 初始客户区高度">
    变量 横向右边距 <公开 类型 = 整数 注释 = "初始客户区宽度 - (初始左边 + 初始宽度)">
    变量 纵向底边距 <公开 类型 = 整数 注释 = "初始客户区高度 - (初始顶边 + 初始高度)">
}

类 控件信息数组类 <公开 基础类 = 对象数组模板类 注释 = "控件信息对象的动态数组, 对应MFC源码中的std::vector<ControlInfo>" @模板实现类 = "控件信息类">

# =====================================================
# 布局管理器: 对应MFC源码中的 CLayoutManager 类
# 窗口尺寸改变时, 按初始比例同时调整全部子控件的位置和尺寸
# =====================================================

类 布局管理器 <公开 注释 = "对话框布局管理器, 对应MFC的CLayoutManager类" 注释 = "窗口尺寸改变时, 按初始比例同时调整全部子控件的位置和尺寸"
      注释 = "使用方法: 在窗口\"创建完毕\"事件中调用\"初始化\"方法, 在窗口\"尺寸被改变\"事件中调用\"更新布局\"方法">
{
    变量 控件列表 <类型 = 控件信息数组类 注释 = "所记录的全部控件布局信息">
    变量 原始客户区矩形 <类型 = 矩形类 注释 = "初始化时所记录的父窗口客户区矩形">
    变量 父窗口句柄 <类型 = 变整数 注释 = "初始化时所记录的父窗口句柄">

    方法 初始化 <公开 注释 = "记录父窗口中所有子控件的初始布局信息" 注释 = "对应MFC: CLayoutManager::Init (pParentDlg)">
    参数 父窗口 <类型 = 窗口组件 注释 = "提供所欲管理的窗口, 一般为本对象">
    {
      控件列表.删除所有成员 ()
      原始客户区矩形 = 父窗口.取用户区矩形 ()
      父窗口句柄 = 父窗口.取窗口句柄 ()

      // 遍历父窗口的所有直接子窗口, 对应MFC: ::GetWindow (GW_CHILD) / ::GetWindow (GW_HWNDNEXT) 循环
      变量 子窗口句柄 <类型 = 变整数>
      子窗口句柄 = 窗口布局API.取关联窗口 (父窗口句柄, 窗口布局API.子窗口)
      判断循环 (子窗口句柄 != 0)
      {
            加入控件信息 (父窗口句柄, 子窗口句柄)
            子窗口句柄 = 窗口布局API.取关联窗口 (子窗口句柄, 窗口布局API.下一个兄弟窗口)
      }
    }

    方法 添加控件 <公开 注释 = "添加一个指定控件到布局管理中, 已调用过\"初始化\"或\"添加控件\"后再次添加时不会重置原始客户区矩形"
            注释 = "对应MFC: CLayoutManager::AddControl (pParentDlg, controlID)">
    参数 父窗口 <类型 = 窗口组件 注释 = "提供所欲管理的窗口">
    参数 控件标识 <类型 = 整数 注释 = "提供控件的标识值, 即窗口布局设计中的id值">
    {
      如果 (控件列表.取成员数 () == 0)
      {
            原始客户区矩形 = 父窗口.取用户区矩形 ()
      }

      // 对应MFC: CWnd* pWnd = pParentDlg->GetDlgItem (controlID); if (!pWnd || !::IsWindow (...)) return;
      变量 控件句柄 <类型 = 变整数>
      控件句柄 = 窗口布局API.取控件句柄 (父窗口.取窗口句柄 (), 控件标识)
      如果 (控件句柄 == 0 || 窗口布局API.窗口是否有效 (控件句柄) == 假)
      {
            返回
      }

      父窗口句柄 = 父窗口.取窗口句柄 ()
      加入控件信息 (父窗口句柄, 控件句柄)
    }

    方法 更新布局 <公开 注释 = "按新的客户区尺寸等比例调整所有已记录控件的位置和尺寸" 注释 = "对应MFC: CLayoutManager::UpdateLayout (newWidth, newHeight)">
    参数 新宽度 <类型 = 整数 注释 = "提供父窗口新的客户区宽度">
    参数 新高度 <类型 = 整数 注释 = "提供父窗口新的客户区高度">
    {
      变量 当前信息 <类型 = 控件信息类>
      变量 新左边 <类型 = 整数>
      变量 新顶边 <类型 = 整数>
      变量 新宽度值 <类型 = 整数>
      变量 新高度值 <类型 = 整数>

      控件列表.枚举循环 ()
      {
            当前信息 = 控件列表.取枚举值 ()
            如果 (窗口布局API.窗口是否有效 (当前信息.控件句柄))
            {
                // 对应MFC: left = (int)(newWidth * xRatio) 等四个比例计算
                新左边 = (整数)(新宽度 * 当前信息.横向位置比例)
                新顶边 = (整数)(新高度 * 当前信息.纵向位置比例)
                新宽度值 = (整数)(新宽度 * 当前信息.宽度比例)
                新高度值 = (整数)(新高度 * 当前信息.高度比例)
                窗口布局API.移动窗口 (当前信息.控件句柄, 新左边, 新顶边, 新宽度值, 新高度值, 真)
            }
      }
    }

    方法 仅更新布局高度 <公开 注释 = "仅按新的客户区高度调整控件的位置和高度, 控件的左边和宽度保持当前值不变" 注释 = "对应MFC: CLayoutManager::UpdateLayoutHeightOnly (newHeight)">
    参数 新高度 <类型 = 整数 注释 = "提供父窗口新的客户区高度">
    {
      变量 当前信息 <类型 = 控件信息类>
      变量 当前矩形 <类型 = 矩形结构>
      变量 左上角 <类型 = 坐标结构>
      变量 右下角 <类型 = 坐标结构>
      变量 新顶边 <类型 = 整数>
      变量 新高度值 <类型 = 整数>

      控件列表.枚举循环 ()
      {
            当前信息 = 控件列表.取枚举值 ()
            如果 (窗口布局API.窗口是否有效 (当前信息.控件句柄))
            {
                // 取控件当前位置, 对应MFC: GetWindowRect + GetParent()->ScreenToClient
                窗口布局API.取窗口屏幕矩形 (当前信息.控件句柄, 当前矩形)
                左上角.横向 = 当前矩形.左边
                左上角.纵向 = 当前矩形.顶边
                窗口布局API.屏幕坐标到用户区 (父窗口句柄, 左上角)
                右下角.横向 = 当前矩形.右边
                右下角.纵向 = 当前矩形.底边
                窗口布局API.屏幕坐标到用户区 (父窗口句柄, 右下角)

                新顶边 = (整数)(新高度 * 当前信息.纵向位置比例)
                新高度值 = (整数)(新高度 * 当前信息.高度比例)
                窗口布局API.移动窗口 (当前信息.控件句柄, 左上角.横向, 新顶边, 右下角.横向 - 左上角.横向, 新高度值, 真)
            }
      }
    }

    方法 仅更新布局宽度 <公开 注释 = "仅按新的客户区宽度调整控件的位置和宽度, 控件的顶边和高度保持当前值不变" 注释 = "对应MFC: CLayoutManager::UpdateLayoutWidthOnly (newWidth)">
    参数 新宽度 <类型 = 整数 注释 = "提供父窗口新的客户区宽度">
    {
      变量 当前信息 <类型 = 控件信息类>
      变量 当前矩形 <类型 = 矩形结构>
      变量 左上角 <类型 = 坐标结构>
      变量 右下角 <类型 = 坐标结构>
      变量 新左边 <类型 = 整数>
      变量 新宽度值 <类型 = 整数>

      控件列表.枚举循环 ()
      {
            当前信息 = 控件列表.取枚举值 ()
            如果 (窗口布局API.窗口是否有效 (当前信息.控件句柄))
            {
                // 取控件当前位置, 对应MFC: GetWindowRect + GetParent()->ScreenToClient
                窗口布局API.取窗口屏幕矩形 (当前信息.控件句柄, 当前矩形)
                左上角.横向 = 当前矩形.左边
                左上角.纵向 = 当前矩形.顶边
                窗口布局API.屏幕坐标到用户区 (父窗口句柄, 左上角)
                右下角.横向 = 当前矩形.右边
                右下角.纵向 = 当前矩形.底边
                窗口布局API.屏幕坐标到用户区 (父窗口句柄, 右下角)

                新左边 = (整数)(新宽度 * 当前信息.横向位置比例)
                新宽度值 = (整数)(新宽度 * 当前信息.宽度比例)
                窗口布局API.移动窗口 (当前信息.控件句柄, 新左边, 左上角.纵向, 新宽度值, 右下角.纵向 - 左上角.纵向, 真)
            }
      }
    }

    方法 加入控件信息 <注释 = "计算并记录单个控件的布局信息, 供本类内部使用">
    参数 父句柄 <类型 = 变整数 注释 = "提供父窗口句柄">
    参数 控件句柄 <类型 = 变整数 注释 = "提供控件的句柄">
    {
      // 取控件屏幕矩形, 对应MFC: pChild->GetWindowRect (&rect)
      变量 屏幕矩形 <类型 = 矩形结构>
      窗口布局API.取窗口屏幕矩形 (控件句柄, 屏幕矩形)

      // 转换到父窗口用户区坐标, 对应MFC: pParentDlg->ScreenToClient (&rect) (转换矩形的两个角点)
      变量 左上角 <类型 = 坐标结构>
      变量 右下角 <类型 = 坐标结构>
      左上角.横向 = 屏幕矩形.左边
      左上角.纵向 = 屏幕矩形.顶边
      窗口布局API.屏幕坐标到用户区 (父句柄, 左上角)
      右下角.横向 = 屏幕矩形.右边
      右下角.纵向 = 屏幕矩形.底边
      窗口布局API.屏幕坐标到用户区 (父句柄, 右下角)

      变量 控件宽度 <类型 = 整数>
      变量 控件高度 <类型 = 整数>
      控件宽度 = 右下角.横向 - 左上角.横向
      控件高度 = 右下角.纵向 - 左上角.纵向

      // 记录控件信息, 对应MFC: ControlInfo info 结构赋值
      变量 信息 <类型 = 控件信息类>
      信息.控件句柄 = 控件句柄
      信息.初始矩形.左边 = 左上角.横向
      信息.初始矩形.顶边 = 左上角.纵向
      信息.初始矩形.右边 = 右下角.横向
      信息.初始矩形.底边 = 右下角.纵向
      信息.横向位置比例 = (小数)左上角.横向 / (小数)原始客户区矩形.宽度
      信息.纵向位置比例 = (小数)左上角.纵向 / (小数)原始客户区矩形.高度
      信息.宽度比例 = (小数)控件宽度 / (小数)原始客户区矩形.宽度
      信息.高度比例 = (小数)控件高度 / (小数)原始客户区矩形.高度
      信息.横向右边距 = 原始客户区矩形.宽度 - (左上角.横向 + 控件宽度)
      信息.纵向底边距 = 原始客户区矩形.高度 - (左上角.纵向 + 控件高度)

      控件列表.加入成员 (信息)
    }
}

# =====================================================
# 固定位置布局管理器: 对应MFC源码中的 CLayoutManagerFixedPos 类
# 窗口尺寸改变时, 控件位置保持初始值不变, 仅调整尺寸以维持原始边距
# =====================================================

类 固定位置布局管理器 <公开 注释 = "固定位置布局管理器, 对应MFC的CLayoutManagerFixedPos类" 注释 = "窗口尺寸改变时, 控件位置保持初始值不变, 仅调整其宽高以保持初始的右边距和底边距"
      注释 = "使用方法: 在窗口\"创建完毕\"事件中调用\"初始化\"或\"添加控件\"方法, 在窗口\"尺寸被改变\"事件中调用\"更新布局\"方法">
{
    变量 控件列表 <类型 = 控件信息数组类 注释 = "所记录的全部控件布局信息">
    变量 原始客户区矩形 <类型 = 矩形类 注释 = "初始化时所记录的父窗口客户区矩形">
    变量 父窗口句柄 <类型 = 变整数 注释 = "初始化时所记录的父窗口句柄">

    方法 初始化 <公开 注释 = "记录父窗口中所有子控件的初始布局信息" 注释 = "对应MFC: CLayoutManagerFixedPos::Init (pParentDlg)">
    参数 父窗口 <类型 = 窗口组件 注释 = "提供所欲管理的窗口, 一般为本对象">
    {
      控件列表.删除所有成员 ()
      原始客户区矩形 = 父窗口.取用户区矩形 ()
      父窗口句柄 = 父窗口.取窗口句柄 ()

      // 遍历父窗口的所有直接子窗口, 对应MFC: ::GetWindow (GW_CHILD) / ::GetWindow (GW_HWNDNEXT) 循环
      变量 子窗口句柄 <类型 = 变整数>
      子窗口句柄 = 窗口布局API.取关联窗口 (父窗口句柄, 窗口布局API.子窗口)
      判断循环 (子窗口句柄 != 0)
      {
            加入控件信息 (父窗口句柄, 子窗口句柄)
            子窗口句柄 = 窗口布局API.取关联窗口 (子窗口句柄, 窗口布局API.下一个兄弟窗口)
      }
    }

    方法 添加控件 <公开 注释 = "添加一个指定控件到布局管理中, 已调用过\"初始化\"或\"添加控件\"后再次添加时不会重置原始客户区矩形"
            注释 = "对应MFC: CLayoutManagerFixedPos::AddControl (pParentDlg, controlID)">
    参数 父窗口 <类型 = 窗口组件 注释 = "提供所欲管理的窗口">
    参数 控件标识 <类型 = 整数 注释 = "提供控件的标识值, 即窗口布局设计中的id值">
    {
      如果 (控件列表.取成员数 () == 0)
      {
            原始客户区矩形 = 父窗口.取用户区矩形 ()
      }

      // 对应MFC: CWnd* pWnd = pParentDlg->GetDlgItem (controlID); if (!pWnd || !::IsWindow (...)) return;
      变量 控件句柄 <类型 = 变整数>
      控件句柄 = 窗口布局API.取控件句柄 (父窗口.取窗口句柄 (), 控件标识)
      如果 (控件句柄 == 0 || 窗口布局API.窗口是否有效 (控件句柄) == 假)
      {
            返回
      }

      父窗口句柄 = 父窗口.取窗口句柄 ()
      加入控件信息 (父窗口句柄, 控件句柄)
    }

    方法 更新布局 <公开 注释 = "控件位置保持初始值不变, 按新的客户区尺寸调整控件宽高以维持初始边距"
            注释 = "对应MFC: CLayoutManagerFixedPos::UpdateLayout (newWidth, newHeight)">
    参数 新宽度 <类型 = 整数 注释 = "提供父窗口新的客户区宽度">
    参数 新高度 <类型 = 整数 注释 = "提供父窗口新的客户区高度">
    {
      变量 当前信息 <类型 = 控件信息类>
      变量 宽度值 <类型 = 整数>
      变量 高度值 <类型 = 整数>

      控件列表.枚举循环 ()
      {
            当前信息 = 控件列表.取枚举值 ()
            如果 (窗口布局API.窗口是否有效 (当前信息.控件句柄))
            {
                // 位置保持初始值, 尺寸 = 新客户区 - 初始位置 - 原始边距
                宽度值 = 新宽度 - 当前信息.初始矩形.左边 - 当前信息.横向右边距
                高度值 = 新高度 - 当前信息.初始矩形.顶边 - 当前信息.纵向底边距
                窗口布局API.移动窗口 (当前信息.控件句柄, 当前信息.初始矩形.左边, 当前信息.初始矩形.顶边, 宽度值, 高度值, 真)
            }
      }
    }

    方法 仅更新布局高度 <公开 注释 = "控件位置和宽度保持初始值不变, 仅按新的客户区高度调整控件高度以维持初始底边距"
            注释 = "对应MFC: CLayoutManagerFixedPos::UpdateLayoutHeightOnly (newHeight)">
    参数 新高度 <类型 = 整数 注释 = "提供父窗口新的客户区高度">
    {
      变量 当前信息 <类型 = 控件信息类>
      变量 高度值 <类型 = 整数>

      控件列表.枚举循环 ()
      {
            当前信息 = 控件列表.取枚举值 ()
            如果 (窗口布局API.窗口是否有效 (当前信息.控件句柄))
            {
                高度值 = 新高度 - 当前信息.初始矩形.顶边 - 当前信息.纵向底边距
                窗口布局API.移动窗口 (当前信息.控件句柄, 当前信息.初始矩形.左边, 当前信息.初始矩形.顶边, 当前信息.初始矩形.右边 - 当前信息.初始矩形.左边, 高度值, 真)
            }
      }
    }

    方法 加入控件信息 <注释 = "计算并记录单个控件的布局信息, 供本类内部使用">
    参数 父句柄 <类型 = 变整数 注释 = "提供父窗口句柄">
    参数 控件句柄 <类型 = 变整数 注释 = "提供控件的句柄">
    {
      // 取控件屏幕矩形, 对应MFC: pChild->GetWindowRect (&rect)
      变量 屏幕矩形 <类型 = 矩形结构>
      窗口布局API.取窗口屏幕矩形 (控件句柄, 屏幕矩形)

      // 转换到父窗口用户区坐标, 对应MFC: pParentDlg->ScreenToClient (&rect) (转换矩形的两个角点)
      变量 左上角 <类型 = 坐标结构>
      变量 右下角 <类型 = 坐标结构>
      左上角.横向 = 屏幕矩形.左边
      左上角.纵向 = 屏幕矩形.顶边
      窗口布局API.屏幕坐标到用户区 (父句柄, 左上角)
      右下角.横向 = 屏幕矩形.右边
      右下角.纵向 = 屏幕矩形.底边
      窗口布局API.屏幕坐标到用户区 (父句柄, 右下角)

      变量 控件宽度 <类型 = 整数>
      变量 控件高度 <类型 = 整数>
      控件宽度 = 右下角.横向 - 左上角.横向
      控件高度 = 右下角.纵向 - 左上角.纵向

      // 记录控件信息, 对应MFC: ControlInfo info 结构赋值
      变量 信息 <类型 = 控件信息类>
      信息.控件句柄 = 控件句柄
      信息.初始矩形.左边 = 左上角.横向
      信息.初始矩形.顶边 = 左上角.纵向
      信息.初始矩形.右边 = 右下角.横向
      信息.初始矩形.底边 = 右下角.纵向
      信息.横向位置比例 = (小数)左上角.横向 / (小数)原始客户区矩形.宽度
      信息.纵向位置比例 = (小数)左上角.纵向 / (小数)原始客户区矩形.高度
      信息.宽度比例 = (小数)控件宽度 / (小数)原始客户区矩形.宽度
      信息.高度比例 = (小数)控件高度 / (小数)原始客户区矩形.高度
      信息.横向右边距 = 原始客户区矩形.宽度 - (左上角.横向 + 控件宽度)
      信息.纵向底边距 = 原始客户区矩形.高度 - (左上角.纵向 + 控件高度)

      控件列表.加入成员 (信息)
    }
}


nurjay 发表于 2026-8-25 12:01:40

辛苦了!!!高才生!

guangye1 发表于 2026-8-25 12:56:58

终于盼来了。厉害厉害

安若清风 发表于 2026-8-25 16:10:41

厉害,学习了

dengzf 发表于 2026-8-26 08:30:49

测试发现,在分组框内的组件没处理
页: [1]
查看完整版本: 视窗组件自适应类库