news 2026/9/16 13:34:28

WinUI 3 TableView 控件实战:基于 TableViewSampleApp 掌握 Tabular 数据表格的构建、主题资源与数据整形

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WinUI 3 TableView 控件实战:基于 TableViewSampleApp 掌握 Tabular 数据表格的构建、主题资源与数据整形

WinUI 3 TableView 控件实战:基于 TableViewSampleApp 掌握 Tabular 数据表格的构建、主题资源与数据整形

【免费下载链接】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

导读

本文以 Samples/TableViewSampleApp/README.md 为主体,结合 controls/dev/TableView/TableView.idl、controls/dev/TableView/TableViewSource.idl 及示例源码,系统讲解 microsoft-ui-xaml 仓库中 WinUI 3 的Microsoft.UI.Xaml.Controls.Tabular.TableView控件。你将掌握:TableView 与 Tabular.dll 的发布形态与本地打包构建流程、在自己应用中正确合并主题资源字典的必备操作、列/宽度/外观/编辑/选择等核心 API,以及基于TableViewSource的过滤、排序、分组数据整形方案,并能定位两类最常见的运行时失败。

TableView 与 Tabular.dll:独立控件集的发布形态

TableView是一个 WinUI 3 桌面端数据表格控件,位于独立的Microsoft.UI.Xaml.Controls.Tabular.dll中,不随主框架 DLL 一起编译(README.md)。但这并不影响它在普通应用中被当作常规 NuGet 包消费:

  • 类型信息会进入公开的 winmd(MergedWinMD合并产物);
  • <ActivatableClass>激活注册会随合并输入一并生成;
  • 控件的主题资源(theme XBF、.pri)随包发布并可被解析。

因此 TableViewSampleApp 是一个普通消费者:只引用Microsoft.WindowsAppSDK.WinUI包,不做任何特殊接线,与仓库中 Samples/ChartApp 系列示例采用同一模式。从源码结构看,这正是本仓库对“独立构建的控件集”既定的示例组织方式。

示例应用总览:左侧调参、右侧实时出表

TableViewSampleApp 是一个小型的 WinUI 3 桌面应用,通过 MainWindow.xaml 中的NavigationView承载 9 个页面:

  • Playground:核心演练页,左栏是每个 TableView API 对应的控件面板,右栏是实时渲染的表格与“Live data source”数据源回显(PlaygroundPage.xaml);
  • All Auto columns / All Star columns / All Pixel columns / Mixed columns:分别演示单一宽度模型与混合列宽;
  • Interactive cells:展示可交互、可展开的单元格模板;
  • Selection:演示单选、Select/Deselect/DeselectAll与“选中跟随数据项”的行为;
  • Cell tooltips:演示单元格 ToolTip;
  • Filter / sort / group:演示TableViewSource的数据整形能力。

行数据由 Data.cs 构造:约 150 行(Make(n = 150))使表格主体可以虚拟化滚动;姓名分为 Short/Medium/Wide 三档宽度,且宽姓名被“隔离”地安插在每 50 行的固定位置,从而让 Auto 列的起始宽度很窄、滚动到宽姓名时才明显变宽——这是特意设计的 Auto 列“增长”演示。Item实现了INotifyPropertyChanged,文本属性可写,配合双向绑定即可验证“编辑单元格 → 数据源更新 → 显示单元格刷新”的完整链路。

环境准备

  • Visual Studio 2022,需安装Desktop development with C++.NET Desktop两个工作负载;
  • 在仓库根目录运行过一次完整初始化:
.\init.cmd

构建与打包:先本地打包 Tabular 组件

为什么不能直接构建

TableView尚未进入任何已发布的Microsoft.WindowsAppSDK.WinUI包,因此单独构建本示例会从 feed 解析到旧版包并失败:

CS0234: The type or namespace name 'Tabular' does not exist

包本身是真实存在的,只是其公开 winmd 早于 Tabular。支持的标准流程是先本地打包组件,再以匹配的版本号构建:

.\Build.cmd product .\Build.cmd samples

Build.cmd samples内部会依次执行pack.component.cmd,并以匹配的/p:WinUIVersion构建全部示例(见 AGENTS.md)。

