Skip to content

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);
}

四种严重级别的 InfoBar 堆叠

流畅方法 效果
.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);
    }
}

带步进按钮的确定型 ProgressBar

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);
    }
}

位于第 2 页(共 5 页)的 PipsPager

流畅方法 效果
.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

PersonPictureInfoBadge 配成"带通知的用户"外观 —— 通过 .Set 在底层控件上设置 BadgeNumber / BadgeGlyph

WinUI 设计页:Person picture

RatingControl

RatingControl 是一个 0–5 星评分(最大值可配置)。工厂方法接受当前值和一个可选的变更处理器 —— 与表单里的 SliderNumberBox 相同的受控输入模式:

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,绝不硬编码。

下一步

  • 文本与媒体 —— 上一篇:只读内容表面。
  • 对话框与浮出 —— 下一篇:模态与临时性交互表面。
  • 表单 —— 交互侧的对应物(RatingControl 的模式与 Slider/NumberBox 一致)。
  • 无障碍 —— InfoBar 的严重级别如何映射到 ARIA live region。
  • 持久化 —— 用 UsePersisted 保存"用户已看过此教学提示"。