视窗组件自适应类库
本程序为开源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 结构赋值
变量 信息 <类型 = 控件信息类>
信息.控件句柄 = 控件句柄
信息.初始矩形.左边 = 左上角.横向
信息.初始矩形.顶边 = 左上角.纵向
信息.初始矩形.右边 = 右下角.横向
信息.初始矩形.底边 = 右下角.纵向
信息.横向位置比例 = (小数)左上角.横向 / (小数)原始客户区矩形.宽度
信息.纵向位置比例 = (小数)左上角.纵向 / (小数)原始客户区矩形.高度
信息.宽度比例 = (小数)控件宽度 / (小数)原始客户区矩形.宽度
信息.高度比例 = (小数)控件高度 / (小数)原始客户区矩形.高度
信息.横向右边距 = 原始客户区矩形.宽度 - (左上角.横向 + 控件宽度)
信息.纵向底边距 = 原始客户区矩形.高度 - (左上角.纵向 + 控件高度)
控件列表.加入成员 (信息)
}
}
辛苦了!!!高才生! 终于盼来了。厉害厉害 厉害,学习了 测试发现,在分组框内的组件没处理
页:
[1]