白易 Win32 窗口 UI 模块开发规范
字符集规范引用:在白易 Win32 相关系统 API 调用中一律采用 W版字符集 (UTF-16)。
[!IMPORTANT] 开发前必看与参考示例: 在开始编写或修改任何窗口 UI 控件或容器模块前,AI/开发者务必优先查阅以下路径下的实际控件实现源码:
$BEInstall\BEModSrc\BEWin32UI\BEWin32UI\中文UI其中最具有代表性的三个核心实现范本为:
控件类与容器类开发核心准则
编写新的 UI 控件或容器时,必须严格遵循以下架构设计:
1. 控件类开发规范 (以 按钮.cpp 为参考)
- 参数结构体规范:
-
关于
static _df的约定:该静态属性只声明,不定义:必须且只需在头文件
参数结构体定义的末尾进行静态成员声明:}static _df;。不要在.cpp源文件中写诸如按钮::参数 按钮::_df;这样的外部定义!该静态成员是在以后需要用到导出控件元信息(如反射默认值)时由元信息端去统一处理定义的。现在仅仅编写/设计 UI 控件类自身时,绝对不要在类外部进行任何定义。
-
- 创建逻辑流程 (
创建方法):- 必须首先调用基类初始化方法:
控件类::_子初始(cs, 父窗口, 父句柄);(若需禁用 Tab 焦点,第四个参数传入true)。 - 创建原生句柄(
CreateWindowExW)时,所有坐标与尺寸(左边,顶边,宽度,高度)必须使用dpi()函数进行 DPI 缩放(如dpi(左边))。 - 句柄创建成功后,务必可选调用
控件类::_子类化();以接管窗口消息过程。
- 必须首先调用基类初始化方法:
- 消息与事件反射:
- 在重写的
挂接消息虚方法中处理完所需消息后,必须返回窗口基类::挂接消息(hwnd, msg, wParam, lParam)。 - 子控件的部分Win32原生核心事件(如单击,这个是由Windows自带控件设定的默认通过父容器接收),而白易通过统一设计的父容器层拦截消息后,以
BE_REFLECT + 消息ID再反射转发回来(如BE_REFLECT + WM_COMMAND到达本层并触发事件_被单击())。
- 在重写的
2. 容器类 (容器类) 开发规范 (以 分组框.cpp 为参考)
- 必须实现的纯虚接口:
- 继承自
容器类的类必须重写并实现_取背景颜色和背景颜色_这两方法,以支持子控件透明绘制背景擦除需求。
- 继承自
- 创建与窗口风格:
- 创建时必须调用
容器类::_子初始(cs, 父窗口, 父句柄)。 - 关键扩展风格:
CreateWindowExW的第一个参数dwExStyle必须包含WS_EX_CONTROLPARENT。若遗漏此风格,会导致容器内子控件的 Tab 键焦点导航穿透失效,用户无法通过键盘切换焦点。 - 窗口风格
_窗口风格需叠加WS_CLIPCHILDREN | WS_CLIPSIBLINGS以防止重叠绘制和背景闪烁。
- 创建时必须调用
- 自绘与区域裁剪:
- 若需在
WM_PAINT中进行背景自绘或定制(如分组框绘制标题与擦除 etched 边框),应先通过CallWindowProcW(_原窗口过程, ...)让原生控件绘制,再通过 GDI 裁剪(如IntersectClipRect、FillRect或调用UI_请求父窗绘制背景)来精准覆盖指定区域,防止文字和子控件区域产生绘制残留或背景穿透。
- 若需在
- 生命周期与内存管理:
- 容器类内部拥有
_组件列表(Arraybe<窗口基类*>) 成员。通过_子入父列表(),所有子控件会被自动添加至该列表。 - 容器销毁(
~容器类()或销毁())时,会自动遍历_组件列表调用it->销毁(),并对堆上申请的控件自动调用delete释放。 - 避坑警告:开发者绝不能手动二次 delete 已交给父容器托管的堆内存子控件。
- 容器类内部拥有
3. 图片组 (Image List) 技术应用规范 (以 选择夹.cpp 为参考)
- 核心原理与成员声明:
- 框架内的
图片组类 (定义于 位图.h) 是对 Win32 原生HIMAGELIST句柄的安全封装。 - 凡是需要图像列表支持的复杂控件(如选择夹
SysTabControl32、列表框SysListView32、树视图SysTreeView32等),应在类保护/私有成员中定义一个图片组实例:图片组 _图片组;。
- 框架内的
- 图片组设置与拷贝 (
置图片组):- 必须提供公开的设置接口
void 置图片组(图片组& imageList)。 - 内部实现应调用
_图片组.从图片组复制(imageList);进行深拷贝/资源复制,然后通过SendMessageW将图片组底层的 HIMAGELIST 句柄_图片组.h发送给原生控件进行绑定。 - 示例:
void 置图片组(图片组& imageList) { _图片组.从图片组复制(imageList); SendMessageW(窗口句柄, TCM_SETIMAGELIST, 0, (LPARAM)(_图片组.h)); }
- 必须提供公开的设置接口
4. 框架内置核心宏的定义、原理与使用场景
为了简化 Win32 GUI 开发中大量的结构体定义、属性反射及资源管理,白易框架内置了三个非常核心的属性包装宏(均定义于公共辅助头文件 窗口常量.h):
1) 定义_标题成员
- 宏定义内容:
#define 定义_标题成员 StrW 标题;\ void 标题_(const StrW& title){ 标题 = title; 窗口_置标题(窗口句柄, title); 重画(); } - 使用场景:
适用于任何包含“文字标题/标签属性”的原生及自定义控件(如
按钮,标签,窗口等)。 - 作用与原理:
- 声明了一个公有的
StrW 标题成员,该属性为只读(直接赋值不触发 API 行为)。 - 提供了一个 Setter 方法
void 标题_(const StrW& title),用于修改标题变量、调用窗口_置标题原生 API 更新句柄文本,并调用重画()重新绘制。
- 声明了一个公有的
2) 定义_字体成员
- 宏定义内容:
#define 定义_字体成员 void 置字体(const 字体& f){窗口基类::置字体(f);}\ 字体 取字体(){return 窗口基类::取字体();}\ HFONT 取字体句柄(){return 窗口基类::取字体句柄();\ } - 使用场景: 几乎所有包含文本展示的 UI 控件类。
- 作用与原理:
在控件类中快速注入
置字体()、取字体()与取字体句柄()成员函数,直接委托给底层的窗口基类字体引擎进行字体的动态刷新和缓存管理,保证了字体应用接口在所有组件中的一致性。
3) 定义_背景颜色成员
- 宏定义内容:
#define 定义_背景颜色成员 颜色 背景颜色;\ 颜色 _取背景颜色(颜色** out=0){ if(out)*out=&背景颜色; return 背景颜色; }\ void 背景颜色_(颜色 cr, bool 是否重画 = true) {\ 窗口基类::背景颜色_(cr,是否重画); \ } - 使用场景:
适用于需要自绘背景颜色、透明背景,或实现了容器机制的类(如
窗口,分组框,选择夹等)。 - 作用与原理:
- 声明了
颜色 背景颜色成员,并提供了背景颜色_()方法来修改并更新画刷。 - 自动实现了基类
容器类要求的纯虚接口_取背景颜色,为子组件背景透明自愈机制提供了坚实的框架底层反射支持。
- 声明了
附录设计条例:
条例1. 成员属性的主要命名
- 正式窗口成员属性:使用
public权限。属性名使用中文(如左边,可视,标题)。- 这些属性在实际意义上是只读的(在外部修改其值不会立即触发系统行为)。
- 属性修改方法:如果其为可修改的属性,假定属性为
可视,则会有对应的修改方法——且命名为可视_(bool is)(结尾带下划线)。- 原因:避免 C++ 符号重定义。
- 含义:用户看到
_结尾的方法在“形象层面”用来表示的是后面是用来填内容的。
- 内部/私有成员:命名一律以
_开头(如_组件列表,_画刷句柄)。- 主要用于 IDE 识别并排除在用户常用选项之外。
条例2. 即时生效的特殊属性
- 对于在消息响应中需要同步识别的变量(如
Esc键关闭_,随意移动_),可以允许直接通过属性更改。 - 命名规则:属性名自带
_结尾。
条例3. 复杂内核结构的属性
- 避免在应用层类中直接存储复杂的 Win32 内核结构(如
LOGFONT,不直接提供在对外用户层)。 - 处理方式:
- 内部存储句柄(使用框架自带的智能句柄类,如
HandleFONT _字体句柄)。 - 提供简化的中介结构体(如
struct 字体)仅用作下面函数的返回值进行临时的设置和取出(当然必要时可设计为内部的成员属性进行缓存)。 - 方法命名:使用
置字体(font)和取字体()。看到“置/取”应意识到不存在对应的直接成员属性。
- 内部存储句柄(使用框架自带的智能句柄类,如
条例4. DPI 感知与虚拟坐标
- 设计单位:以虚拟像素单位 (vpx) 为主,与 DPI 无关。
- 转换逻辑:框架内部使用
dpi(vpx)进行转换。 - 模板封装:
- 核心逻辑使用带模板参数
<D>(表示Device) 的函数。 - 常规方法不带模板,内部调用
dpi()后转发。 - 示例:
template <class D> void 重画(RECT r);->void 重画(RECT r) { 重画<D>( dpi(r) ); }。
- 核心逻辑使用带模板参数
条例5. UI 参数初始化
- 子组件创建参数:前 6 个参数必须为:
左边,顶边,宽度,高度(int) 以及可视,禁止(bool)。 - 可选参数:使用
可空<类型>。- 用于默认值需动态确定(如
可视由nCmdShow决定)或多选一的情况。
- 用于默认值需动态确定(如
- 支持类型:
- 基本类型:
bool,char,short,int,int64,u开头各种整数型,Bytes,StrW。 - 特殊类型:
颜色,字体,定义_枚举型。
- 基本类型:
- 默认值参考:仅需给出静态成员
static _df;声明(在struct 参数结构体定义的末尾写}static _df;即可),用于 IDE 属性反射默认值。