单应用快速迭代

只迭代这一个应用时,打包一次后直接构建:

.\pack.component.cmd /version 3.0.0-mylocal .\initrun.ps1 msb /q /restore Samples\TableViewSampleApp\TableViewSampleApp.csproj /p:Platform=x64 /p:WinUIVersion=3.0.0-mylocal

注意:/p:WinUIVersion必须与pack.component.cmd /version给出的版本号一致,构建才能解析到本地刚打包的组件。

NuGet 版本缓存陷阱

每次改动控件后都要重新执行pack.component.cmd。NuGet 按版本号缓存包(缓存目录为packages\microsoft.windowsappsdk.winui\),因此:

  • 复用同一版本号会被静默忽略——重新打包不会覆盖缓存;
  • 要么在重新打包前删除packages\microsoft.windowsappsdk.winui\目录,要么(更推荐)每次使用全新的版本字符串。

运行

BuildOutput\obj\amd64chk\Samples\TableViewSampleApp\TableViewSampleApp.exe

构建产物说明:为什么应用的.pri很大

控件 DLL、其.pri与主题 XBF 均来自 NuGet 包。消费者应用构建时会展开所有被引用的.pri并重新索引进应用自己的TableViewSampleApp.pri——该文件之所以达数 MB,是因为它同时包含 MUXC、Tabular 与本示例三方的资源(AGENTS.md)。这是正常现象,不是配置错误。

在自己的应用中使用 TableView

步骤一:引用包并合并 TabularControlsResources

参考Microsoft.WindowsAppSDK.WinUI后,在App.xaml同时合并XamlControlsResourcesTabularControlsResources

<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <!-- Tabular ships its own theme resources, exactly as MUXC ships XamlControlsResources. TableView's column-header style resolves SortIndicatorForeground from this dictionary, so activation fails without it. --> <TabularControlsResources xmlns="using:Microsoft.UI.Xaml.Controls.Tabular" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources>

(完整文件见 App.xaml。)

TabularControlsResources是 Tabular 自己的主题资源字典,相当于 MUXC 的XamlControlsResources必选而非可选。根因在源码层:TableView的列头样式会解析SortIndicatorForeground,而该资源定义在 Tabular 的主题资源中——因为SortIndicator控件随 Tabular.dll 发布而非 MUXC(controls/Tabular.ProjectImports.targets 是SortIndicator.vcxitems的唯一导入方,见 AGENTS.md)。

漏合并的症状:首次布局期间抛出XamlParseException 0x802B000A,在应用启动数秒后表现为0xC000027Bstowed exception。

步骤二:声明列

不声明列时,表格会渲染出行,但没有列头行、也没有单元格。最简声明方式:

<tabular:TableView x:Name="Table"> <tabular:TableViewTextColumn Header="Name" Binding="{Binding Name}" /> <tabular:TableViewTextColumn Header="Age" Binding="{Binding Age}" /> </tabular:TableView>

TableViewTextColumn.Binding在 IDL 中是 CLR 属性(TableView.idl),因此 XAML 中的Binding值会经 setter 正确路由。示例同时使用SampleColumns.Text(...)工厂在代码中构建列,并提供了Auto()/Star(factor)/Pixels(px)三个GridLength便捷工厂(SampleColumns.cs)。

步骤三:处理 [MUX_PREVIEW] 警告

TableView标记为[MUX_PREVIEW]

  • C# 用法产生CS8305(“for evaluation purposes only”),示例在 TableViewSampleApp.csproj 中通过NoWarn抑制;
  • XAML 用法每页产生一次WMC1501,示例故意保留可见,提醒该 API 处于预览状态。

控件 API 全景:从 TableView.idl 看能力边界

列与宽度模型

TableViewColumn是未密封(unsealed)的DependencyObjectWidth携带GridLength意图,ActualWidth是解析后的像素值(MinWidth/MaxWidth夹取,见 TableView.idl):

  • Pixel:固定像素;
  • Auto:等于已实现单元格中最宽者(在数据集内增长);
  • Star:在扣除固定列后按比例瓜分视口宽度。

