简介Xamarin.Forms.DataGrid 是面向 Xamarin.Forms 跨平台开发者的数据网格组件源码包帮助在 iOS、Android 与 UWP 上快速构建类似电子表格的数据展示界面。它扩展了 Xamarin.Forms 原生控件能力支持多列定义、排序、筛选、分页与自定义列模板适合需要处理大量结构化数据、追求一次编写多端运行的中级 C# 开发者。资源包共 173 个文件以 58 个 cs 源码、84 个 png 截图、4 个 xaml 布局及 6 个 csproj 工程文件为主另含 sln 解决方案、md 说明与 plist、appxmanifest 等平台配置整体约 4.92MB可直接通过 NuGet 引入或导入源码集成。目前已有 505 人学习下载。借助其中的 DataGridColumn、DataGridTemplateColumn 示例与排序筛选测试用例读者可掌握列定义、ItemsSource 数据绑定、SortMode 与 FilterMode 配置及分页实现并参考虚拟化与触摸事件处理思路优化大数据量滚动性能快速搭建功能完整的数据驱动应用。1. Xamarin.Forms.DataGrid 到底解决什么问题从 ListView 手搓表格的痛说起如果你用 Xamarin.Forms 做过企业级 App大概率遇到过这种需求后端返回一个 List 要求以表格形式展示带表头、支持排序、能隐藏某些列、行高还得自适应。第一反应是用 ListView 加 DataTemplate 拼结果发现表头要单独写一个 Grid列宽对不齐滚动时表头跟着跑排序要自己写手势隐藏列更是要动态改模板——一套下来三百行代码维护起来就是血泪史。Xamarin.Forms.DataGrid 这个控件就是冲着这个场景来的它把表格的列定义、排序、行高、选中态、隐藏列这些能力封装成一个可绑定 ItemsSource 的 DataGrid让你在 XAML 里几行配置就能跑起来。它适合谁适合正在用 Xamarin.Forms 做跨平台业务 App、需要展示结构化数据但不想引入第三方商业控件的 C# 开发者。热词里提到的「datagrid隐藏列」「wpf datagrid一行变为两行显示」其实反映了同一个诉求表格的列和行要能按业务动态控制而不是写死在 XAML 里。这个方向值不值得投入如果你团队已经在 Xamarin.Forms 技术栈上答案是值得因为它省掉的是你反复手搓表格的重复劳动。2. 把 DataGrid 跑起来从零到第一个可排序表格2.1 安装与初始化NuGet 包和渲染器注册Xamarin.Forms.DataGrid 在 NuGet 上的包名是Xamarin.Forms.DataGrid安装到你的 Forms 主项目和三个平台项目Android、iOS、UWP。安装完成后必须在各平台做渲染器注册否则运行时只会看到一个空白区域——这是最常见的翻车点。Android 项目在MainActivity.cs的OnCreate里加// Android 平台渲染器注册必须在 LoadApplication 之前调用 Xamarin.Forms.DataGrid.Droid.DataGridRenderer.Init();iOS 项目在AppDelegate.cs的FinishedLaunching里加// iOS 平台渲染器注册 Xamarin.Forms.DataGrid.iOS.DataGridRenderer.Init();UWP 项目在App.xaml.cs的OnLaunched里加// UWP 平台渲染器注册 Xamarin.Forms.DataGrid.UWP.DataGridRenderer.Init();这三行代码的逻辑很简单DataGrid 是自定义控件每个平台需要把 Forms 层的抽象映射到原生 View。参数方面没有可调的位置必须在LoadApplication之前否则初始化顺序错乱会导致渲染器找不到。如果你忘了注册App 不会崩但 DataGrid 区域是空的日志里也不会有明显报错排查起来很玄学。2.2 XAML 里定义列DataGridColumn 的绑定与宽度策略初始化完成后在页面 XAML 里引入命名空间并声明 DataGrid。下面是一个订单列表的最小可用示例!-- 引入 DataGrid 命名空间 -- ContentPage xmlns:dgclr-namespace:Xamarin.Forms.DataGrid;assemblyXamarin.Forms.DataGrid dg:DataGrid x:NameOrderGrid ItemsSource{Binding Orders} SelectedItem{Binding SelectedOrder} RowHeight48 HeaderHeight44 BorderColor#E0E0E0 HeaderBackground#F5F5F5 dg:DataGrid.Columns !-- 每列绑定一个属性Width 支持绝对值、星号和自动 -- dg:DataGridColumn Title订单号 PropertyNameOrderNo Width120 / dg:DataGridColumn Title客户 PropertyNameCustomerName Width* / dg:DataGridColumn Title金额 PropertyNameAmount Width100 / dg:DataGridColumn Title状态 PropertyNameStatus Width80 / /dg:DataGrid.Columns /dg:DataGrid /ContentPage这里的关键参数是PropertyName它对应 ViewModel 里集合元素的属性名必须完全一致大小写敏感。Width支持三种写法固定值如120、星号*表示按比例填充剩余空间、Auto表示按内容自适应。我一般会把客户名这类长度不定的列设为*金额和状态用固定宽度避免列宽跳动。RowHeight和HeaderHeight是全局的不能按行单独设——这是和 WPF DataGrid 的一个明显差异后面避坑章节会展开。2.3 排序与选中内置能力和绑定写法DataGrid 内置了点击表头排序的能力但需要你在 ViewModel 里把数据源准备成可排序的集合。最省事的做法是用ObservableCollectionT配合 LINQ 在排序事件里重新赋值但更推荐直接用SortingEnabledTrue让控件自己处理。dg:DataGrid SortingEnabledTrue ItemsSource{Binding Orders} SelectedItem{Binding SelectedOrder, ModeTwoWay}SortingEnabled默认是False设为True后点击表头会按该列PropertyName对应的属性做升序/降序切换。注意它只对实现了IComparable的类型有效如果你绑定的属性是自定义对象排序会静默失败。SelectedItem用TwoWay绑定选中行变化时 ViewModel 能收到通知适合做「点击行跳详情」的场景。参数上SelectedItem的类型必须和ItemsSource的元素类型一致否则绑定不生效且不报错。3. 隐藏列与动态列业务里真正高频的需求3.1 用 IsVisible 控制列显示绑定到 ViewModel 属性热词里「datagrid隐藏列」是真实痛点。Xamarin.Forms.DataGrid 的DataGridColumn没有直接的Visibility属性但可以通过IsVisible绑定一个布尔值来实现。下面示例根据用户角色隐藏「成本」列dg:DataGridColumn Title成本 PropertyNameCost IsVisible{Binding ShowCostColumn} Width100 /ViewModel 里// 根据当前用户角色决定是否显示成本列 private bool _showCostColumn; public bool ShowCostColumn { get _showCostColumn; set { _showCostColumn value; OnPropertyChanged(); } } // 在构造函数或登录后设置 ShowCostColumn CurrentUser.Role Admin;逻辑说明IsVisible为false时该列不渲染剩余列自动重新分配宽度。参数上要注意IsVisible绑定的是 ViewModel 的属性不是 DataGrid 自身的属性所以BindingContext必须正确设置。如果隐藏后列宽出现异常检查是否有列设了*宽度——隐藏列后星号列会重新计算通常没问题但极端情况下需要手动刷新ItemsSource。3.2 运行时动态增删列代码后置操作 Columns 集合有些场景列不是固定的比如用户自定义报表字段。这时需要在代码后置里操作Columns集合// 根据配置动态添加列 public void BuildColumns(ListColumnConfig configs) { OrderGrid.Columns.Clear(); foreach (var cfg in configs) { OrderGrid.Columns.Add(new DataGridColumn { Title cfg.DisplayName, PropertyName cfg.FieldName, Width cfg.Width 0 ? new GridLength(cfg.Width) : GridLength.Star }); } }逻辑说明Columns是ObservableCollectionDataGridColumn支持运行时增删。参数上Width的类型是GridLength固定值用new GridLength(120)星号用GridLength.Star自动用GridLength.Auto。注意每次Clear再Add会触发重绘如果列很多超过 20 列会有明显卡顿建议批量构建完再一次性赋值。另外动态列的PropertyName必须和 ItemsSource 元素的属性对得上否则该列显示空白。3.3 行高自适应与「一行变两行」的替代方案热词里「wpf datagrid一行变为两行显示」在 Xamarin.Forms.DataGrid 里没有直接对应能力因为它的RowHeight是全局固定值。但如果你需要某列内容换行显示可以用DataGridColumn的Template自定义单元格dg:DataGridColumn Title备注 PropertyNameRemark Width* dg:DataGridColumn.Template DataTemplate !-- Label 设置 LineBreakMode 实现多行 -- Label Text{Binding Remark} LineBreakModeWordWrap VerticalOptionsCenter Margin4,2 / /DataTemplate /dg:DataGridColumn.Template /dg:DataGridColumn逻辑说明Template允许你完全接管单元格渲染LineBreakModeWordWrap让长文本自动换行。但要注意行高仍然是RowHeight设定的固定值换行后内容可能被裁剪。解决办法是把RowHeight设大一些比如 72或者接受这个限制——这是该控件目前的边界不是配置能绕过的。参数上Template里的绑定上下文是当前行数据对象直接写属性名即可。4. 避坑与排查那些文档不会告诉你的翻车现场4.1 现象DataGrid 显示空白无报错原因最常见的是忘了在平台项目注册渲染器其次是ItemsSource绑定失败。排查步骤先确认三个平台的Init()都调了再检查BindingContext是否设置最后看ItemsSource绑定的集合是否为null。解决按 2.1 节补注册在 ViewModel 构造函数里初始化空集合Orders new ObservableCollectionOrder()避免绑定到 null。4.2 现象点击表头排序无效原因SortingEnabled没设为True或者绑定的属性类型没实现IComparable。排查检查 XAML 里SortingEnabledTrue是否存在如果是自定义类型确认它实现了IComparableT。解决对简单类型string、int、DateTime直接可用自定义类型要么实现接口要么在 ViewModel 里手动处理排序逻辑并重新赋值ItemsSource。4.3 现象隐藏列后剩余列宽错乱原因隐藏的列如果设了固定宽度剩余空间重新分配时星号列会扩张但有时不会立即刷新。排查观察是否只有星号列受影响。解决在IsVisible变更后手动调用OrderGrid.Columns的刷新或者把ItemsSource重新赋值一次触发重绘。更稳妥的做法是隐藏列时同时把它的Width设为 0显示时再恢复。4.4 现象Android 上滚动卡顿列表超过 200 行明显掉帧原因DataGrid 默认没有做虚拟化优化所有行一次性渲染。排查用 Android Profiler 看渲染线程。解决确保ItemsSource是ObservableCollection而不是List避免全量重绘如果数据量确实大考虑分页加载每次只绑定 50 条。这是该控件的性能边界不是配置能解决的。4.5 现象选中行后SelectedItem为 null原因SelectedItem绑定的属性类型和ItemsSource元素类型不一致或者绑定模式不是TwoWay。排查检查 ViewModel 里SelectedOrder的类型是否为Order。解决类型对齐绑定加ModeTwoWay。另外注意如果ItemsSource重新赋值SelectedItem会被重置为 null需要在赋值后手动恢复选中项。5. 进阶技巧用样式和模板把 DataGrid 打磨到生产级5.1 统一单元格样式Style 与资源字典生产环境里表格不能是默认灰底黑字需要统一字体、颜色、对齐。DataGrid 支持通过ColumnHeaderStyle和CellStyle注入样式ContentPage.Resources Style x:KeyHeaderStyle TargetTypeLabel Setter PropertyFontSize Value14 / Setter PropertyFontAttributes ValueBold / Setter PropertyTextColor Value#333333 / Setter PropertyVerticalOptions ValueCenter / /Style Style x:KeyCellStyle TargetTypeLabel Setter PropertyFontSize Value13 / Setter PropertyTextColor Value#666666 / Setter PropertyVerticalOptions ValueCenter / /Style /ContentPage.Resources dg:DataGrid ColumnHeaderStyle{StaticResource HeaderStyle} CellStyle{StaticResource CellStyle} ... /逻辑说明ColumnHeaderStyle作用于表头 LabelCellStyle作用于默认单元格 Label。参数上样式里能设的属性取决于 Label 支持的属性TextColor、FontSize、HorizontalTextAlignment都可用。注意如果你用了自定义TemplateCellStyle不会作用于模板内的控件需要在模板里单独设样式。5.2 条件格式化根据数值改变单元格颜色业务表格经常需要「金额为负显示红色」这类条件格式。用Template配合 Converter 实现// 值转换器负数返回红色正数返回默认色 public class AmountColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is decimal amount amount 0) return Color.Red; return Color.FromHex(#666666); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) throw new NotImplementedException(); }XAML 里dg:DataGridColumn Title金额 PropertyNameAmount Width100 dg:DataGridColumn.Template DataTemplate Label Text{Binding Amount, StringFormat{0:C}} TextColor{Binding Amount, Converter{StaticResource AmountColorConverter}} VerticalOptionsCenter / /DataTemplate /dg:DataGridColumn.Template /dg:DataGridColumn逻辑说明Converter 接收Amount值返回对应颜色。参数上StringFormat{0:C}把数值格式化为货币Converter在App.xaml或页面资源里注册。注意 Converter 每次单元格渲染都会调用逻辑要轻量别在里面做数据库查询。5.3 验证清单上线前必查的 5 个点检查项验证方法通过标准渲染器注册三平台各跑一次DataGrid 区域有内容空数据状态ItemsSource 设为空集合不崩溃显示空白表格大数据量绑定 500 条记录滚动无明显卡顿隐藏列切换运行时改 IsVisible列宽正常重算选中态恢复重新赋值 ItemsSourceSelectedItem 按预期处理这套方案我从 2019 年用到现在最大的教训是别指望 DataGrid 能覆盖 WPF 那种级别的表格能力它的定位是「够用的跨平台表格」。遇到它做不到的比如行内编辑、合并单元格早点换自定义渲染或者第三方商业控件别硬改源码——我改过一次升级 Forms 版本时全部重来。希望帮到你。本文还有配套的精品资源点击获取