WinUI TeachingTip 控件完整实战指南:半持久化内容丰富的 Flyout 交互设计
【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml
TeachingTip 是 WinUI(microsoft-ui-xaml)中一种半持久化(semi-persistent)、内容丰富的浮出控件,用于向用户告知、提醒或教学新功能与重要更新。它既能以尖角(tail)精确指向某个 UI 元素,也能作为无目标提示悬浮在窗口边缘。本文将以仓库中 TeachingTipSpec.md 为核心骨架,结合 控件源码 与 IDL 定义,完整讲解 TeachingTip 的适用场景、13 种放置模式、按钮/图标/Hero 内容定制、轻触关闭(light-dismiss)、取消与延迟关闭事件,以及无障碍与键盘/游戏手柄导航细节,帮助你直接上手编写可运行的高质量引导提示。
什么是 TeachingTip,何时该用它
原文定义:A teaching tip is a semi-persistent and content-rich flyout that provides contextual information.
TeachingTip 的典型用途是将用户注意力聚焦到新功能、重要更新或非必需但能改善体验的选项上,或者教会用户如何完成某个任务。它可以:
- 通过尖角(tail)精确指向屏幕上的某个 UI 元素,增强上下文清晰度;
- 完全不设置 Target,作为无目标提示悬浮在窗口边缘;
- 由用户显式关闭(点击右上角 X 或底部 Close 按钮),或以**轻触关闭(light-dismiss)**方式在用户滚动或点击其他区域时自动消失。
什么时候不应该用
因为 TeachingTip 是瞬态的,所以不推荐用它提示错误或重要的状态变更——这类信息应该使用 ContentDialog、InfoBar 等更持久、更需要用户确认的控件。原文档对此有明确说明,这也是选择控件时的第一判断标准。
推荐使用原则(来自原文档 Recommendations)
- 提示内容应是非关键的:不要在其中放置对应用体验至关重要的信息或选项;
- 避免过于频繁地弹出提示:将提示分散在较长的会话或多个会话中,才能让每条提示获得用户的独立关注;
- 保持简短、主题清晰:研究表明用户平均只阅读 3-5 个单词、理解 2-3 个单词,就会决定是否与提示交互;
- 对于预测会使用游戏手柄(gamepad)输入的应用,务必参考 XY 焦点导航相关设计,并在所有可能的 UI 配置下逐一测试每条提示的手柄可达性(仓库源码
TeachingTipAutomationPeer与焦点处理也印证了这一点,详见后文"输入与无障碍")。
基础用法:创建一个 TeachingTip
原文档提供了两种声明方式:放置在 ResourceDictionary / 资源中,或直接放在元素树(element tree)中。两种方式行为完全一致——TeachingTip 只在IsOpen = true时显示,不占据布局空间。
有目标(Targeted)的 TeachingTip
以下 XAML 演示默认外观:通过Target属性绑定到按钮,标题与副标题随之显示:
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Save automatically" Subtitle="When you save your file to OneDrive, we save your changes as you go - so you never have to."> </controls:TeachingTip> </Button.Resources> </Button>在代码后置中控制显示时机(例如首次启动时展示一次):
public MainPage() { this.InitializeComponent(); if (!HaveExplainedAutoSave()) { AutoSaveTip.IsOpen = true; SetHaveExplainedAutoSave(); } }无目标(Non-targeted)的 TeachingTip
当提示内容与屏幕上某个元素无关时,不设置Target,提示将相对于 XamlRoot 的边缘显示;同时通过TailVisibility="Collapsed"隐藏尖角:
<Button x:Name="SaveButton" Content="Save" /> <controls:TeachingTip x:Name="AutoSaveTip" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to."> </controls:TeachingTip>从 TeachingTip.h 源码可以看到,控件内部维护了m_target引用,并在UpdateTail()中处理"无目标且 TailVisibility 非 Visible 时折叠尖角"的逻辑;DetermineEffectivePlacement()则根据m_target是否存在分流到DetermineEffectivePlacementTargeted()/DetermineEffectivePlacementUntargeted()两套完全不同的放置算法。
放置模式(PreferredPlacement):13 种模式与回退策略
TeachingTip 复刻了 Flyout 的FlyoutPlacementMode行为,通过TeachingTipPlacementMode枚举实现(完整枚举见 TeachingTip.idl):
enum TeachingTipPlacementMode { Auto, Top, TopRight, TopLeft, Right, RightTop, RightBottom, Bottom, BottomRight, BottomLeft, Left, LeftTop, LeftBottom, Center, };默认行为:
- 有目标:默认尝试放置在目标上方;
- 无目标:默认放置在 XamlRoot底部居中;
- 与 Flyout 相同:若首选放置模式空间不足,将自动选择其他模式。
有目标的放置语义
放置模式第一个词表示提示将居中对齐于目标的哪一侧,尖角(tail)始终位于该侧的中心并指向目标;若存在第二个词,则提示主体向该方向偏移。例如BottomLeft表示提示出现在目标下方、主体向左偏移。Center是唯一的特例:尖角指向目标中心,提示主体居中于目标上半部分。
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." PreferredPlacement="BottomLeft"> </controls:TeachingTip> </Button.Resources> </Button>无目标的放置语义
第一个词表示提示居中对齐于 XamlRoot 的哪一侧;有第二个词时提示会靠向对应角落。注意:无目标模式下两个词的顺序不影响结果——TopRight与RightTop等价。Center会让提示出现在 XamlRoot 的水平和垂直中心。
源码级回退算法
从 TeachingTip.cpp 的GetPlacementFallbackOrder()可以看到真实的回退优先级:算法维护一个 13 项优先级数组(初始顺序为 Top → Bottom → Left → Right → 四个角 → 四个边 → Center),然后根据首选方向做"交换排序":
- 首选为 Bottom 系:将 Bottom 提到 Top 之前;
- 首选为 Left/Right 系:将横向(Left/Right)提到纵向之前,Right 系再把 Right 提到 Left 之前;
- 最后用
std::rotate把首选模式旋转到第一位。
随后DetermineEffectivePlacementTargeted()对 13 种模式逐一计算可用性(目标是否足够大、是否有足够空间、尖角是否保持 12px 边缘间距等,见原文档"Placement"行为说明),按优先级返回第一个可用模式;若 13 种模式全部不可用,返回tipDoesNotFit = true,此时 TeachingTip 将不会打开,而是迭代完整的事件生命周期强制关闭(并在调试模式下通过TeachingTipTestHooks记录"因无空间而未打开"的遥测计数)。原文档特别警告:如果应用在 Closing 事件中取消了关闭,提示可能保持打开且没有可访问的关闭按钮。
非首选因素(placement 不考虑的条件)
根据原文档,以下条件在放置时不会被优先考虑:
- 提示无足够空间完整显示而不被裁剪;
- 目标不够大,无法维持提示对齐且保持尖角距提示边缘 12px;
- 目标元素过大,无法在保持尖角居中的同时维持边缘对齐。
放置边距(PlacementMargin)
PlacementMargin控制有目标提示与目标之间、无目标提示与 XamlRoot 边缘之间的间距。与FrameworkElement.Margin相同,它有 Left/Right/Top/Bottom 四个值,只有与当前放置方向相关的值会被使用——例如PlacementMargin.Left仅在提示位于目标左侧或 XamlRoot 左边缘时生效。
<Button x:Name="SaveButton" Content="Save" /> <controls:TeachingTip x:Name="AutoSaveTip" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." PreferredPlacement="BottomLeft" PlacementMargin="80"> </controls:TeachingTip>以上示例将无目标提示的四个方向边距都设为 80。在源码中,PositionPopup()/PositionTargetedPopup()/PositionUntargetedPopup()分别使用这些边距计算最终偏移(UntargetedTipNearPlacementOffset、UntargetedTipFarPlacementOffset等工具函数直接读取s_untargetedTipWindowEdgeMargin与 PlacementMargin 求和)。
内容区、按钮、图标与 Hero Content
添加内容(Content)
任意 XAML 内容都可以放入Content属性:文本、图片、视频、动画、复选框、超链接等。内容超出提示高度时,会自动启用滚动条。
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to."> <StackPanel> <CheckBox x:Name="HideTipsCheckBox" Content="Don't show tips at start up" IsChecked="{x:Bind HidingTips, Mode=TwoWay}" /> <TextBlock>You can change your tip preferences in <Hyperlink NavigateUri="app:/item/SettingsPage">Settings</Hyperlink> if you change your mind.</TextBlock> </StackPanel> </controls:TeachingTip> </Button.Resources> </Button>添加按钮
默认情况下,标题右侧显示一个标准的X 关闭按钮。可通过CloseButtonContent自定义关闭按钮文案——此时按钮会移动到提示底部。另外可通过ActionButtonContent添加自定义操作按钮,并可选配ActionButtonCommand与ActionButtonCommandParameter(注意:轻触关闭(light-dismiss)启用的提示不会显示任何关闭按钮)。
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." ActionButtonContent="Disable" ActionButtonCommand="DisableAutoSave" CloseButtonContent="Got it!"> <StackPanel> <CheckBox x:Name="HideTipsCheckBox" Content="Don't show tips at start up" IsChecked="{x:Bind HidingTips, Mode=TwoWay}" /> <TextBlock>You can change your tip preferences in <Hyperlink NavigateUri="app:/item/SettingsPage">Settings</Hyperlink> if you change your mind.</TextBlock> </StackPanel> </controls:TeachingTip> </Button.Resources> </Button>在 TeachingTip.xaml 模板中可以看到ButtonsStates状态组(NoButtonsVisible/ActionButtonVisible/CloseButtonVisible/BothButtonsVisible)与CloseButtonLocations状态组(HeaderCloseButton/FooterCloseButton)共同决定两个按钮的可见性、列位置与间距;AlternateCloseButton则是标题旁的无边框 X 按钮,其样式来自AlternateCloseButtonStyle主题资源。
Hero Content(通栏内容)
通过HeroContent属性添加拉伸到提示边缘的通栏媒体内容(如图片、视频),并用HeroContentPlacement将其置于提示顶部或底部:
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to."> <controls:TeachingTip.HeroContent> <Image Source="Assets/cloud.png" /> </controls:TeachingTip.HeroContent> </controls:TeachingTip> </Button.Resources> </Button>模板中的HeroContentPlacementStates(HeroContentTop/HeroContentBottom)通过HeroContentBorder的 Grid.Row 与圆角转换器实现上下切换。值得注意的细节(源码UpdateDynamicHeroContentPlacementToTop/Bottom与行为表"Tail/Hero Content Avoidance"):为避免尖角从 Hero Content 中"长出"的视觉怪相,控件会依次尝试:将 Hero 内容移到顶部或底部(HeroContentPlacement非 Auto 时禁用)、沿提示边缘平移尖角(edge-aligned 放置时禁用)、更换提示放置模式(非 Auto 放置时禁用)。
添加图标(IconSource)
使用IconSource在标题/副标题旁添加图标,推荐尺寸为 16px、24px、32px:
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to."> <controls:TeachingTip.IconSource> <controls:SymbolIconSource Symbol="Save" /> </controls:TeachingTip.IconSource> </controls:TeachingTip> </Button.Resources> </Button>模板中IconPresenter通过绑定TemplateSettings.IconElement渲染图标(IconStates状态组控制有无图标时的间距),TeachingTipTemplateSettings类(见 TeachingTip.idl)还暴露TopRightHighlightMargin/TopLeftHighlightMargin两个 Thickness,用于让容器顶部 1px 高光边缘"避让"尖角,保持视觉连续。
轻触关闭(Light-dismiss)
IsLightDismissEnabled默认关闭。开启后,提示会在用户滚动或与应用其他元素交互时自动消失,因此当提示需要放置在可滚动区域中时,轻触关闭是最佳方案。开启后控件会自动移除关闭按钮,以向用户表明其轻触关闭行为(模板LightDismissStates状态组同时会把背景切换为TeachingTipTransientBackground半透明画刷)。
<Button x:Name="SaveButton" Content="Save" /> <controls:TeachingTip x:Name="AutoSaveTip" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." IsLightDismissEnabled="True"> </controls:TeachingTip>源码层面,CreateLightDismissIndicatorPopup()会创建专门的m_lightDismissIndicatorPopup用于捕获提示之外的点击/滚动;OnLightDismissIndicatorPopupClosed()回调负责按LightDismiss原因触发关闭流程。IDL 中TeachingTipCloseReason枚举的三个成员(CloseButton/LightDismiss/Programmatic)正是Closing/Closed事件参数Reason的取值来源。
逃逸 XamlRoot 边界(ShouldConstrainToRootBounds)
在Windows 19H1 及以上版本,设置ShouldConstrainToRootBounds="False"可以让提示逃逸 XamlRoot 与屏幕边界,始终按设定的PreferredPlacement定位。官方强烈建议同时启用IsLightDismissEnabled,并将PreferredPlacement设为最接近 XamlRoot 中心的模式,以保证最佳体验。在更早的 Windows 版本上该属性会被忽略,提示始终约束在 XamlRoot 内。
<Button x:Name="SaveButton" Content="Save" /> <controls:TeachingTip x:Name="AutoSaveTip" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." PreferredPlacement="BottomRight" PlacementMargin="-80,-50,0,0" ShouldConstrainToRootBounds="False"> </controls:TeachingTip>从 TeachingTip.cpp 的DetermineEffectivePlacement()可以看到:当ShouldConstrainToRootBounds()为 false 时,放置算法切换到GetEffectiveScreenBoundsInCoreWindowSpace()得到的屏幕边界而非窗口边界(源码注释也指出:由于缺少多显示器 API 信息,超出根边界的场景不做特殊计算,默认返回首选值或 Top,该行为可通过SetReturnTopForOutOfWindowPlacement测试钩子调整)。
取消与延迟关闭(Closing / Closed 事件)
Closing事件可用于**取消(cancel)或延迟(defer)**提示的关闭,以便保持提示打开、为某个动作或自定义动画争取时间。关键行为:
- 关闭被取消时,
IsOpen会回到 true; - 延迟期间(deferral 未完成时)
IsOpen保持 false; - 程序化关闭同样可以被取消。
<controls:TeachingTip x:Name="EnableNewSettingsTip" Title="New ways to protect your privacy!" Subtitle="Please close this tip and review our updated privacy policy and privacy settings." Closing="OnTipClosing"> </controls:TeachingTip>public async void OnTipClosing(object sender, TeachingTipClosingEventArgs args) { if (args.Reason == TeachingTipCloseReason.CloseButton) { using (args.GetDeferral()) { bool success = await UpdateUserSettings(User thisUsersID); if (!success) { // We were not able to update the settings! // Don't close the tip and display the reason why. args.Cancel = true; ShowLastErrorMessage(); } } } }重要警告(原文档原文强调):如果没有任何放置选项能让提示完整显示,提示会迭代完整的事件生命周期以强制关闭,而不是在缺少可访问关闭按钮的情况下显示。如果应用此时取消了Closing事件,提示可能保持打开且没有可访问的关闭按钮。
IDL 中TeachingTipClosingEventArgs暴露Reason(只读)、Cancel(可写)与GetDeferral();TeachingTipClosedEventArgs只暴露Reason。另外仓库源码中还有一个[MUX_PREVIEW]的Opened事件(TeachingTipOpenedEventArgs),用于打开完成的回调。
重新配置已打开的提示(Reconfiguring)
部分内容与属性可以在提示打开期间修改并立即生效;但以下变更必须关闭并重新打开才会生效:
- 图标属性(
IconSource); - Action / Close 按钮相关属性;
- 在轻触关闭与显式关闭之间切换。
特别注意(原文档警告):在提示打开时从手动关闭(manual-dismiss)切换到轻触关闭,会先移除关闭按钮再启用轻触关闭行为,提示可能卡在屏幕上无法关闭。
事件与 API 速查
Notable Properties(API Notes 原表)
| 属性 | 说明 |
|---|---|
| TailVisibility | 获取或设置提示尖角是折叠还是保持可见(Auto / Visible / Collapsed) |
| PreferredPlacement | 获取或设置提示的默认放置模式 |
| ShouldConstrainToRootBounds | 获取或设置提示是否约束在根边界内 |
Events(原表)
| 事件 | 说明 |
|---|---|
| ActionButtonClick | 操作按钮被点击后发生 |
| CloseButtonClick | 关闭按钮被点击后发生 |
| Closed | 提示关闭后发生 |
| Closing | 提示即将开始关闭时发生 |
完整属性清单(来自 IDL,含默认值)
| 属性 | 类型 / 默认值 |
|---|---|
| Title / Subtitle | string |
| IsOpen | bool,默认false |
| Target | FrameworkElement |
| TailVisibility | 默认Auto |
| ActionButtonContent / ActionButtonStyle / ActionButtonCommand / ActionButtonCommandParameter | object / Style / ICommand / object |
| CloseButtonContent / CloseButtonStyle / CloseButtonCommand / CloseButtonCommandParameter | object / Style / ICommand / object |
| PlacementMargin | Thickness |
| ShouldConstrainToRootBounds | bool,默认true |
| IsLightDismissEnabled | bool,默认false |
| PreferredPlacement | 默认Auto |
| HeroContentPlacement | 默认Auto |
| HeroContent / IconSource | UIElement / IconSource |
| TemplateSettings | TeachingTipTemplateSettings(只读) |
视觉与行为组件详解(原文档 Appendix)
视觉组件(Visual Components)
| 组件 | 说明 |
|---|---|
| Container | 提示主体,封装所有组件;非模态;内容高度/宽度超过上限时启用垂直滚动;外缘有随尖角贴合的过程描边;顶部边缘有 1px 高光并随尖角贴合 |
| Title | 半粗体,在关闭按钮与容器边框处自动换行 |
| Subtitle | 在关闭按钮与容器边框处自动换行 |
| Content | 可定制任意 XAML 内容;超过高度时滚动;位于 Subtitle 下方、关闭/操作按钮上方 |
| Close Button | 默认在右上角显示 X(RTL 语言自动移到左上角);可设置为底部常规按钮,也可完全隐藏以便在内容区自定义关闭方式;轻触关闭时完全不显示 |
| Action Button | 允许用户触发自定义事件;这是开箱提供的唯一非关闭按钮 |
| Tail | 指向屏幕 UI 元素的三角形延伸;TailVisibility为 Auto 时,有目标自动显示、无目标自动隐藏;优先居中于目标;距提示边缘保持12px间距;不参与动画;无阴影(非矩形表面暂不支持阴影) |
| Icon | 默认位于标题/副标题左侧,RTL 时自动移到右侧 |
| Hero Content | 拉伸到提示边缘的媒体;可置于顶部或底部 |
| Scroll Bar | 内容过大时出现在内容区,不会与右上角 X 按钮相交 |
关于 Tail 的形状细节,源码注释(TeachingTip.h)特别说明:尖角设计为 8x16 像素形状,实际实现为 10x20 形状并部分被提示内容遮挡,这样可以让提示边框沿尖角形状贴合,而无需在尖角与提示主体相接的边上画边框。同时MinimumTipEdgeToTailEdgeMargin()等函数精确计算了尖角与边缘的最小间距。
行为组件(Behavioral Components)
| 行为 | 说明 |
|---|---|
| Opening | 通过IsOpen = true显示,带开启动画;若任何位置都无足够空间完整显示,则不会打开并将 IsOpen 覆写为 false |
| Closing | 三种关闭方式:程序设IsOpen=false、用户点击关闭按钮、轻触关闭;用TeachingTipCloseReason区分;可用Cancel=true阻止关闭;可用 deferral 异步响应 |
| Placement | 有目标放置遵循 Flyout 先例,Center使尖角指向元素中心;无目标放置覆盖窗口每侧、每角与中心;不优先考虑:无足够空间、目标过小、目标过大无法保持尖角居中 |
| Light-dismiss | 用户滚动或点击应用其他区域时关闭 |
| Persistent Tip Location | 打开后提示不会随目标移动(窗口调整大小除外) |
| Motion | 内置开/关动画,可通过 Storyboard 自定义(源码中CreateExpandAnimation/CreateContractAnimation,默认展开 300ms、收起 200ms,均可用测试钩子调整) |
| Tail/Hero Content Avoidance | 依次尝试移动 Hero 内容、平移尖角、更换放置模式,避免尖角与 Hero 内容相交 |
| Out of Window Bounds | 新系统上可用ShouldConstrainToRootBounds=false让提示逃逸窗口,改用屏幕边界参与放置算法 |
输入与无障碍(Inputs & Accessibility)
UI Automation 模式
- TeachingTip 会在持久提示与轻触关闭提示之间切换 Pane / Window 自动化模式;
- 可滚动内容区提供
IScrollProvider; - 实现自定义的"tip" Landmark(模板中
ContentRootGrid设置了AutomationProperties.LandmarkType="Custom",与规范一致)。
键盘导航
| 状态 | 操作 |
|---|---|
| 提示出现 | 无需任何操作即可调用提示 |
| 提示获得焦点 | F6:提示被加入 F6 区域导航停止点,可用 F6 进入/离开;Tab:Narrator 激活时,提示自动加入 Narrator 导航停止点顶部(类似 Popup / ContentDialog),可通过 Tab 进入 |
| Tab 遍历提示 | Tab:按顺序遍历所有可操作项;在最后一个元素上按 Tab,焦点循环回第一个元素;左右方向键:两个底部按钮都存在时可在其间导航;Esc:关闭提示 |
| 提示被关闭 | 1. 按 X 按钮;2. 按关闭按钮;3. 按操作按钮。Tab 使焦点前进到下一元素但不会关闭提示 |
Narrator
TeachingTip 复用 Windows 通知使用的现有 API。有目标提示会注入其目标名称,在标题前与标题一起被 Narrator 朗读,提供目标上下文。通知语音流程:"Click Up to move to new notification from" + App 名 + 提示内容;触摸屏设备上可通过Swipe遍历所有可操作项,在最后一项再次滑动会将焦点移到 Narrator 的全屏隐形关闭按钮,双击屏幕关闭窗口,再次滑动移出提示。
游戏手柄(Gamepad)
| 状态 | 操作 |
|---|---|
| 提示出现 | 无需操作 |
| 提示获得焦点 | 空间导航(spatial navigation)可访问提示;官方建议为提示可达性与测试做合理设计 |
| 提示被导航 | 空间导航遍历可操作项;A 键交互(如"按下"操作/关闭按钮);B 键关闭提示 |
| 提示被关闭 | 1. 头部 X 按钮;2. 底部关闭按钮;3. 操作按钮;4. B 键将焦点还给之前聚焦的元素 |
原文档明确提醒:TeachingTip 的手柄可达性并不被保证,因此针对预测手柄输入的应用,必须用应用 UI 的所有可能配置逐一测试。
结语:一段可复制的完整示例
结合以上所有特性,下面是一个同时包含目标、内容、Hero 内容、图标、双按钮与延迟关闭检查的完整示例,可直接在 WinUI 3 应用中运行:
<Button x:Name="SaveButton" Content="Save"> <Button.Resources> <controls:TeachingTip x:Name="AutoSaveTip" Target="{x:Bind SaveButton}" Title="Saving automatically" Subtitle="We save your changes as you go - so you never have to." PreferredPlacement="Bottom" PlacementMargin="12" ActionButtonContent="Disable" CloseButtonContent="Got it!" Closing="OnTipClosing"> <controls:TeachingTip.IconSource> <controls:SymbolIconSource Symbol="Save" /> </controls:TeachingTip.IconSource> <controls:TeachingTip.HeroContent> <Image Source="Assets/cloud.png" /> </controls:TeachingTip.HeroContent> <StackPanel> <CheckBox Content="Don't show tips at start up" /> </StackPanel> </controls:TeachingTip> </Button.Resources> </Button>private void OnTipClosing(TeachingTip sender, TeachingTipClosingEventArgs args) { if (args.Reason == TeachingTipCloseReason.CloseButton) { // 在此执行必要的清理,若失败则 args.Cancel = true 阻止关闭 } }后续你可以:
- 阅读 控件源码 深入放置算法、动画与焦点处理;
- 查看 API 测试 与 交互测试 了解已验证的行为矩阵;
- 在 TestUI 页面 中探索控件在真实应用里的各种配置形态;
- 参考 视觉组件附录图片(Container、Title、ScrollBar 等)核对控件外观规格。
【免费下载链接】microsoft-ui-xamlWinUI: a modern UI framework with a rich set of controls and styles to build dynamic and high-performing Windows applications.项目地址: https://gitcode.com/GitHub_Trending/mi/microsoft-ui-xaml
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考