列级属性还包括:MinWidth(默认 20)、MaxWidth(默认无穷)、CanResize(默认 true)、FrozenEdgeNone/Leading/Trailing,Leading 已实现,Trailing 预留)、Visibility(隐藏列)、ActualWidth(只读)。表格级开关CanUserResizeColumns是所有列的“总闸”,列自身可用CanResize单独退出。

外观与行为

  • HeadersVisibility:枚举为[flags],当前只定义None/Column(行头不在范围内,故无 Row/All,与 WPFDataGridHeadersVisibility的 Column 槽位对应);
  • GridLinesVisibilityAll/Horizontal/None/Vertical
  • DensityCompact/Standard/Comfortable,决定行高与内置单元格/列头内边距资源;
  • RowBackground/AlternatingRowBackground:null 时保留主题行背景(行斑马纹);
  • EmptyTemplateItemsSource为空时显示的空状态模板;
  • HeaderTemplate/HeaderTemplateSelector/HeaderToolTip:列头定制(HeaderTemplateSelector优先级更高);
  • 列内单元格:TableViewTextColumn(绑定文本)、TableViewTemplateColumnCellTemplate自定义内容)、CellToolTipBinding(按行的 ToolTip 绑定)、CellEditingTemplate(编辑态模板)。

编辑

编辑是单单元格范围默认关闭:表格级IsReadOnly默认 true;为某个单元格启用编辑需同时把表格与列设为非只读、并给列提供CellEditingTemplate或内置编辑器。IsEditing反映是否有编辑正在进行;CommitEdit()/CancelEdit()返回 false 表示被否决或校验失败、编辑保持打开。事件BeginningEdit(可Cancel)、CellEditEnding(携带EditAction.Commit/Cancel,可Cancel)。编辑由用户动作触发:双击或当前单元格按 F2;本版本没有编程式BeginEdit,也没有行级编辑事务。

选择

默认SelectionMode = Single(与 ItemsView、ListView、WPF DataGrid 一致);None为纯展示。SelectedItem/SelectedIndex是选择状态的只读投影,保持一致;通过Select(index)/Deselect(index)/IsSelected(index)/DeselectAll()驱动。示例的 SelectionPage.xaml.cs 专门演示了两类易错行为:选中跟随数据项(插入不触发SelectionChanged,仅SelectedIndex移位);删除被选中项是清除而非滑向邻居。

数据整形:TableViewSource 的过滤、排序与分组

TableViewSource是本控件的核心数据整形入口(完整契约见 TableViewSource.idl)。关键设计:Filter、GroupBy、Sort 是同一个源上的独立“阶段”,而非三份独立集合,因此三者可自由组合,且 reshape 保持行身份——选择与焦点按对象身份重新锚定,而不是按索引(ShapingPage.xaml.cs)。

创建源

_source = TableViewSource.From(_items); // _items 为 ObservableCollection<Item> Table.ItemsSource = _source;

From接受与ItemsSourceView相同的集合接口(IVector<Object>/IObservableVector<Object>IBindableVectorIIterable<Object>IBindableIterable),返回时投影已填充完毕。注意TableViewSource密封(sealed):具体实现由 TableView 驱动,密封可防止子类绕过整形动词;将来解封是兼容的,反向则不兼容。

过滤

_source.Filter(new TableViewPredicate(item => Matches((Item)item, text, highOnly))); // 移除过滤时用 ClearFilter(),不要传“全通过”的谓词 _source.ClearFilter();
  • 谓词或 items 为 null 时抛E_INVALIDARG(与 TableView 命令面“坏输入静默忽略”不同);
  • UI 线程亲和:底层集合必须在 UI 线程上发出变更通知,动词内部不持锁;
  • ClearFilter()移除过滤意味着“阶段被删除”,而不是对每个条目再跑一遍恒真谓词。

分组

// 值类型键(String/Int32/Int64/Guid/Boolean/enum)走内置组身份,无需 identity selector _source.GroupBy(new TableViewKeySelector(item => groupKey((Item)item))); // 移除分组 _source.ClearGroupBy();

