WinUI 参考: 完整的属性表面与设计建议,参见 Controls。
状态与信息¶
Microsoft.UI.Reactor(Reactor)的状态与信息控件负责在不抢焦点的前提下,告知应用正在做什么。当用户需要知道某件事 —— 保存成功了、一个长操作正在跑、某个功能存在 —— 而不需要做任何决定时,就用它们。需要用户做选择的控件(保存 / 丢弃 / 取消),请用对话框与浮出。
参考¶
| 控件 | 工厂方法 | 用途 |
|---|---|---|
InfoBar |
InfoBar(title?, message?) |
带严重级别的应用级消息横幅。 |
InfoBadge |
InfoBadge() / InfoBadge(int) |
通知计数或存在性圆点。 |
ProgressBar |
Progress(double) / ProgressIndeterminate() |
线性进度条,确定或不确定型。 |
ProgressRing |
ProgressRing() / ProgressRing(double) |
转圈 / 确定型圆环。 |
TeachingTip |
TeachingTip(title, subtitle?) |
锚定到某个控件上的一次性引导气泡。 |
PipsPager |
PipsPager(count, index, onChanged?) |
紧凑的分页圆点。 |
PersonPicture |
PersonPicture() |
联系人头像 —— 显示名、首字母缩写或图像。 |
RatingControl |
RatingControl(value, onChanged?) |
0–5 星评分。 |
InfoBar¶
对于用户应当注意到、但不必立刻采取行动的应用级消息,InfoBar 是正确的控件。严重级别流畅方法(.Informational()、.Success()、.Warning()、.Error())一次调用就设好图标、颜色和无障碍角色:
class InfoBarSeveritiesDemo : Component
{
public override Element Render() => VStack(8,
SubHeading("InfoBar — severity fluents"),
InfoBar("Saving…", "Your changes are being written.")
.Informational().IsClosable(false),
InfoBar("Saved", "Your changes were saved.")
.Success().IsClosable(false),
InfoBar("Slow connection",
"Some assets may not load until the network recovers.")
.Warning().IsClosable(false),
InfoBar("Save failed",
"The destination drive is read-only.")
.Error().IsClosable(false)
).Padding(24);
}

| 流畅方法 | 效果 |
|---|---|
.Informational() / .Success() / .Warning() / .Error() |
设置 Severity 及配套的图标/颜色。 |
.IsClosable(bool) |
开关关闭按钮。 |
.IconSource(IconData) |
覆盖严重级别图标。 |
.Content(Element) |
在消息下方渲染富内容(链接、内嵌控件)。 |
.ActionButtonClick(Action) |
订阅尾部动作按钮。 |
.Closed(Action) |
用户关闭时触发。 |
IsOpen 默认为 true,因此一个裸 InfoBar(...) 立刻可见。要做一个可关闭又能重新打开的横幅,把 IsOpen 当作受控状态 —— 自己持有那个 bool,在 OnClosed 时设为 false,条件变化时再翻回 true:
class InfoBarDismissDemo : Component
{
public override Element Render()
{
// InfoBar 是受控组件 —— 你持有 IsOpen 标志,
// 并通过用户关闭操作所引发的 OnClosed 回调来重置它。
var (open, setOpen) = UseState(true);
return VStack(8,
SubHeading("Dismiss and re-open"),
InfoBar("Tip", "InfoBar uses controlled visibility.") with
{
IsOpen = open,
IsClosable = true,
OnClosed = () => setOpen(false),
Severity = Microsoft.UI.Xaml.Controls.InfoBarSeverity.Informational,
},
Button("Show again", () => setOpen(true)).IsEnabled(!open)
).Padding(24);
}
}
WinUI 设计页:Info bar。
InfoBadge¶
InfoBadge 是装饰在 NavigationViewItem、选项卡或任何带通知的元素上的圆点或计数。传一个 int 表示计数;省略参数则是纯存在性圆点:
class InfoBadgeDemo : Component
{
public override Element Render() => VStack(8,
SubHeading("InfoBadge"),
HStack(16,
// 圆点变体 —— 没有值,只是一个存在性指示。
VStack(4,
InfoBadge(),
TextBlock("dot").FontSize(11).Opacity(0.6)),
// 数字型 —— 常用于未读计数。
VStack(4,
InfoBadge(3),
TextBlock("count").FontSize(11).Opacity(0.6)),
VStack(4,
InfoBadge(127),
TextBlock("large").FontSize(11).Opacity(0.6))
)
).Padding(24);
}

