主题
流式布局扩展语法教程
字号、宽高和间距使用 UI 设计坐标,随画布缩放。正文不要直接套用普通 App 的 12–16 DIP/sp;选择字号、放大框体与验收不同窗口前,先读 UI 字号与可读性。
🚀 快速开始
流式扩展提供链式配置控件属性的写法。下列示例中的设计尺寸应按项目画布与显示需求调整。
什么是流式布局?
流式布局是一种链式调用的UI构建方式,每个方法都返回控件本身,支持连续调用多个方法:
csharp
// 属性赋值
var button = new Button();
button.Width = 120;
button.Height = 40;
button.HorizontalAlignment = HorizontalAlignment.Center;
button.VerticalAlignment = VerticalAlignment.Center;
button.Background = new SolidColorBrush(Colors.Primary);
// 对应的流式写法
var button = Button()
.Size(120, 40)
.Center()
.Background(Colors.Primary);📚 第一章:基础语法
1.1 导入命名空间
csharp
using GameUI.Control.Extensions;1.2 创建基础控件
csharp
// 创建控件
var panel = Panel();
var label = Label();
var button = Button();
// 创建带文本的控件
var title = Label("欢迎使用WasiCore");
var submitButton = TextButton("提交");1.3 设置基础属性
csharp
// 尺寸设置
control.Size(200, 100) // 宽200,高100
.Width(200) // 只设置宽度
.Height(100); // 只设置高度
// 位置设置
control.AbsolutePosition(50, 30); // 相对父容器左上角的绝对位置
control.RelativeOffset(0, -20); // 相对正常排版位置向上偏移
// 可见性和状态
control.Show() // 显示
.Hide() // 隐藏
.Enable() // 启用
.Disable(); // 禁用1.4 链式调用示例
csharp
var welcomeLabel = Label("欢迎来到游戏世界!")
.FontSize(24) // 字体大小
.Bold() // 粗体
.TextColor(Colors.Primary) // 文字颜色
.Center() // 居中对齐
.Padding(20) // 内边距
.Show(); // 显示📐 第二章:布局与对齐
2.1 尺寸控制
csharp
// 固定尺寸
control.Size(200, 100); // 固定宽高
control.Square(150); // 正方形150x150
// 自动尺寸(根据内容自适应)
control.AutoWidth() // 宽度自动适应内容
.AutoHeight() // 高度自动适应内容
.AutoSize(); // 宽高都自动
// 最小/最大尺寸
control.MinWidth(100) // 最小宽度
.MaxWidth(500) // 最大宽度
.MinHeight(50) // 最小高度
.MaxHeight(300); // 最大高度2.2 对齐方式
csharp
// 水平对齐
control.AlignLeft() // 左对齐
.Center() // 水平+垂直居中
.AlignRight(); // 右对齐
// 垂直对齐
control.AlignTop() // 顶部对齐
.Center() // 水平+垂直居中
.AlignBottom(); // 底部对齐
// 拉伸对齐
control.StretchHorizontal() // 水平拉伸
.StretchVertical() // 垂直拉伸
.Stretch(); // 双向拉伸
// 居中快捷方式
control.Center(); // 水平+垂直居中当前游戏模式引用的 GameDataGameUI 决定新建控件未显式配置时的内容对齐默认值:
UseTopLeftContentAlignmentDefaults = false或字段缺失:兼容旧项目,默认Center/Center。UseTopLeftContentAlignmentDefaults = true:默认Left/Top;新版项目模板主动开启。
该设置只影响之后创建的控件,不修改 PositionType,也不覆盖代码或数编模板里的显式对齐值。 可复用地图库应继续显式设置关键对齐,避免行为随宿主项目配置变化。
2.3 边距设置
csharp
// 统一边距
control.Margin(10) // 四周边距10
.Padding(15); // 四周内边距15
// 水平/垂直边距
control.Margin(20, 10) // 水平20,垂直10
.Padding(15, 5); // 水平15,垂直5
// 四个方向边距
control.Margin(10, 5, 10, 5); // 左、上、右、下🏗️ 第三章:容器布局
3.1 垂直堆叠 (VStack)
csharp
// 基础垂直堆叠
var vertical = VStack(16, // 间距16像素
Label("标题"),
Label("内容1"),
Label("内容2"),
Button("确定")
);
// 复杂垂直布局
var loginForm = VStack(24,
Title("用户登录", 32),
VStack(16, // 表单字段分组
Label("用户名"),
Input().Padding(12, 8),
Label("密码"),
Input().Padding(12, 8)
),
Button("登录")
.Size(200, 44)
.Center()
);3.2 水平堆叠 (HStack)
csharp
// 基础水平堆叠
var horizontal = HStack(12, // 间距12像素
Button("取消"),
Button("确定"),
Button("重置")
);
// 工具栏示例
var toolbar = HStack(8,
Button("保存").Size(80, 32),
Button("撤销").Size(80, 32),
Spacer(), // 弹性间距
Label("状态:已保存"),
Button("设置").Size(60, 32)
);3.3 嵌套布局
csharp
// 复杂嵌套布局
var mainLayout = VStack(20,
// 头部区域
HStack(16,
Label("应用标题").FontSize(24).Bold(),
Spacer(),
Button("用户")
).Padding(20, 16),
HDivider(), // 分隔线
// 主要内容区域
HStack(0,
// 左侧边栏
VStack(12,
Button("首页"),
Button("设置"),
Button("帮助")
).Padding(16).Width(200),
VDivider(), // 垂直分隔线
// 右侧内容
VStack(16,
Label("主要内容区域"),
Panel().Stretch() // 占满剩余空间
).Padding(20).Stretch()
).Stretch()
);🎨 第四章:样式与外观
4.1 颜色与背景
csharp
// 背景颜色
control.Background(Colors.Primary) // 使用预设颜色
.Background(Color.Blue) // 使用系统颜色
.Background(Color.FromArgb(255, 100, 150, 200)); // 自定义颜色
// 文字颜色
label.TextColor(Colors.OnPrimary) // 主色调上的文字色
.TextColor(Color.White); // 白色文字4.2 字体样式
csharp
// 字体设置
label.FontSize(18) // 字体大小
.Bold() // 粗体
.Italic() // 斜体
.TextColor(Colors.OnSurface); // 文字颜色
// 预设文本样式
Title("页面标题") // 预设标题样式
Subtitle("副标题") // 预设副标题样式
Body("正文内容") // 预设正文样式
Caption("说明文字") // 预设说明文字样式4.3 视觉效果
csharp
// 圆角
control.CornerRadius(8) // 8像素圆角
.CornerRadius(12); // 12像素圆角
// 透明度
control.Opacity(0.8f) // 80%不透明度
.Opacity(0.5f); // 50%不透明度🚀 第五章:高级功能
5.1 Flexbox布局
csharp
// Flexbox API - CSS标准命名
// 增长控制 - 控制元素如何占用剩余空间
control.WidthGrow(1.0f) // 宽度增长比例
.HeightGrow(0.5f) // 高度增长比例
.GrowRatio(1, 2) // 同时设置宽高增长比例
// 收缩控制 - 控制元素在空间不足时如何收缩
control.WidthShrink(0.5f) // 宽度收缩比例
.HeightShrink(0.3f) // 高度收缩比例
.ShrinkRatio(0.5, 0.5) // 同时设置宽高收缩比例
// Flex基础尺寸 - 设置元素的初始尺寸
control.FlexBasis(100, 50) // 设置基础宽高
.FlexBasisWidth(100) // 仅设置基础宽度
.FlexBasisHeight(50) // 仅设置基础高度
// 实际应用示例
HStack(8,
Panel().Background(DesignColors.Primary).Size(100, 50), // 固定尺寸
Panel().Background(DesignColors.Secondary).GrowRatio(1, 1), // flex: 1 1 auto
Panel().Background(DesignColors.Success).GrowRatio(2, 1) // flex: 2 1 auto
).Width(400);5.2 响应式布局
csharp
// 页面根(推荐)
UI.Page()
.Background(Colors.Background)
.Show()
.AddToRoot();
// 普通容器占满父容器
panel.Stretch() // 占满整个父容器
.Show();
// 填满父容器(嵌套控件)
contentPanel.FillParent() // 填满父容器(适用于嵌套控件)
.Background(Colors.Surface);
// 响应式尺寸 - 基于断点系统自动调整
panel.ResponsiveWidth(320, 800) // 最小320px,最大800px
.ResponsiveHeight(240, 600) // 最小240px,最大600px
.Center(); // 居中显示
// 响应式容器 - 预设尺寸
panel.ResponsiveContainer(ResponsiveContainerSize.Standard) // 标准容器
.ResponsiveContainer(ResponsiveContainerSize.Compact); // 紧凑容器
// 响应式可见性 - 基于断点控制显示
sidebar.ResponsiveVisibility(ResponsiveVisibility.MediumAndUp); // 中等屏幕及以上显示
mobileMenu.ResponsiveVisibility(ResponsiveVisibility.SmallOnly); // 仅小屏幕显示5.3 滚动容器
csharp
// 可滚动的垂直堆叠
var scrollContent = ScrollableVStack(8,
...GetListItems() // 获取列表项
).Height(300);
// 可滚动的水平堆叠
var horizontalScroll = ScrollableHStack(8,
...GetImages() // 获取图片列表
).Width(400);5.4 条件布局
csharp
// 根据条件显示不同内容
var dynamicPanel = VStack(16,
Title("动态内容"),
// 条件显示
isLoggedIn ?
VStack(8,
Label($"欢迎,{username}"),
Button("退出登录")
) :
VStack(8,
Label("请先登录"),
Button("立即登录")
),
Button("继续")
);💡 第六章:实用模式
6.1 卡片布局
csharp
// 信息卡片
var infoCard = Panel()
.MinHeight(120)
.Padding(24)
.CornerRadius(12)
.Background(Colors.Surface)
.Add(
VStack(12,
Title("卡片标题", 18),
Body("这是卡片的详细内容..."),
HStack(8,
Primary("操作1"),
Secondary("操作2")
)
)
);6.2 对话框布局
csharp
// 确认对话框
var confirmDialog = Panel()
.Size(400, 200)
.Center()
.CornerRadius(16)
.Background(Colors.Surface)
.Add(
VStack(24,
Title("确认操作", 20).Center(),
Body("您确定要执行此操作吗?").Center(),
HStack(16,
Secondary("取消").Click(() => CloseDialog()),
Primary("确定").Click(() => ExecuteAction())
).Center()
).Padding(32)
);6.3 列表项布局
csharp
// 标准列表项
Panel CreateListItem(string title, string subtitle, string action)
{
return Panel()
.Padding(20, 16)
.Background(Colors.Surface)
.Add(
HStack(16,
VStack(4,
Body(title, 16).Bold(),
Caption(subtitle)
),
Spacer(),
Secondary(action).FontSize(14)
)
);
}
// 使用示例
var itemList = VStack(4,
CreateListItem("项目1", "详细描述1", "编辑"),
CreateListItem("项目2", "详细描述2", "删除"),
CreateListItem("项目3", "详细描述3", "查看")
);布局与控件生命周期
流式扩展与对象初始化器最终配置同一套控件属性,可以混用。是否换行、对齐或重叠取决于尺寸约束、布局方向和父子关系,链式语法本身不保证布局正确。
- 按用途选择固定尺寸、自动尺寸、拉伸和响应式规则;设计坐标中的固定尺寸也是有效配置。适配关系见 UI 尺寸与适配入口。
- 使用
TextWrap时仍要建立 Label 的有限宽度,并为多行提供高度或滚动空间,见下方常见问题。 - 复用控件时检查当前父节点、状态和事件;不再使用时调用
Destroy()。RemoveFromParent()/ClearChildren()只摘树,不能当作资源释放。 - 屏幕变化时更新已有控件;订阅事件在页面销毁时解除,见屏幕变化示例。
🛠️ 第八章:常见问题
Q1: 如何让控件填满父容器?
csharp
// 水平填满
control.StretchHorizontal();
// 垂直填满
control.StretchVertical();
// 完全填满父容器
control.Stretch();
// 页面根占满视口(推荐)
Page().AddToRoot();
// 嵌套控件填满父容器
control.FillParent();Q2: 如何创建等宽的按钮?
csharp
var buttonRow = HStack(12,
Primary("按钮1").MinWidth(100),
Secondary("按钮2").MinWidth(100),
Success("按钮3").MinWidth(100)
);Q3: 如何处理溢出内容?
csharp
// 使用滚动容器
var scrollableContent = ScrollableVStack(8,
...manyItems
).Height(300); // 限制高度,超出部分可滚动Q3.1: Label 设置 TextWrap 后仍不换行?
TextWrap 允许文字在给定宽度内换行,但不会替 Label 设置最大宽度。父面板的宽度有限,也不代表它会自动把该宽度作为每个子控件的测量宽度。如果 Label 仍按文字的自然宽度测量,一整段文字可以形成一个超出父面板的单行 Label。
对固定宽度、向下增高的说明面板,可以使用:
csharp
#if CLIENT
using GameUI.Control.Extensions;
using static GameUI.Control.Extensions.UI;
var panel = Panel().Width(440).AutoHeight().Padding(16).FlowVertical();
var message = Label("WASD / 摇杆移动")
.FontSize(24)
.AutoWidth().StretchHorizontal().AutoHeight().TextWrap()
.TextTrimming(GameUI.Control.Enum.TextTrimming.Clip);
message.Parent = panel;
// panel 按页面结构加入可视树;之后可更新同一 Label 的文本。
message.Text = "西巷民居:沿着石板路穿过小镇,经过水井和花田,寻找藏在屋舍旁的苔光水晶。";
#endif这组配置各有用途:
| 设置 | 用途与边界 |
|---|---|
| 父容器 Width + FlowVertical | 提供有限宽度,沿垂直方向排列子控件 |
| Label AutoWidth + StretchHorizontal | 清除显式宽度,让垂直流式布局沿交叉轴把 Label 拉伸到可用内宽 |
| Label AutoHeight + TextWrap | 保持字号,换行后根据内容计算高度 |
| 父容器 AutoHeight | 随说明增高;受页面高度限制时应改用滚动等合适布局 |
| TextTrimming.Clip | 防止文字绘制越界;不能代替宽度约束或多行空间 |
父容器的 Padding 和子控件的 Margin 都会占用可用空间。StretchHorizontal 不是所有容器的统一解法:横向流式容器中的水平轴是主轴,应按列宽或 flex 分配空间;已有固定宽度的文本列可用 .Width(...).AutoHeight().TextWrap()。不要在拉伸之后再次设置固定宽度,或用 AutoSize 清掉需要保持的高度/宽度策略。
排查时先通过 ui.find 找到实际 Label,用 ui.snapshot / ui.get_rect 核对 Label 和父容器的屏幕矩形,再配合截图判断文字是否换行、是否被裁剪。rect_px 是屏幕像素,Width/Padding 通常是设计坐标;比较内容区时先统一单位,详见 UI 字号与画布缩放。多模拟客户端先按 Runtime MCP 目标路由 选择同一目标,调试重新开局后重新发现。
Label 宽度超过父内容区时,应优先检查约束与测量过程,但不能仅凭尺寸就断言引擎没有 bug。反过来,宽度正确也不等于文字一定完整显示:Label 或父容器的固定高度、Min/Max 尺寸、裁剪策略都可能影响后续行。检查实际模板是否覆盖 TextWrap、宽高或对齐,再检查富文本和字体。
动态文字需要验证三个阶段:短文本、足够长而需要换行的文本、重新变短的文本。固定宽度应保持,高度应在正常布局更新后按内容增减(显式最小高度等限制除外),后续控件和父面板应同步排版。初始短文字正常,不能证明后续长文字已正确处理。当前文本更新链路会更新文字布局,并对自动尺寸的 Label 标记布局失效;如果已有正确约束却不更新,应保留最小复现继续排查实现,不要默认添加每帧强制刷新。
完整说明优先保持字号并换行;单行名称按用途选择省略号。手动插入换行适用于作者明确指定的段落,不能修复缺失的宽度约束。
Q4: 如何创建响应式间距?
csharp
// 使用屏幕比例计算间距
var spacing = (int)(ScreenViewport.Primary.Size.Width * 0.02f); // 屏幕宽度的2%
var layout = VStack(spacing, ...items);📐 第九章:断点系统与响应式设计
9.1 断点系统
WasiCore使用五级断点系统,基于常见设备尺寸:
csharp
// 断点定义 (DesignTokens)
XSmall: < 480px // 小手机
Small: 480-768px // 大手机/小平板
Medium: 768-1024px // 平板
Large: 1024-1440px // 桌面
XLarge: > 1440px // 大屏桌面9.2 响应式API详解
csharp
// 基础响应式尺寸
control.ResponsiveWidth(minWidth, maxWidth) // 响应式宽度
.ResponsiveHeight(minHeight, maxHeight) // 响应式高度
.ResponsiveSize(minW, maxW, minH, maxH) // 同时设置宽高
// 响应式字体和间距
label.ResponsiveFontSize(12, 24, 1.2f) // 最小12px,最大24px,倍数1.2
control.ResponsiveSpacing(8, 24) // 响应式外边距
.ResponsivePadding(12, 32) // 响应式内边距
// 响应式容器尺寸
control.ResponsiveContainer(ResponsiveContainerSize.Standard)
.ResponsiveContainer(ResponsiveContainerSize.Compact)
.ResponsiveContainer(ResponsiveContainerSize.Comfortable)
// 响应式可见性控制
control.ResponsiveVisibility(ResponsiveVisibility.MediumAndUp) // 中等屏幕及以上
.ResponsiveVisibility(ResponsiveVisibility.SmallOnly) // 仅小屏幕
.ResponsiveVisibility(ResponsiveVisibility.LargeAndDown) // 大屏幕及以下
// 响应式布局方向
container.ResponsiveOrientation(
Orientation.Horizontal, // 横屏/大屏使用水平布局
Orientation.Vertical // 竖屏/小屏使用垂直布局
)9.3 实际应用示例
csharp
// 响应式导航栏
var navbar = HStack(16,
Logo().ResponsiveWidth(120, 200), // Logo大小随屏幕调整
// 导航菜单 - 大屏显示,小屏隐藏
HStack(24,
NavButton("首页"),
NavButton("产品"),
NavButton("关于")
).ResponsiveVisibility(ResponsiveVisibility.MediumAndUp),
// 汉堡菜单 - 小屏显示,大屏隐藏
MenuButton()
.ResponsiveVisibility(ResponsiveVisibility.SmallAndDown)
.Size(32, 32)
);
// 响应式卡片网格
var cardGrid = TrueGrid(
GetResponsiveGridColumns(), // 根据屏幕尺寸自动计算列数
rowSpacing: 16,
columnSpacing: 16,
...GetCards()
);
// 响应式侧边栏布局
var layout = HStack(0,
// 侧边栏 - 大屏显示,小屏隐藏
Sidebar()
.ResponsiveWidth(200, 300)
.ResponsiveVisibility(ResponsiveVisibility.LargeAndUp),
// 主内容区 - 自适应剩余空间
MainContent()
.GrowRatio(1, 1)
.ResponsivePadding(16, 32)
);9.4 屏幕变化时的布局更新
窗口尺寸变化并不一定意味着 drawable 宽高变化:屏幕旋转、设备像素比、安全区或移动端 Surface 重建都可能要求重新布局。因此需要整体刷新界面时,使用 ScreenViewport.Primary.OnViewportChanged,并在回调中读取更新后的 viewport 状态。
csharp
#if CLIENT
var viewport = ScreenViewport.Primary;
void RefreshLayout()
{
var safe = viewport.SafeZonePadding;
var visibleSize = viewport.Size;
rootPanel.Padding = new Thickness(safe.Left, safe.Top, safe.Right, safe.Bottom);
rootPanel.Width = visibleSize.Width;
rootPanel.Height = visibleSize.Height;
}
viewport.OnViewportChanged += RefreshLayout;
RefreshLayout();
#endif最佳实践:
OnViewportChanged适合统一处理尺寸、方向、DPI、安全区和裁切偏移;只关心单一维度时再使用OnSizeChanged或OnOrientationChanged。- 回调触发时 viewport 属性已经更新完成,应直接读取
Size、DisplayOrientations、DevicePixelRatio和SafeZonePadding,不要缓存初始化时的屏幕尺寸。 - 更新已有控件并保持回调幂等,不要在每次屏幕变化时重复创建整页 UI;页面根优先使用
Page()、FillParent()、Stretch()和响应式扩展。 - 同尺寸 Surface 重建通知需要客户端能力版本
4。旧客户端仍兼容实际 drawable 尺寸变化,但不能依赖它们为同尺寸重建单独抛出事件。 - 页面销毁或重复进入时成对管理事件订阅,避免回调继续访问已销毁控件。
🔗 第十章:相关资源
参考文档
- UI设计尺寸规范 - 详细的尺寸标准
- 流式布局UI API - API 参考
- DesignTokens设计令牌 - 断点和尺寸常量
常用扩展方法速查
基础属性
csharp
.Show() / .Hide() // 显示/隐藏
.Enable() / .Disable() // 启用/禁用
.Size(w, h) / .Width(w) / .Height(h) // 尺寸
.AbsolutePosition(x, y) // 父容器左上角绝对位置
.RelativeOffset(x, y) // 相对正常排版位置的偏移
.Position(x, y) // 保留当前 PositionType 的底层坐标设置
.Background(color) // 背景色
.Opacity(value) // 透明度
.CornerRadius(radius) // 圆角布局对齐
csharp
.Center() // 居中
.AlignLeft() / .AlignRight() // 水平对齐
.AlignTop() / .AlignBottom() // 垂直对齐
.Stretch() / .StretchHorizontal() / .StretchVertical() // 拉伸
.FullScreen() // 全屏(根级控件)
.FillParent() // 填满父容器(通用)
.Margin(all) / .Padding(all) // 边距
.AutoWidth() / .AutoHeight() / .AutoSize() // 自动尺寸容器创建
csharp
VStack(spacing, ...items) // 垂直堆叠
HStack(spacing, ...items) // 水平堆叠
ScrollableVStack(spacing, ...items) // 可滚动垂直堆叠
ScrollableHStack(spacing, ...items) // 可滚动水平堆叠
Page() // 页面级根容器(占满 viewport)
Panel() / Label() / Button() // 基础控件预设样式
csharp
Title(text, size) // 标题
Subtitle(text, size) // 副标题
Body(text, size) // 正文
Caption(text, size) // 说明文字
Primary(text) / Secondary(text) // 预设按钮