GroupByWithIdentity重载可选地接受TableViewIdentitySelector;引用类型分组键若无 selector,会在投影期快速失败。示例用RoleCityScoreBand(score)(80-100 / 50-79 / 0-49 三档)演示按值类型键分组。分组的只读投影是TableViewGroupInfoKey/ItemCount/Level/IsExpandable/IsExpanded/KeyText/ItemCountText,镜像ICollectionViewGroup),它是GroupHeaderTemplate的绑定源,原地更新并引发PropertyChanged,因此回收不会重算模板中的每个绑定。

排序:两种途径,一种“最后写入者胜出”规则

途径一:TableViewSource 层(数据层动词)

// 按属性路径排序(推荐,显示路径通常即排序路径) _source.Sort(nameof(Item.Name), SortDirection.Ascending); // 或按键选择器排序(计算/多字段/规范化键),该轴是“匿名”的,无法点亮列头的排序指示 _source.Sort(new TableViewKeySelector(item => item.Name), SortDirection.Ascending); _source.ClearSort();
  • 路径形式由与列SortMemberPath相同的绑定求值器求值,支持点路径与索引器;绑定到列的 TableView 可以把路径归因到列并点亮其排序指示;
  • 多轴排序时第一个声明的轴为主排序,后续轴在前一轴内打破平局(对齐 WPFSortDescriptions顺序);
  • SortDirection.None会移除该键的轴而非播种新阶段。

途径二:TableView 控件层(命令面)

Table.SortByColumn(column, direction); // 先清掉其他排序状态 Table.ToggleSortDirection(column); // 按列的 SortCycle 循环 Table.ClearSort(); // 一次清空所有列 Table.CanUserSortColumns = true; // 列头点击排序总开关,默认 true
  • Sorting(排序应用前,Cancel=true可交由应用自持排序)与Sorted(应用后,不可取消);
  • 控件排序与源排序互相替换而非叠加:任意时刻只有一个排序轴生效,最后写入者胜出。源码注释特别指出:控件的排序会“替换”之前的一切,而源的Sort是“组合式”的,只有相同 token 的轴才被原位替换(ShapingPage.xaml.cs)。因此示例每次只声明单轴排序,先ClearSort()再排序;
  • 列级SortCycleAscendingDescending/AscendingDescendingNone/DescendingAscending/DescendingAscendingNone)决定列头重复点击时循环的方向序列——*None变体提供第三次点击回到未排序状态的步骤。SortCycle只约束列头点击,对编程式SortByColumn或源上的排序没有话语权。示例中 Score 列特意以DescendingAscendingNone开局(指标列最值得看的是最大值,见 TableView.idl);
  • 未显式设置时,TableViewTextColumn的排序键回退到Binding.Path.Path,因此列头点击排序无需配置SortMemberPath

组展开与组头模板

Table.ExpandAllGroups(); // 批量展开 Table.CollapseAllGroups(); // 批量折叠 Table.GroupHeaderTemplate = (DataTemplate)Resources["GroupHeader"]; // null 回退内置 KeyText/ItemCountText 头

GroupHeaderTemplate为 null 时使用控件内置模板(KeyText/ItemCountText延迟计算,模板只绑Key/ItemCount时零开销)。示例还通过KeyboardAccelerator触发批量折叠/展开——因为按钮点击会先抢走焦点,破坏控件“批量命令后恢复焦点”的真实场景(ShapingPage.xaml.cs)。

示例中的模板实战:从 App.xaml 看高级单元格

App.xaml 集中展示了本控件支持的内容模板形态,可直接复用为自研表格的模板素材:

  • ScoreCell:ProgressBar 绑ScoreTableViewTemplateColumn.CellTemplate);
  • StarHeader:图标+文本的列头模板(Column.HeaderTemplate);
  • EmptyState:空状态模板(EmptyTemplate);
  • BioCellTextWrapping="Wrap"的变高行文本(依赖 RebuildCells 重入修复后可变行高);
  • JoinedCellDatePicker双向编辑;
  • NotesCell/NotesEditCell:显示 TextBlock + 编辑 TextBox 分离的“真编辑列”——编辑器只在编辑期换入,且编辑模板使用经典{Binding}而非{x:Bind},因为提交逻辑经GetBindingExpression只能看到{Binding}
  • AutoEditCell:不固定宽度的多行 TextBox,输入更长的行会让 Auto列宽增长、增加行数会让 Auto行高增长,两个方向都可实时验证;
  • ExpandNameCell等:每个单元格内嵌Expander(折叠/展开改变单元格期望尺寸,Auto 列随之增长、Star 列换行/裁切);
  • GrowWidthCell:拖动 Slider 改变内部条形宽度,观察 Auto 列实时重排(Star/Pixel 列则裁切到固定列宽)。

