28 KiB
28 KiB
EzUI Control 基类权威指南
本文档是 EzUI C++ UI 框架的核心参考资料,面向初学者入门。
目录
1. 类概述
1.1 Control 在 UI 库架构中的定位
Control 是 EzUI 框架中所有 UI 控件的基类,位于 include/EzUI/Control.h 中。它继承了 Object 类,是构建整个 UI 界面的核心抽象。
Object (基类)
│
└── Control (UI控件基类)
├── Label (文本显示)
│ └── Button (按钮)
├── TextBox (文本框)
├── ComboBox (下拉框)
├── CheckBox / RadioButton
├── PictureBox (图片)
├── ProgressBar (进度条)
├── HLayout / VLayout (布局容器)
├── HListView / VListView / TileListView / PagedListView (列表)
├── TreeView (树形视图)
├── TableView (表格)
├── TabLayout (标签页)
└── ScrollBar / HScrollBar / VScrollBar (滚动条)
注意:
Window类并非直接继承自 Control,而是继承自 Object。它是顶级窗口,与 Control 是并行的类层次结构。
1.2 Control 的核心职责
Control 类承担以下四大职责:
| 职责 | 描述 |
|---|---|
| 渲染 | 管理控件的外观绘制,包括背景、前景、边框 |
| 布局 | 计算控件的位置和尺寸,处理子控件的排列 |
| 事件处理 | 接收和响应鼠标、键盘等用户交互事件 |
| 状态管理 | 维护控件的可见性、启用状态、悬停/按下等状态 |
1.3 快速入门示例
#include <EzUI.h>
using namespace EzUI;
int main() {
Application::Init();
Window window;
window.SetSize(800, 600);
window.SetTitle("Control Demo");
// 创建布局容器
VLayout* layout = new VLayout();
window.Add(layout);
// 创建按钮
Button* btn = new Button();
btn->SetText("Click Me");
btn->SetFixedSize(120, 40);
btn->SetLocation(100, 100);
// 绑定事件
btn->EventHandler = [](Control* sender, EventArgs& args) {
if (args.EventType == Event::OnMouseUp) {
MessageBox(NULL, "Button Clicked!", "Info", MB_OK);
}
};
layout->Add(btn);
window.Show();
return Application::Run();
}
2. 核心属性详解
2.1 几何属性
2.1.1 位置属性
| 属性 | 类型 | 作用 |
|---|---|---|
X() |
int |
获取控件相对于父容器的 X 坐标 |
Y() |
int |
获取控件相对于父容器的 Y 坐标 |
SetX(int) |
void |
设置 X 坐标 |
SetY(int) |
void |
设置 Y 坐标 |
SetLocation(const Point&) |
void |
同时设置 X 和 Y 坐标 |
代码示例:
// 设置位置的三种方式
control->SetX(100);
control->SetY(200);
// 或
control->SetLocation(Point(100, 200));
// 获取位置
Point pos = control->GetLocation(); // 等价于 Point(control->X(), control->Y())
2.1.2 尺寸属性
| 属性 | 类型 | 作用 |
|---|---|---|
Width() |
int |
获取控件宽度 |
Height() |
int |
获取控件高度 |
SetWidth(int) |
void |
设置宽度 |
SetHeight(int) |
void |
设置高度 |
SetSize(const Size&) |
void |
同时设置宽高 |
SetFixedSize(const Size&) |
void |
设置固定尺寸(优先级最高) |
SetFixedWidth(int) |
void |
设置固定宽度 |
SetFixedHeight(int) |
void |
设置固定高度 |
SetRateWidth(float) |
void |
设置宽度为父容器的百分比 (0.0-1.0) |
SetRateHeight(float) |
void |
设置高度为父容器的百分比 (0.0-1.0) |
GetRect() |
const Rect& |
获取控件矩形区域(布局计算后) |
代码示例:
// 固定尺寸
control->SetFixedSize(Size(200, 100));
control->SetFixedWidth(200);
control->SetFixedHeight(100);
// 百分比尺寸(响应式布局)
control->SetRateWidth(0.5f); // 宽度为父容器的50%
control->SetRateHeight(0.25f); // 高度为父容器的25%
// 自动尺寸
control->SetAutoWidth(true);
control->SetAutoHeight(true);
control->SetAutoSize(true); // 同时设置自动宽高
2.1.3 外边距 (Margin)
| 属性 | 类型 | 作用 |
|---|---|---|
Margin |
Distance |
设置控件外边距,影响在父容器中的布局 |
代码示例:
// 设置外边距
control->Margin.Left = 10;
control->Margin.Top = 20;
control->Margin.Right = 10;
control->Margin.Bottom = 20;
// 或使用 Distance 构造函数
control->Margin = Distance(10, 20); // 左+右=10, 上+下=20
2.1.4 停靠样式 (Dock)
| 属性 | 类型 | 作用 |
|---|---|---|
GetDockStyle() |
DockStyle |
获取停靠样式 |
SetDockStyle(const DockStyle&) |
void |
设置停靠样式 |
DockStyle 枚举值:
| 值 | 描述 |
|---|---|
DockStyle::None |
不停靠,使用绝对定位 |
DockStyle::Horizontal |
水平停靠,从左到右排列 |
DockStyle::Vertical |
垂直停靠,从上到下排列 |
DockStyle::Fill |
填满剩余空间 |
代码示例:
// 填满父容器
control->SetDockStyle(DockStyle::Fill);
// 垂直停靠(类似 StackPanel)
layout->SetDockStyle(DockStyle::Vertical);
2.2 视觉属性
2.2.1 可见性
| 属性 | 类型 | 作用 |
|---|---|---|
IsVisible() |
bool |
获取控件是否可见 |
SetVisible(bool) |
void |
设置控件可见性 |
Show() |
void |
显示控件(等价于 SetVisible(true)) |
Hide() |
void |
隐藏控件(等价于 SetVisible(false)) |
代码示例:
// 显示/隐藏控件
control->SetVisible(true);
control->Show();
control->SetVisible(false);
control->Hide();
// 判断可见性
if (control->IsVisible()) {
// 控件可见
}
2.2.2 启用状态
| 属性 | 类型 | 作用 |
|---|---|---|
IsEnabled() |
bool |
获取控件是否启用 |
SetEnabled(bool) |
void |
设置控件启用/禁用 |
SetDisabled(bool) |
void |
设置控件禁用/启用 |
代码示例:
// 禁用控件(变灰,不可交互)
control->SetEnabled(false);
control->SetDisabled(true); // 等价
// 启用控件
control->SetEnabled(true);
control->SetDisabled(false);
2.2.3 样式状态
Control 支持多套样式,分别对应不同的交互状态:
| 属性 | 类型 | 作用 |
|---|---|---|
Style |
ControlStyle |
静态/默认状态样式 |
HoverStyle |
ControlStyle |
鼠标悬停状态样式 |
ActiveStyle |
ControlStyle |
鼠标按下状态样式 |
DisabledStyle |
ControlStyle |
禁用状态样式 |
ControlStyle 结构:
struct ControlStyle {
ezui::Border Border; // 边框
Color BackColor = 0; // 背景颜色
Image* BackImage = NULL; // 背景图片
Image* ForeImage = NULL; // 前景图片
std::wstring FontFamily; // 字体名称
int FontSize = 0; // 字体大小
Color ForeColor; // 前景颜色(文字颜色)
HCURSOR Cursor = NULL; // 鼠标光标
float Angle = 0; // 旋转角度 (0-360)
};
代码示例:
// 编程方式设置样式
control->Style.BackColor = Color(255, 255, 255); // 白色背景
control->Style.Border.Left = 1; // 左边框宽度
control->Style.Border.LeftColor = Color(200, 200, 200);
control->Style.FontSize = 14;
// 使用 CSS 字符串设置样式(更方便)
control->SetStyleSheet(ControlState::Static,
"background-color:#FFFFFF;border:1px solid #CCCCCC;font-size:14px");
control->SetStyleSheet(ControlState::Hover,
"background-color:#E0E0E0");
2.3 层级属性
2.3.1 父子关系
| 属性 | 类型 | 作用 |
|---|---|---|
Parent |
Control* |
获取父控件指针(公共属性) |
SetParent(Control*) |
void |
设置父控件 |
代码示例:
// 设置父控件(推荐方式)
parentLayout->Add(childControl);
// 或
childControl->SetParent(parentLayout);
// 获取父控件
Control* parent = childControl->Parent;
if (parent) {
// 父控件存在
}
2.3.2 子控件集合
| 方法 | 作用 |
|---|---|
GetControls() |
获取所有子控件集合 |
GetViewControls() |
获取可见的子控件集合 |
Add(Control*) |
添加子控件到末尾 |
Insert(int, Control*) |
在指定位置插入子控件 |
Remove(Control*, bool freeCtrl = false) |
移除子控件 |
Clear(bool freeChilds = false) |
清除所有子控件 |
GetControl(int pos) |
按索引获取子控件(跳过占位符) |
IndexOf(Control*) |
获取子控件索引 |
代码示例:
// 添加子控件
VLayout* layout = new VLayout();
layout->Add(new Button());
layout->Add(new Label());
// 插入到指定位置
layout->Insert(0, new Control()); // 插入到开头
// 移除子控件
Control* child = layout->GetControl(0);
layout->Remove(child); // 移除但不删除
layout->Remove(child, true); // 移除并删除
// 遍历子控件
for (Control* ctl : layout->GetControls()) {
ctl->SetVisible(false);
}
// 清空子控件
layout->Clear(); // 移除但不删除子控件对象
layout->Clear(true); // 移除并删除子控件对象
2.4 标识属性
| 属性 | 类型 | 作用 |
|---|---|---|
Name |
UIString |
控件名称(用于查找) |
代码示例:
// 设置名称
control->Name = "submitButton";
// 通过名称查找控件
Control* found = window.FindControl("submitButton");
// 查找具有特定属性值的控件
Controls results = window.FindControl("type", "button");
2.5 状态属性
2.5.1 控件状态枚举
enum class ControlState : int {
None = 1, // 无状态
Static = 2, // 静态/默认
Disabled = 4, // 禁用
Checked = 8, // 选中(复选框/单选按钮)
Hover = 16, // 鼠标悬停
Active = 32 // 鼠标按下/激活
};
2.5.2 获取状态
| 方法 | 作用 |
|---|---|
State |
获取当前控件状态 |
GetLayoutState() |
获取当前布局状态 |
代码示例:
// 判断当前状态
if (control->State == ControlState::Hover) {
// 鼠标正在控件上
}
if (control->State == ControlState::Active) {
// 控件正在被按下
}
// 布局状态
LayoutState layoutState = control->GetLayoutState();
if (layoutState == LayoutState::Pend) {
// 布局已挂起,需要重新布局
}
3. 核心方法详解
3.1 构造函数与析构函数
// 构造函数
Control(Object* parentObject = NULL);
// 析构函数
virtual ~Control();
行为说明:
- 构造函数接受一个可选的父对象指针
- 如果传入父控件,子控件会被自动添加到父控件的子集合中
- 析构函数会自动销毁所有子控件(递归)
代码示例:
// 方式1:创建后手动添加
Control* child = new Control();
parent->Add(child);
// 方式2:创建时指定父控件(推荐)
Control* child = new Control(parent); // 自动添加到 parent 的子控件集合
3.2 布局相关方法
布局是 EzUI 的核心特性之一,采用了延迟布局机制以提高性能。
3.2.1 布局状态机
┌─────────────┐
│ None │ ←─── 布局完成状态
└──────┬──────┘
│ TryPendLayout()
▼
┌─────────────┐
│ Pend │ ←─── 布局已挂起,等待下次布局
└──────┬──────┘
│ OnLayout()
▼
┌─────────────┐
│ Layouting │ ←─── 正在布局中
└──────┬──────┘
│ EndLayout()
▼
None
3.2.2 核心布局方法
| 方法 | 作用 |
|---|---|
TryPendLayout() |
挂起布局,请求重新布局 |
RefreshLayout() |
强制立即刷新布局 |
GetLayoutState() |
获取当前布局状态 |
IsPendLayout() |
检查是否挂起了布局 |
EndLayout() |
结束当前布局 |
OnLayout() |
虚函数,子类可重写实现自定义布局逻辑 |
代码示例:
// 修改尺寸后,布局会自动挂起
control->SetFixedSize(Size(200, 100));
// 手动触发布局刷新
control->RefreshLayout();
// 检查布局状态
if (control->IsPendLayout()) {
// 需要重新布局
}
3.3 渲染相关方法
3.3.1 渲染流程
┌────────────────────────────────────┐
│ OnPaintBefore() │ ← 绘制前处理(可重写)
└──────────────┬─────────────────────┘
▼
┌────────────────────────────────────┐
│ OnBackgroundPaint() │ ← 绘制背景(可重写)
└──────────────┬─────────────────────┘
▼
┌────────────────────────────────────┐
│ OnForePaint() │ ← 绘制前景内容(可重写)
└──────────────┬─────────────────────┘
▼
┌────────────────────────────────────┐
│ OnBorderPaint() │ ← 绘制边框(可重写)
└──────────────┬─────────────────────┘
▼
┌────────────────────────────────────┐
│ OnChildPaint() │ ← 绘制子控件
└────────────────────────────────────┘
3.3.2 渲染方法列表
| 方法 | 作用 |
|---|---|
OnPaintBefore(PaintEventArgs&) |
绘制前回调,可重写 |
OnBackgroundPaint(PaintEventArgs&) |
绘制背景回调,可重写 |
OnForePaint(PaintEventArgs&) |
绘制前景回调,可重写 |
OnBorderPaint(PaintEventArgs&, const Border&) |
绘制边框回调,可重写 |
OnChildPaint(PaintEventArgs&) |
绘制子控件回调,可重写 |
Refresh() |
立即重绘 |
Invalidate() |
延迟重绘(更高效) |
代码示例:
// 方式1:立即重绘
control->Refresh();
// 方式2:延迟重绘(推荐,多次修改只重绘一次)
control->Invalidate();
control->Invalidate(); // 多次调用只重绘一次
// 自定义绘制
class MyControl : public Control {
protected:
virtual void OnForePaint(PaintEventArgs& e) override {
// 获取绘图上下文
Graphics& g = e.Graphics;
// 绘制自定义内容
Rect rect = GetRect();
g.DrawString(L"Hello", rect);
// 调用基类
Control::OnForePaint(e);
}
};
3.4 事件处理方法
3.4.1 事件分发机制
用户操作
│
▼
Window 消息循环
│
▼
Control::SendEvent(EventArgs)
│
├── ▼ 控件禁用? → 拦截鼠标/键盘事件
│
▼
Control::OnEvent(EventArgs)
│
├── OnMouseEvent() / OnKeyBoardEvent()
│ │
│ ▼
│ 具体事件处理 (OnMouseDown, OnMouseUp...)
│
▼
EventHandler 回调(如果有)
│
▼
Frame::OnNotify()(如果在 NotifyFlags 中)
3.4.2 事件处理方法列表
鼠标事件:
| 方法 | 作用 |
|---|---|
OnMouseEvent(const MouseEventArgs&) |
鼠标事件总入口 |
OnMouseEnter(const MouseEventArgs&) |
鼠标进入 |
OnMouseLeave(const MouseEventArgs&) |
鼠标离开 |
OnMouseMove(const MouseEventArgs&) |
鼠标移动 |
OnMouseDown(const MouseEventArgs&) |
鼠标按下 |
OnMouseUp(const MouseEventArgs&) |
鼠标释放 |
OnMouseWheel(const MouseEventArgs&) |
鼠标滚轮 |
OnMouseDoubleClick(const MouseEventArgs&) |
鼠标双击 |
键盘事件:
| 方法 | 作用 |
|---|---|
OnKeyBoardEvent(const KeyboardEventArgs&) |
键盘事件总入口 |
OnKeyDown(const KeyboardEventArgs&) |
键盘按下 |
OnKeyUp(const KeyboardEventArgs&) |
键盘释放 |
OnKeyChar(const KeyboardEventArgs&) |
字符输入 |
焦点事件:
| 方法 | 作用 |
|---|---|
OnFocus(const FocusEventArgs&) |
获得焦点 |
OnKillFocus(const KillFocusEventArgs&) |
失去焦点 |
其他事件:
| 方法 | 作用 |
|---|---|
OnMove(const MoveEventArgs&) |
位置改变 |
OnSize(const SizeEventArgs&) |
尺寸改变 |
OnDpiChange(const DpiChangeEventArgs&) |
DPI 改变 |
代码示例:
// 方式1:使用 EventHandler 回调
button->EventHandler = [](Control* sender, EventArgs& args) {
switch (args.EventType) {
case Event::OnMouseUp:
// 处理点击
break;
case Event::OnMouseEnter:
// 处理鼠标进入
break;
}
};
// 方式2:重写虚函数(需要继承)
class MyButton : public Button {
protected:
virtual void OnMouseUp(const MouseEventArgs& arg) override {
// 自定义处理逻辑
Button::OnMouseUp(arg); // 可选:调用基类
}
};
3.5 生命周期方法
| 方法 | 作用 | 调用时机 |
|---|---|---|
OnRemove() |
移除回调 | 控件从父容器移除时 |
GetCursor(ControlState) |
获取光标 | 需要显示特定光标时 |
4. CSS 样式属性映射表
EzUI 支持使用 CSS 字符串设置控件样式。以下是完整的属性映射:
| CSS 属性 | C++ 属性/方法 | 值类型 | 示例 |
|---|---|---|---|
| 背景 | |||
background-color |
Style.BackColor |
Color | background-color:#FFFFFF |
background-image |
Style.BackImage |
Image* | background-image:res.png |
background-position |
- | - | (暂不支持) |
background-size |
- | - | (暂不支持) |
| 前景 | |||
fore-color / color |
Style.ForeColor |
Color | color:#000000 |
fore-image |
Style.ForeImage |
Image* | fore-image:icon.png |
fore-image-size |
- | - | (暂不支持) |
| 边框 | |||
border |
Style.Border |
Border | border:1px solid #000000 |
border-width |
Style.Border.Left/Top/Right/Bottom |
int | border-width:1 |
border-style |
Style.Border.LeftStyle |
BorderStyle | border-style:solid |
border-color |
Style.Border.LeftColor |
Color | border-color:#000000 |
border-radius |
Style.Border.LeftRadius |
int | border-radius:5 |
border-top-left-radius |
Style.Border.LeftTopRadius |
int | border-top-left-radius:5 |
border-top-right-radius |
Style.Border.RightTopRadius |
int | border-top-right-radius:5 |
border-bottom-left-radius |
Style.Border.LeftBottomRadius |
int | border-bottom-left-radius:5 |
border-bottom-right-radius |
Style.Border.RightBottomRadius |
int | border-bottom-right-radius:5 |
border-left |
Style.Border.Left |
BorderItem | border-left:1px solid #000 |
border-top |
Style.Border.Top |
BorderItem | border-top:1px solid #000 |
border-right |
Style.Border.Right |
BorderItem | border-right:1px solid #000 |
border-bottom |
Style.Border.Bottom |
BorderItem | border-bottom:1px solid #000 |
| 字体 | |||
font-size |
Style.FontSize |
int | font-size:14 |
font-family |
Style.FontFamily |
wstring | font-family:Microsoft YaHei |
| 光标 | |||
cursor |
Style.Cursor |
HCURSOR | cursor:pointer |
| 交互 | |||
pointer-events |
m_hitTestEnabled |
bool | pointer-events:none |
| 定位 | |||
x |
SetX() |
int | x:100 |
y |
SetY() |
int | y:200 |
width |
SetFixedWidth() |
int | width:200 |
height |
SetFixedHeight() |
int | height:100 |
| 外边距 | |||
margin |
Margin |
Distance | margin:10 |
margin-left |
Margin.Left |
int | margin-left:10 |
margin-top |
Margin.Top |
int | margin-top:10 |
margin-right |
Margin.Right |
int | margin-right:10 |
margin-bottom |
Margin.Bottom |
int | margin-bottom:10 |
代码示例:
// 设置静态样式
control->SetStyleSheet(ControlState::Static,
"background-color:#FFFFFF;"
"border:1px solid #CCCCCC;"
"font-size:14px;"
"font-family:Microsoft YaHei;"
"cursor:pointer");
// 设置悬停样式
control->SetStyleSheet(ControlState::Hover,
"background-color:#E8E8E8");
// 设置按下样式
control->SetStyleSheet(ControlState::Active,
"background-color:#D0D0D0");
// 设置禁用样式
control->SetStyleSheet(ControlState::Disabled,
"background-color:#F0F0F0;"
"fore-color:#999999");
5. 事件系统
5.1 支持的事件类型
事件类型定义在 EzUI.h 中:
enum Event : long long {
None = 1,
OnMouseWheel = 2,
OnMouseEnter = 4,
OnMouseMove = 8,
OnMouseLeave = 16,
OnMouseDoubleClick = 32,
OnMouseDown = 64,
OnMouseUp = 128,
OnKeyDown = 256,
OnKeyUp = 512,
OnPaint = 1024,
OnFocus = 2048,
OnKillFocus = 4096,
OnKeyChar = 8192,
OnMove = 16384,
OnSize = 32768,
OnRect = 65536,
OnDpiChange = 131072,
};
复合事件:
OnActive = OnMouseDown | OnMouseUp, // 点击
OnHover = OnMouseEnter | OnMouseLeave, // 悬停
OnMouseDrag = OnMouseDown | OnMouseMove, // 拖拽
OnMouseEvent = ..., // 所有鼠标事件
OnKeyBoardEvent = ... // 所有键盘事件
5.2 事件参数类
| 类名 | 包含信息 |
|---|---|
EventArgs |
Event EventType - 事件类型 |
MouseEventArgs |
MouseButton Button, int ZDelta, Point Location |
KeyboardEventArgs |
WPARAM wParam, LPARAM lParam |
FocusEventArgs |
Control* Ctl - 获得焦点的控件 |
KillFocusEventArgs |
Control* Ctl - 失去焦点的控件 |
MoveEventArgs |
Point Location - 新位置 |
SizeEventArgs |
Size NewSize - 新尺寸 |
PaintEventArgs |
Graphics& Graphics, HWND, Rect, etc. |
5.3 事件订阅与取消订阅
5.3.1 使用 EventHandler 回调
// 订阅事件
control->EventHandler = [](Control* sender, EventArgs& args) {
// sender: 事件发送者
// args: 事件参数
if (args.EventType == Event::OnMouseUp) {
// 处理点击
}
};
// 取消订阅(设为空即可)
control->EventHandler = nullptr;
5.3.2 使用 NotifyFlags 转发事件
// 设置事件转发到 Window 的 NotifyHandler
control->NotifyFlags = Event::OnMouseEvent | Event::OnKeyBoardEvent;
// 在 Window 中处理
window.NotifyHandler = [](Control* sender, EventArgs& args) {
// 处理所有子控件转发的事件
};
5.3.3 重写虚函数
class MyControl : public Control {
protected:
virtual void OnMouseDown(const MouseEventArgs& arg) override {
// 自定义处理
// 调用基类实现
Control::OnMouseDown(arg);
}
virtual void OnKeyDown(const KeyboardEventArgs& arg) override {
// 处理键盘按下
}
};
5.4 自定义事件扩展指南
EzUI 使用 EventArgs 作为基础事件参数,可以扩展自定义事件:
// 自定义事件参数
class MyEventArgs : public EventArgs {
public:
int CustomData;
UIString Message;
};
// 触发自定义事件
void MyControl::DoSomething() {
MyEventArgs args;
args.EventType = (Event)0x10000; // 使用未定义的事件类型
args.CustomData = 42;
args.Message = L"Custom Event";
SendEvent(args);
}
6. 最佳实践与常见陷阱
6.1 内存管理
6.1.1 父子控件生命周期
最佳实践:
// 方式1:创建时指定父控件(推荐)
VLayout* layout = new VLayout(&window); // window 会负责销毁 layout
Button* btn = new Button(layout); // layout 会负责销毁 btn
// 方式2:使用 Add 方法
Control* parent = new Control();
Control* child = new Control();
parent->Add(child);
// parent 销毁时自动销毁 child
陷阱:
// 错误:父控件销毁后,子指针变成悬空指针
Control* child = new Control();
parent->Add(child);
delete parent; // child 已被销毁
child->SetVisible(false); // 崩溃!
// 错误:重复添加导致ouble free
parent->Add(child);
parent->Add(child); // 崩溃!
6.1.2 Detach 与 Attach
// Attach:附加图片资源,控件销毁时自动释放图片
Image* img = Image::LoadFromFile(L"test.png");
control->Attach(img);
// Detach:分离图片资源,控件销毁时不释放图片
control->Detach(img);
// 注意:Detach 后需要手动 delete img
6.2 线程安全
6.2.1 UI 线程访问规则
关键原则:所有 UI 操作必须在主线程中执行。
// 错误:跨线程修改 UI
std::thread t([&]() {
control->SetText(L"Hello"); // 可能崩溃!
});
t.join();
// 正确:使用 Invoke 同步派发到主线程
std::thread t([&]() {
Application::Invoke([&]() {
control->SetText(L"Hello"); // 安全
});
});
t.join();
// 正确:使用 BeginInvoke 异步派发
std::thread t([&]() {
Application::BeginInvoke([&]() {
control->SetText(L"Hello"); // 安全
});
});
t.join();
6.3 性能优化
6.3.1 减少重绘
// 差:每次修改都立即重绘
control->SetWidth(100);
control->Refresh();
control->SetHeight(50);
control->Refresh();
// 好:使用 Invalidate 延迟重绘(推荐)
control->SetWidth(100);
control->SetHeight(50);
control->Invalidate(); // 一次重绘
6.3.2 批量更新布局
// 差:每次修改都触发布局
layout->SetDockStyle(DockStyle::Vertical);
layout->RefreshLayout();
layout->Add(child1);
layout->RefreshLayout();
layout->Add(child2);
layout->RefreshLayout();
// 好:使用 Invalidate 批量更新
layout->SetDockStyle(DockStyle::Vertical);
layout->Add(child1);
layout->Add(child2);
layout->Invalidate(); // 一次布局
6.3.3 隐藏控件不参与渲染
// 设置为不可见后,控件不会参与渲染计算
control->SetVisible(false);
control->Hide();
6.4 常见编译错误和运行时错误排查
| 错误 | 原因 | 解决方案 |
|---|---|---|
unresolved external symbol |
链接库缺失 | 确保链接 EzUI 库 |
error C2027: use of undefined type |
头文件包含顺序错误 | 检查 include 顺序 |
| 控件不显示 | 未添加到父容器 | 使用 Add() 方法 |
| 控件不响应事件 | 不可见或禁用 | 检查 SetVisible/SetEnabled |
| 布局不正确 | 未设置尺寸 | 使用 SetFixedSize/SetAutoSize |
| 样式不生效 | 样式状态优先级 | 检查 ControlState 设置 |
| 内存泄漏 | 未正确管理父子关系 | 使用智能指针或正确配对 new/delete |
7. 附录:速查表
7.1 常用方法速查
| 操作 | 方法 |
|---|---|
| 创建控件 | new Control(parent) |
| 添加子控件 | parent->Add(child) |
| 设置尺寸 | SetFixedSize(Size(w, h)) |
| 设置位置 | SetLocation(Point(x, y)) |
| 设置样式 | SetStyleSheet(ControlState::Static, "css") |
| 绑定事件 | ctrl->EventHandler = [](...){} |
| 显示/隐藏 | Show() / Hide() |
| 启用/禁用 | SetEnabled(true/false) |
| 刷新绘制 | Invalidate() |
| 刷新布局 | RefreshLayout() |
7.2 CSS 速查
/* 按钮样式示例 */
Button {
background-color: #0078D4;
border: 1px solid #005A9E;
border-radius: 4px;
color: #FFFFFF;
font-size: 14px;
font-family: Microsoft YaHei;
cursor: pointer;
}
/* 悬停状态 */
Button:Hover {
background-color: #106EBE;
}
/* 按下状态 */
Button:Active {
background-color: #005A9E;
}
/* 禁用状态 */
Button:Disabled {
background-color: #CCCCCC;
color: #999999;
cursor: not-allowed;
}
文档版本:1.0 最后更新:2026-02-20