InfoBadgeElement.Value 可为空 —— 大计数(≥100)由底层 WinUI 控件渲染为"99+"。需要自定义字形而非数字值时,用 .Set(b => b.Icon = ...)。
WinUI 设计页:Info badge。
ProgressBar 与 ProgressRing¶
Progress(value) 返回确定型进度条;ProgressIndeterminate() 返回不确定型进度条。ProgressRing(value) 和 ProgressRing() 是对应的圆环版本。在一小条横向空间里做已知时长的工作(上传、下载、批处理进度)用条;在标签旁边做未知时长的转圈("加载中…")用环:
class ProgressBarDemo : Component
{
public override Element Render()
{
var (value, setValue) = UseState(35.0);
return VStack(12,
SubHeading("ProgressBar"),
Progress(value).Width(320),
TextBlock($"{value:0}%").Opacity(0.6),
HStack(8,
Button("−10", () => setValue(Math.Max(0, value - 10))),
Button("+10", () => setValue(Math.Min(100, value + 10)))
),
// 不确定型 —— 不传值参数。
SubHeading("Indeterminate"),
ProgressIndeterminate().Width(320)
).Padding(24);
}
}

class ProgressRingDemo : Component
{
public override Element Render() => VStack(12,
SubHeading("ProgressRing"),
// 60% 的确定型圆环。
ProgressRing(60).Width(48).Height(48),
// 不确定型转圈。
ProgressRing().IsActive().Width(48).Height(48)
).Padding(24);
}
Progress(double) 和 ProgressRing(double) 的 value 参数都是 0–100 —— 与底层 WinUI 控件同一套约定。没有 ProgressBar(...) 工厂方法:线性进度条通过 Progress / ProgressIndeterminate 到达,这正是元素记录 ProgressElement 所绑定的(spec 039 §5)。
WinUI 设计页:Progress controls。
TeachingTip¶
TeachingTip 是一次性引导气泡 —— "你知道这个菜单存在吗?" —— 锚定在目标控件旁边。与 InfoBar 一样,它使用受控的 IsOpen:
class TeachingTipDemo : Component
{
public override Element Render()
{
var (show, setShow) = UseState(false);
var target = this.UseElementRef<FrameworkElement>();
return VStack(12,
SubHeading("TeachingTip"),
Button("Show tip", () => setShow(true)).Ref(target),
TeachingTip("Try the new sort menu",
"Sort across multiple columns by holding Shift.",
target: target) with
{
IsOpen = show,
OnClosed = () => setShow(false),
}
).Padding(24);
}
}
| 流畅方法 | 效果 |
|---|---|
.IconSource(IconData) |
标题旁的引导图标。 |
.HeroContent(Element) |
标题上方的图像或富内容。 |
.PreferredPlacement(mode) |
Top / Bottom / Left / Right / Auto。 |
.PlacementMargin(Thickness) |
相对锚点的偏移。 |
.ActionButtonClick(Action) |
订阅动作按钮。 |
.Closed(Action) |
用户关闭时触发。 |
教学提示只展示一次,而不是每次渲染都展示。UsePersisted 模式保存一个 "tour-seen" 布尔值来把住 IsOpen,这样用户关掉之后提示不会再冒出来。
WinUI 设计页:Teaching tip。
PipsPager¶
PipsPager 是一个紧凑的分页器 —— 一排圆点,当前索引高亮。用于页数较少的集合(通常 ≤ 10 页),此时标准 Pager 的那套外框太重:
class PipsPagerDemo : Component
{
public override Element Render()
{
var (page, setPage) = UseState(2);
var pageCount = 5;
return VStack(8,
SubHeading("PipsPager"),
TextBlock($"Page {page + 1} of {pageCount}").Opacity(0.6),
PipsPager(pageCount, page, setPage)
).Padding(24);
}
}