常见失败速查表

症状根因处置
CS0234: ... 'Tabular' does not exist包早于 Tabular 的公开 winmd,未用本地打包版本执行.\Build.cmd product+.\Build.cmd samples,或打包后用/p:WinUIVersion指定匹配版本
XamlParseException 0x802B000A(首帧布局),数秒后0xC000027Bstowed exceptionApp.xaml未合并TabularControlsResources,列头样式解析不到SortIndicatorForeground合并TabularControlsResources字典
表格出现但没有列头行、没有单元格几乎总是未声明列,而非字典缺失声明至少一个TableViewTextColumn/TableViewTemplateColumn
重新pack.component.cmd后控件改动未生效NuGet 按版本缓存换新版本号,或删除packages\microsoft.windowsappsdk.winui\

入口与维护备注

  • 入口由 Program.cs 提供Main,项目定义了DISABLE_XAML_GENERATED_MAIN,遵循 DisableXamlGeneratedMain 示例——这是常规 WinUI 模式而非变通方案;
  • 项目文件仅约 60 行(TableViewSampleApp.csproj),是普通包消费者。维护者若再次遇到需要“手工接线”的场景,说明产品可能回归,应按 AGENTS.md 中的顺序依次核查:① Tabular 类型在公开合并 winmd(MergedWinMD)中未被剔除;②<ActivatableClass>注册随合并输入生成;③ 控件主题 XBF 随包发布、默认样式 URI 为无权威的ms-appx:///,与AppxPriInitialPath一致。

延伸阅读

  • 构建与维护备注:Samples/TableViewSampleApp/AGENTS.md
  • 控件完整 API 契约:controls/dev/TableView/TableView.idl
  • 数据整形契约:controls/dev/TableView/TableViewSource.idl
  • 同模式独立控件集示例:Samples/ChartApp
  • 自定义入口模式:Samples/DisableXamlGeneratedMain

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 13:33:28

system-design-notes 第14章:设计YouTube视频平台完整指南

system-design-notes 第14章&#xff1a;设计YouTube视频平台完整指南 【免费下载链接】system-design-notes Notes of the book System Desgin Interview - An Insiders Guide 项目地址: https://gitcode.com/GitHub_Trending/sy/system-design-notes system-design-no…

作者头像 李华
网站建设 2026/9/16 13:30:02

2026专业的日语网课机构推荐排行榜

一、前言国内日语线上学习市场不断拓展&#xff0c;已经进入“选哪种模式”的阶段。机构之间的差异&#xff0c;不再只是教材和价格的差别&#xff0c;而是各自锚定了不同人群与目标&#xff1a;有的用多年有效期和小班直播对抗惰性&#xff0c;有的以低预算录播换取学习自由度…

作者头像 李华
网站建设 2026/9/16 13:26:49

MATLAB音频信号去噪实战:从WAV读取到小波与LMS对比

简介&#xff1a;一份围绕MATLAB音频信号处理的入门实践资源&#xff0c;聚焦频谱分析与噪声去除两个核心环节&#xff0c;适合信号处理初学者、音频算法工程师及课程设计者使用。压缩包内共有2个文件&#xff1a;一个.m脚本负责读取WAV音频文件并执行频谱分析&#xff0c;一个…

作者头像 李华
网站建设 2026/9/16 13:26:13

图优化在SLAM中的基本思想与应用

引言 在机器人软件开发的核心领域,SLAM(Simultaneous Localization and Mapping)技术扮演着至关重要的角色。它让机器人能够在未知环境中实时定位自身位置,并构建环境地图。而图优化的方法,作为SLAM的主流技术之一,以其高效和精度赢得了广泛运用。本文将深入探讨图优化的…

作者头像 李华