WinUI PipsPager 控件详解:圆点分页导航的 API、样式定制与无障碍实现 WinUI PipsPager 控件详解圆点分页导航的 API、样式定制与无障碍实现【免费下载链接】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-xamlPipsPager 是 WinUImicrosoft-ui-xaml 仓库提供的一种轻量分页控件用圆点pip而非数字页码来表示分页位置适用于照片轮播、应用列表等媒体浏览场景。本文以仓库中的 PipsPager 设计规范 为主线结合 PipsPager.cpp、PipsPager.xaml 与 PipsPager.idl 等源码实现系统讲解它的设计动机、全部公开 API、主题资源定制方法、内部模板结构、输入与无障碍行为并给出可直接复制运行的 XAML 示例。设计背景为什么需要 PipsPager在大量应用中媒体与数据以列表List、网格Grid和表格Table等形式呈现。用户除了滚动scroll和扫视pan内容外还常常需要借助上一页/下一页按钮或直接跳转到指定页码来分页浏览内容。WinUI 中已经存在一个支持数字页码的分页控件PagerControl它支持多种页码可视化格式可参考仓库中对应的设计文档 docs/design-notes/ 与 specs/ 目录下的相关规范。但媒体查看器场景如照片轮播、应用列表中存在一个 PagerControl 尚未覆盖的常见 UI 模式用圆点字形而非数字来表示页面以鼓励用户探索内容、减少页面上不必要的 UI 元素。从概念上讲这就像用项目符号bullet代替数字来表示无序列表。新的PipsPager控件正是为此而生当页面内容没有显式编号、或你希望使用基于字形的表示时都可以使用它。认识 PipsPager控件定位PipsPager 是一个表示控件representation control它为分页布局视图ListView、GridView、ItemsRepeater、DataGrid 等提供交互与导航但不控制布局视图中显示的任何数据与其它分页控件一样与布局视图保持独立。你可以通过绑定让二者同步见下文 FlipView 集成示例。什么时候该用 PipsPager当布局中的内容没有按相关性显式排序、或你希望以字形方式表示编号页面时使用。它常见于照片查看器、应用列表以及显示空间受限的场景且可以水平或垂直排列。什么是 pipPip 是数值的一个单位表示通常是一个圆点但也可以自定义为短横线、方块等其它字形。在 PipsPager 中默认每个页面显示一个实心圆点用户可以选择某个 pip 跳转到对应页面。快速上手PipsPager 基础示例本节示例全部来自设计规范原文并补充了关键属性说明。创建最基本的 PipsPager一个包含五个可见 pip 的 PipsPager用户可直接点击 pip 跳转到指定页也可使用上一页/下一页导航按钮逐页切换。在首尾两端导航不会回绕wrap around。默认情况下导航按钮折叠Collapsed、pip 水平排列、总页数为无限。muxc:PipsPager x:NameDefaultPipsPager /显示导航按钮上一页/下一页按钮的可见性由PreviousButtonVisibility与NextButtonVisibility属性控制取值来自PipsPagerButtonVisibility枚举Visible按钮可见且可用但在边界处隐藏。例如当前页为第一页时上一页按钮会隐藏。注意这里的隐藏指按钮不可见但仍占布局空间实际实现是通过 Opacity0 的视觉状态实现见 PipsPager.xaml 中的PreviousPageButtonHidden状态。VisibleOnPointerOver行为与 Visible 相同区别在于按钮仅在鼠标悬停在分页 UI 上时才可见。Collapsed用户不可见且不占用布局空间。默认值muxc:PipsPager x:NameVisibleButtonPipsPager NumberOfPages5 PreviousButtonVisibilityVisible NextButtonVisibilityVisible /垂直 PipsPager VisibleOnPointerOverPipsPager 支持垂直Vertical方向其行为与交互方式不变各种按钮可见性模式对两种方向都适用。muxc:PipsPager x:NameVerticalPipsPager NumberOfPages5 OrientationVertical PreviousButtonVisibilityVisibleOnPointerOver NextButtonVisibilityVisibleOnPointerOver /限制可见 pip 数量MaxVisiblePips当内容页数很多、且无需一次性全部导航时可通过MaxVisiblePips限制可见且可交互的 pip 数量当NumberOfPages大于MaxVisiblePips时pip 会滚动使当前选中页在控件中居中当NumberOfPages小于等于MaxVisiblePips时不发生滚动显示数量与NumberOfPages一致如果布局空间不足以容纳MaxVisiblePips个 pip多余 pip 会被裁剪布局空间取MaxVisiblePips与总 pip 数之间的较小值。默认最大可见 pip 数为5。muxc:PipsPager x:NameScrollingPipsPager NumberOfPages20 MaxVisiblePips10 /从源码看滚动居中是通过ScrollToCenterOfViewportPipsPager.cpp实现的它构造BringIntoViewOptions水平方向设置HorizontalAlignmentRatio(0.5)、垂直方向设置VerticalAlignmentRatio(0.5)并开启动画AnimationDesired(true)将选中 pip 带到视口中央。与集合控件FlipView集成PipsPager 最常见的用法是与集合控件搭配下面的例子把 PipsPager 与 FlipView 绑定既提供内容分页指示又提供额外导航途径。提示如果只想把 PipsPager 当作纯页面指示器把IsEnabled设为false即可禁用用户交互。StackPanel FlipView x:NameGallery MaxWidth400 Height270 ItemsSource{x:Bind Pictures} FlipView.ItemTemplate DataTemplate x:DataTypex:String Image Source{x:Bind ModeOneWay}/ /DataTemplate /FlipView.ItemTemplate /FlipView !-- SelectedPageIndex 与 FlipView 双向绑定以保持同步 -- muxc:PipsPager x:NameFlipViewPipsPager HorizontalAlignmentCenter Margin0, 10, 0, 0 NumberOfPages{x:Bind Pictures.Count} SelectedPageIndex{x:Bind PathGallery.SelectedIndex, ModeTwoWay} / /StackPanelSelectedPageIndex是 0 基索引双向绑定后无论用户滑动 FlipView 还是点击 pip两者都会保持同步。Pip 与导航按钮的样式定制导航按钮与 pip 分别通过PreviousButtonStyle、NextButtonStyle、SelectedPipStyle、DefaultPipStyleIDL 中为NormalPipStyle见 PipsPager.idl四个样式属性自定义。优先级规则如果PreviousButtonStyle或NextButtonStyle在样式中显式设置了按钮的Visibility属性则该设置优先于PreviousButtonVisibility/NextButtonVisibility——除非这两个属性被显式设置为PipsPagerButtonVisibility.Collapsed。下面的示例基于主题资源中的PipsPagerNavigationButtonBaseStyle派生自定义样式把导航按钮替换为尖角caret字形#xEDDB;/#xEDDC;为 Segoe MDL2 Assets 中的字形编码Page.Resources Style x:KeyNavButtonBaseStyle TargetTypeButton BasedOn{StaticResource PipsPagerNavigationButtonBaseStyle} Setter PropertyWidth Value30 / Setter PropertyHeight Value30 / Setter PropertyFontSize Value12 / /Style Style x:KeyPreviousButtonStyle BasedOn{StaticResource NavButtonBaseStyle} TargetTypeButton Setter PropertyContent Value#xEDDB; / /Style Style x:KeyNextButtonStyle BasedOn{StaticResource NavButtonBaseStyle} TargetTypeButton Setter PropertyContent Value#xEDDC; / /Style /Page.Resources muxc:PipsPager x:NameCustomNavButtonPipsPager PreviousButtonStyle{StaticResource PreviousButtonStyle} NextButtonStyle{StaticResource NextButtonStyle} PreviousButtonVisibilityVisibleOnPointerOver NextButtonVisibilityVisibleOnPointerOver /仓库中 TestUI/PipsPagerExamples.xaml 提供了上述各类场景的完整可运行示例页面可直接对照参考。PipsPager 成员详解设计规范给出了以下成员表默认值与 PipsPager.idl 中MUX_DEFAULT_VALUE标注一致名称说明默认值NumberOfPages设置索引控件可迭代的最大页数。默认表示无限页范围页数无限时用户可以无限滚动并选择 pip。-1PreviousButtonStyle / NextButtonStyle自定义导航按钮的文本或字形。主题资源中定义的上一页/下一页按钮字形、Content 与PipsPagerNavigationButtonBaseStyleDefaultPipStyle / SelectedPipStyle自定义 pip 的外观。基于主题资源中默认/选中 pip 资源与PipsPagerButtonBaseStyleNextButtonVisibility设置下一页按钮的可见性。CollapsedPreviousButtonVisibility设置上一页按钮的可见性。CollapsedSelectedPageIndex当前选中的 0 基索引默认指向第一个索引。0MaxVisiblePips控件中同时出现的最大 pip 数。如果可能UI 会滚动使选中 pip 居中。合法范围是 0设为 0 时用户看不到任何页面。5Orientation控件方向可为 Vertical 或 Horizontal。HorizontalSelectedIndexChanged用户选中 pip 或点击方向按钮后触发的事件返回用户选中页的索引号。N/A新成员WrapMode源码新增设计规范中的 IDL 片段是初版草案仓库实际实现中PipsPager.idl 已追加了PipsPagerWrapMode枚举与WrapMode属性标注为MUX_PUBLIC_V7None默认在首尾边界停止不回绕Wrap启用回绕模式。此时从第一页点上一页会跳到最后一页从最后一页点下一页会回到第一页。该行为在 PipsPager.cpp 的按钮点击处理中实现OnPreviousButtonClicked/OnNextButtonClicked。此外从 PipsPager.cpp 的UpdateLayoutVirtualization可以看出启用 Wrap 时控件会关闭 ItemsRepeater 的布局虚拟化以避免从第一页跳到最后一页时 pip 消失的 bug——这是使用 WrapMode 时值得一提的实现细节。通过主题资源定制外观PipsPager 支持轻量级样式定制lightweight styling不重写整个模板只覆盖应用中的 XAML 资源即可修改外观详细机制可参考仓库的 docs/design-notes/ThemeResource-from-code.md 与 docs/how-to-author-a-xaml-control.md。可覆盖的资源键资源键说明类型PipsPagerVerticalButtonWidth垂直方向下每个 pip 的边界框宽度DoublePipsPagerVerticalButtonHeight垂直方向下每个 pip 的边界框高度DoublePipsPagerHorizontalButtonWidth水平方向下每个 pip 的边界框宽度DoublePipsPagerHorizontalButtonHeight水平方向下每个 pip 的边界框高度DoublePipsPagerButtonBorderThickness每个 pip 的边框厚度ThicknessPipsPagerNavigationButtonBorderThickness每个导航按钮的边框厚度ThicknessPipsPagerSelectedGlyph选中 pip 的 MDL2 图标字形StringPipsPagerNormalGlyph默认 pip 的 MDL2 图标字形StringPipsPagerNavigationButtonHeight每个导航按钮的高度DoublePipsPagerNavigationWidth每个导航按钮的宽度DoublePipsPagerNavigationButtonFontSize每个导航按钮的字体大小DoublePipsPagerSelectedGlyphFontSize选中 pip 字形的像素大小DoublePipsPagerNormalGlyphFontSize静止 pip 字形的像素大小Double仓库实际主题资源文件 PipsPager_themeresources.xaml 中的默认值可作为参考如水平方向按钮框 12×24、垂直方向 24×12、导航按钮 24×24、导航按钮字形字体 8、选中字形 6、普通字形 4以及PipsPagerPreviousPageButtonGlyph#xEDDB;与PipsPagerNextPageButtonGlyph#xEDDC;。静态资源状态相关以下资源随控件的 Normal / PointerOver / Pressed / Selected / Disabled 状态变化可作为 StaticResource 覆盖资源键说明PipsPagerSelectionIndicatorBackground(-PointerOver/-Pressed/-Selected/-Disabled)设置 pip 背景PipsPagerSelectionIndicatorBorderBrush(-PointerOver/-Pressed/-Selected/-Disabled)设置 pip 边框PipsPagerSelectionIndicatorForeground(-PointerOver/-Pressed/-Selected/-Disabled)设置 pip 前景PipsPagerNavigationButtonBackground(-PointerOver/-Pressed/-Disabled)设置导航按钮背景PipsPagerNavigationButtonBorderBrush(-PointerOver/-Pressed/-Disabled)设置导航按钮边框PipsPagerNavigationButtonForeground(-PointerOver/-Pressed/-Disabled)设置导航按钮前景这些资源在主题字典Light / Default / HighContrast中均有定义Light/Default 主题映射到ControlFillColor*/ControlStrongFillColor*等系统笔刷HighContrast 主题则映射到SystemColor*笔刷以保障高对比度下的可访问性。内部模板与实现原理默认模板结构PipsPager.xaml 中的默认 ControlTemplate 揭示了控件内部结构理解它有助于自定义模板根节点为StackPanel x:NameRootPanel其Orientation通过{TemplateBinding Orientation}与控件属性绑定内部依次排列PreviousPageButton上一页按钮、ScrollViewer名为PipsPagerScrollViewer、NextPageButton下一页按钮ScrollViewer内部是一个ItemsRepeater名为PipsPagerItemsRepeater其ItemsSource绑定到TemplateSettings.PipsPagerItems布局使用StackLayout并跟随Orientation方向ItemTemplate 就是一个空Button——每个 pip 本质上是一个 Button模板中定义了四组 VisualStatePreviousPageButtonVisibilityStates、NextPageButtonVisibilityStatesVisible / Hidden / Collapsed、PreviousPageButtonIsEnabledStates、NextPageButtonIsEnabledStates以及方向状态组RootPanelOrientationStates垂直方向时导航按钮通过RotateTransform Angle-90旋转 90° 复用同一套按钮模板。水平方向下 pip 按钮模板PipsPager_themeresources.xaml中RootGrid的宽高引用PipsPagerHorizontalOrientationButtonWidth/Height并通过OrientationStates视觉状态在垂直方向切换为PipsPagerVerticalOrientationButtonWidth/Height。核心实现逻辑从 PipsPager.cpp 可以确认几个关键行为pip 列表的生成无限页UpdatePipsItemsL310-L346维护一个IVectorInt32m_pipsPagerItems即PipsPagerTemplateSettings.PipsPagerItems。NumberOfPages 0或MaxVisiblePips 0时清空列表NumberOfPages 0无限模式时列表会随SelectedPageIndex增长持续追加新 pip有限页时则按需增删到NumberOfPages大小。滚动区域尺寸计算CalculateScrollViewerSizeL274-L290用公式defaultPipSize × (可视数 - 1) selectedPipSize计算 ScrollViewer 的最大尺寸其中可视数为min(MaxVisiblePips, NumberOfPages)无限页时直接取MaxVisiblePips。这也验证了规范中布局空间取 MaxVisiblePips 与总 pip 数较小值的描述。索引越界钳制OnSelectedPageIndexChangedL418-L447会把SelectedPageIndex强制限制在合法区间超出NumberOfPages - 1时钳到末页小于 0 时钳到首页然后依次更新选中 pip 样式、导航按钮状态并通过RaiseSelectedIndexChanged触发SelectedIndexChanged事件。pip 点击处理OnElementPreparedL348-L377为每个 pip 按钮挂接 Click 事件点击时通过repeater.GetElementIndex(button)反查索引并写入SelectedPageIndex。Automation 命名OnApplyTemplate中为控件设置 PipsPager 名称为每个 pip 设置 Page N 名称并写入PositionInSet/SizeOfSet自动化属性对应屏幕阅读器播报的 page x of y。键盘方向映射OnKeyDownL161-L191把方向键映射为焦点移动方向垂直方向用 Up/Down水平方向用 Left/Right并借助FocusManager::TryMoveFocus移动焦点。输入与无障碍行为键盘Tab 进入 PipsPager 时焦点首先落在第一个可操作项上即上一页按钮若未 Collapsed或当前选中的 pip。未折叠的方向按钮也可以通过 Tab 到达获得焦点时可见。方向键可在 pip 与方向按钮之间导航。与方向设置无关左/上键把焦点移到上一个 pip 或上一个按钮右/下键移到下一个。可操作项用空格键Space选择。Gamepad游戏手柄PipsPager 可通过空间导航spatial navigation到达。选中 pip 会自动获得焦点导航到即选中。无论PipsPagerButtonVisibility设置如何导航按钮对用户都不可见。水平方向左右空间导航聚焦并选中 pip用户向上/向下导航、或试图越过第一页/最后一页时焦点离开 PipsPager。规范建议不要在水平 PipsPager 的左右两侧放置 UI。垂直方向上下空间导航聚焦并选中 pip向左/右导航或越过首尾页时焦点离开。建议不要在垂直 PipsPager 的上方或下方放置 UI。触摸为触摸优化体验规范建议将 PipsPager 与视图控件如 FlipView见上文与集合控件集成一节搭配使用以便利用内容上的触摸翻页能力。用户也可以直接触摸选中单个 pip。屏幕阅读器焦点在控件上时屏幕阅读器播报 pager。焦点在上一页/下一页按钮上时播报 previous page / next page若开发者为按钮设置了文本属性则播报该文本。焦点在 pip 上时播报 page x of y页数无限时播报 page x。pip 被选中时播报 page x of y selected。这些播报对应源码中的自动化实现控件名称使用本地化资源SR_PipsPagerNameText按钮名称使用SR_PipsPagerPreviousPageButtonText/SR_PipsPagerNextPageButtonTextpip 名称使用SR_PipsPagerPageText拼接序号PipsPager.cpp、PipsPagerAutomationPeer.cpp本地化字符串位于 controls/dev/PipsPager/Strings/en-us/Resources.resw并提供了 80 语言版本。测试验证仓库为 PipsPager 提供了完整的 API 与交互测试可作为理解控件契约的参考APITests/PipsPagerTests.cs验证自动化对等对象行为ISelectionProviderCanSelectMultiple false、IsSelectionRequired true、pip 按钮的PositionInSet/SizeOfSet属性、空 Pager 不崩溃、以及SelectedIndexChanged事件随NumberOfPages/SelectedPageIndex变化的触发结果InteractionTests/PipsPagerTests.cs 与 InteractionTests/PipsPagerElements.cs覆盖真实用户交互路径的端到端测试TestUI/PipsPagerPage.xaml测试应用中的展示与手动验证页面。尚未决的问题Open Questions设计规范最后列出两个待定问题也是后续演进方向的线索如何针对该形态因子优化触摸输入PipsPager 是否需要一种标准可视化默认放大 pip 与导航按钮、可直接触摸操作键盘与 Gamepad 输入时选择是否应该跟随焦点selection should follow focus小结PipsPager 用最少的 UI 元素完成了指示页码 跳转页面两大职责在媒体浏览场景中替代数字分页。通过NumberOfPages、MaxVisiblePips、Orientation、PreviousButtonVisibility/NextButtonVisibility以及四类样式属性开发者可以快速搭建默认、滚动、垂直、自定义外观等多种形态主题资源支持轻量级外观定制键盘、Gamepad、触摸与屏幕阅读器均有明确行为约定且与 FlipView 等集合控件通过SelectedPageIndex双向绑定即可无缝集成。若需深入源码可从 PipsPager.cpp 与 PipsPager_themeresources.xaml 入手并结合 specs/PipsPager/PipsPager.md 规范对照阅读。【免费下载链接】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),仅供参考