| 流畅方法 | 效果 |
|---|---|
.MaxVisiblePips(int) |
为长区间限制可见圆点数。 |
.WrapMode(mode) |
None(默认)/ Wrap —— 从最后一个的右侧回到第一个。 |
.PreviousButtonVisibility(visibility) |
Collapsed / Visible / VisibleOnPointerOver。 |
.NextButtonVisibility(visibility) |
下一个按钮的同样选项。 |
WinUI 设计页:Pips pager。
PersonPicture¶
PersonPicture 是联系人头像。它接受显示名(取每个单词的首字母作为缩写)、显式首字母缩写,或通过 .Set(p => p.ProfilePicture = ...) 提供的个人头像图。三者都没有时,回退到一个通用的人形字形:
class PersonPictureDemo : Component
{
public override Element Render() => VStack(8,
SubHeading("PersonPicture"),
HStack(12,
PersonPicture()
.DisplayName("Ada Lovelace")
.Width(48).Height(48),
PersonPicture()
.Initials("CB")
.Width(48).Height(48),
// 没有姓名也没有缩写 —— 回退到通用人形字形。
PersonPicture().Width(48).Height(48)
)
).Padding(24);
}
| 流畅方法 | 效果 |
|---|---|
.DisplayName(string) |
自动推导首字母缩写。 |
.Initials(string) |
覆盖推导出的缩写。 |
.Set(p => p.ProfilePicture = ...) |
提供一个 BitmapImage。 |
把 PersonPicture 与 InfoBadge 配成"带通知的用户"外观 —— 通过 .Set 在底层控件上设置 BadgeNumber / BadgeGlyph。
WinUI 设计页:Person picture。
RatingControl¶
RatingControl 是一个 0–5 星评分(最大值可配置)。工厂方法接受当前值和一个可选的变更处理器 —— 与表单里的 Slider、NumberBox 相同的受控输入模式:
class RatingDemo : Component
{
public override Element Render()
{
var (rating, setRating) = UseState(3.0);
return VStack(8,
SubHeading("RatingControl"),
RatingControl(rating, setRating)
.Caption("Tap a star or use ←/→ to rate"),
TextBlock($"Selected: {rating} stars").Opacity(0.6)
).Padding(24);
}
}
| 流畅方法 | 效果 |
|---|---|
.MaxRating(int) |
星星数量。默认 5。 |
.IsReadOnly(bool) |
只读模式,用于展示一个既有评分。 |
.Caption(string) |
显示在星星下方的说明文字。 |
.PlaceholderValue(double) |
用户评分前显示的灰色值。 |
.InitialSetValue(int) |
首次交互时应用的值。 |
WinUI 设计页:Rating control。
提示¶
为这条消息挑最小的信号。 一个三行的 InfoBar 和一句"Saved!"toast 传达的是同一件事,但占用十倍的屏幕高度。先用 InfoBadge,其次 InfoBar,对话框最后。
只要算得出来,确定型进度永远优于不确定型。 用户能围绕"还剩 3 分钟"作安排,却无法围着一个转圈作安排。用已完成项数 / 总项数,哪怕每项耗时只是个粗略估计。
不要在同一页上叠多个 TeachingTip。 它们会互相抢注意力。每次会话只展示一个提示,在与目标交互后关掉它,并用 UsePersisted 把关闭状态持久化。
节制使用 Severity.Error。 只留给真正的失败状态。如果 Warning 够用("操作耗时超出预期"),就用它 —— Error 会从无障碍树借来 alert 角色,屏幕阅读器会立即播报。
把 PersonPicture 绑定到你的用户模型,而不是界面装饰。 这个控件只是当前用户身份的一个被动视图 —— 驱动显示名的应当是你的认证/档案状态 Hook,绝不硬编码。