1077 lines
28 KiB
Markdown
1077 lines
28 KiB
Markdown
|
|
# EzUI Control 基类权威指南
|
|||
|
|
|
|||
|
|
> 本文档是 EzUI C++ UI 框架的核心参考资料,面向初学者入门。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
|
|||
|
|
1. [类概述](#1-类概述)
|
|||
|
|
2. [核心属性详解](#2-核心属性详解)
|
|||
|
|
3. [核心方法详解](#3-核心方法详解)
|
|||
|
|
4. [CSS 样式属性映射表](#4-css-样式属性映射表)
|
|||
|
|
5. [事件系统](#5-事件系统)
|
|||
|
|
6. [最佳实践与常见陷阱](#6-最佳实践与常见陷阱)
|
|||
|
|
7. [附录:速查表](#7-附录速查表)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 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 快速入门示例
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#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 坐标 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置位置的三种方式
|
|||
|
|
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&` | 获取控件矩形区域(布局计算后) |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 固定尺寸
|
|||
|
|
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` | 设置控件外边距,影响在父容器中的布局 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置外边距
|
|||
|
|
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` | 填满剩余空间 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 填满父容器
|
|||
|
|
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)) |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 显示/隐藏控件
|
|||
|
|
control->SetVisible(true);
|
|||
|
|
control->Show();
|
|||
|
|
|
|||
|
|
control->SetVisible(false);
|
|||
|
|
control->Hide();
|
|||
|
|
|
|||
|
|
// 判断可见性
|
|||
|
|
if (control->IsVisible()) {
|
|||
|
|
// 控件可见
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2.2.2 启用状态
|
|||
|
|
|
|||
|
|
| 属性 | 类型 | 作用 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `IsEnabled()` | `bool` | 获取控件是否启用 |
|
|||
|
|
| `SetEnabled(bool)` | `void` | 设置控件启用/禁用 |
|
|||
|
|
| `SetDisabled(bool)` | `void` | 设置控件禁用/启用 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 禁用控件(变灰,不可交互)
|
|||
|
|
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 结构**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
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)
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 编程方式设置样式
|
|||
|
|
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` | 设置父控件 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置父控件(推荐方式)
|
|||
|
|
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*)` | 获取子控件索引 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 添加子控件
|
|||
|
|
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` | 控件名称(用于查找) |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置名称
|
|||
|
|
control->Name = "submitButton";
|
|||
|
|
|
|||
|
|
// 通过名称查找控件
|
|||
|
|
Control* found = window.FindControl("submitButton");
|
|||
|
|
|
|||
|
|
// 查找具有特定属性值的控件
|
|||
|
|
Controls results = window.FindControl("type", "button");
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2.5 状态属性
|
|||
|
|
|
|||
|
|
#### 2.5.1 控件状态枚举
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
enum class ControlState : int {
|
|||
|
|
None = 1, // 无状态
|
|||
|
|
Static = 2, // 静态/默认
|
|||
|
|
Disabled = 4, // 禁用
|
|||
|
|
Checked = 8, // 选中(复选框/单选按钮)
|
|||
|
|
Hover = 16, // 鼠标悬停
|
|||
|
|
Active = 32 // 鼠标按下/激活
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2.5.2 获取状态
|
|||
|
|
|
|||
|
|
| 方法 | 作用 |
|
|||
|
|
|------|------|
|
|||
|
|
| `State` | 获取当前控件状态 |
|
|||
|
|
| `GetLayoutState()` | 获取当前布局状态 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 判断当前状态
|
|||
|
|
if (control->State == ControlState::Hover) {
|
|||
|
|
// 鼠标正在控件上
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (control->State == ControlState::Active) {
|
|||
|
|
// 控件正在被按下
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 布局状态
|
|||
|
|
LayoutState layoutState = control->GetLayoutState();
|
|||
|
|
if (layoutState == LayoutState::Pend) {
|
|||
|
|
// 布局已挂起,需要重新布局
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 核心方法详解
|
|||
|
|
|
|||
|
|
### 3.1 构造函数与析构函数
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 构造函数
|
|||
|
|
Control(Object* parentObject = NULL);
|
|||
|
|
|
|||
|
|
// 析构函数
|
|||
|
|
virtual ~Control();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**行为说明**:
|
|||
|
|
- 构造函数接受一个可选的父对象指针
|
|||
|
|
- 如果传入父控件,子控件会被自动添加到父控件的子集合中
|
|||
|
|
- 析构函数会自动销毁所有子控件(递归)
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 方式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()` | 虚函数,子类可重写实现自定义布局逻辑 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 修改尺寸后,布局会自动挂起
|
|||
|
|
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()` | 延迟重绘(更高效) |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 方式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 改变 |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 方式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` |
|
|||
|
|
|
|||
|
|
**代码示例**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置静态样式
|
|||
|
|
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` 中:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
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,
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**复合事件**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
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 回调
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 订阅事件
|
|||
|
|
control->EventHandler = [](Control* sender, EventArgs& args) {
|
|||
|
|
// sender: 事件发送者
|
|||
|
|
// args: 事件参数
|
|||
|
|
|
|||
|
|
if (args.EventType == Event::OnMouseUp) {
|
|||
|
|
// 处理点击
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 取消订阅(设为空即可)
|
|||
|
|
control->EventHandler = nullptr;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 5.3.2 使用 NotifyFlags 转发事件
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置事件转发到 Window 的 NotifyHandler
|
|||
|
|
control->NotifyFlags = Event::OnMouseEvent | Event::OnKeyBoardEvent;
|
|||
|
|
|
|||
|
|
// 在 Window 中处理
|
|||
|
|
window.NotifyHandler = [](Control* sender, EventArgs& args) {
|
|||
|
|
// 处理所有子控件转发的事件
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 5.3.3 重写虚函数
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
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` 作为基础事件参数,可以扩展自定义事件:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 自定义事件参数
|
|||
|
|
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 父子控件生命周期
|
|||
|
|
|
|||
|
|
**最佳实践**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 方式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
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**陷阱**:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 错误:父控件销毁后,子指针变成悬空指针
|
|||
|
|
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
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 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 操作必须在**主线程**中执行。
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 错误:跨线程修改 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 减少重绘
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 差:每次修改都立即重绘
|
|||
|
|
control->SetWidth(100);
|
|||
|
|
control->Refresh();
|
|||
|
|
control->SetHeight(50);
|
|||
|
|
control->Refresh();
|
|||
|
|
|
|||
|
|
// 好:使用 Invalidate 延迟重绘(推荐)
|
|||
|
|
control->SetWidth(100);
|
|||
|
|
control->SetHeight(50);
|
|||
|
|
control->Invalidate(); // 一次重绘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 6.3.2 批量更新布局
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 差:每次修改都触发布局
|
|||
|
|
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 隐藏控件不参与渲染
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
// 设置为不可见后,控件不会参与渲染计算
|
|||
|
|
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 速查
|
|||
|
|
|
|||
|
|
```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*
|