.NET MAUI Windows平台代码创建DataTemplate异常排查
问题
我正尝试为.NET MAUI的CollectionView创建DataTemplate,该实现在Android模拟器上运行正常,但在Windows平台出现问题。
实现步骤:创建Border作为DataTemplate基础,通过代码生成DataTemplate并绑定到CollectionView的ItemTemplate,所有代码位于页面构造函数的try/catch块中,但未触发异常捕获。
MainPage.cs 代码:
AbsoluteLayout mainLayout = new AbsoluteLayout().LoadFromXaml(xaml); CollectionView collectionViewTest = mainLayout.FindByName<CollectionView>("CollectionViewTest"); Border collectionViewBorderTemplate = new CollectionViewTestBorder(); TapGestureRecognizer tapGesture = new(); tapGesture.Tapped += TapGesture_Tapped; collectionViewBorderTemplate.GestureRecognizers.Add(tapGesture); DataTemplate dataTemplate = new() { LoadTemplate = () => collectionViewBorderTemplate }; collectionViewTest.ItemTemplate = dataTemplate; ObservableCollection<User> users = new() { new User { Name = "Joe", Email = "test@mail.com", Age = 40 }, new User { Name = "Jane", Email = "test1@mail.com", Age = 50 }, new User { Name = "James", Email = "test2@mail.com", Age = 35 } }; collectionViewTest.ItemsSource = users; BindingContext = vm;
CollectionViewTestBorder.xaml 代码:
<?xml version="1.0" encoding="utf-8" ?> <Border xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DesignADistance.Components.CollectionViewTestBorder" xmlns:models="clr-namespace:DesignADistance.Models" x:DataType="models:User"> <Grid RowDefinitions="100,100,100"> <Grid.Resources> <ResourceDictionary> <Style TargetType="Label"> <Setter Property="TextColor" Value="PowderBlue"/> <Setter Property="HorizontalTextAlignment" Value="Center"/> <Setter Property="VerticalTextAlignment" Value="Center"/> </Style> </ResourceDictionary> </Grid.Resources> <Label Grid.Row="0" Text="{Binding Name}"/> <Label Grid.Row="1" Text="{Binding Age}"/> <Label Grid.Row="2" Text="{Binding Email}"/> </Grid> </Border>
异常信息:
在Windows运行时,App.g.i.cs中触发空引用异常,异常堆栈信息:
at Microsoft.Maui.Controls.Platform.ItemContentControl.MeasureOverride(Size availableSize) at Microsoft.UI.Xaml.FrameworkElement.Microsoft.UI.Xaml.IFrameworkElementOverrides.MeasureOverride(Size availableSize) at ABI.Microsoft.UI.Xaml.IFrameworkElementOverrides.Do_Abi_MeasureOverride_0(IntPtr thisPtr, Size availableSize, Size* result)
异常消息:
Object reference not set to an instance of an object.
推测是资源字典初始化异常,请问在Windows平台需要如何调整实现以解决该问题?
解决方案
核心问题原因
当前实现的致命问题是:DataTemplate的LoadTemplate委托返回的是同一个CollectionViewTestBorder实例。CollectionView会为每个Item复用这个单一实例,Android平台可能兼容这种错误用法,但Windows布局系统对控件实例的复用逻辑更严格,会导致控件资源(如ResourceDictionary中的Style)初始化异常,最终触发空引用。
调整步骤
修改DataTemplate创建逻辑
不要预先创建CollectionViewTestBorder实例,而是在LoadTemplate委托中每次新建实例,确保每个CollectionView Item拥有独立的控件实例:DataTemplate dataTemplate = new() { LoadTemplate = () => { var border = new CollectionViewTestBorder(); TapGestureRecognizer tapGesture = new(); tapGesture.Tapped += TapGesture_Tapped; border.GestureRecognizers.Add(tapGesture); return border; } };可选:将GestureRecognizer移入XAML
为简化代码,可直接在CollectionViewTestBorder.xaml中添加TapGestureRecognizer,避免在代码中重复创建:<Border xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DesignADistance.Components.CollectionViewTestBorder" xmlns:models="clr-namespace:DesignADistance.Models" x:DataType="models:User"> <Border.GestureRecognizers> <TapGestureRecognizer Tapped="TapGesture_Tapped"/> </Border.GestureRecognizers> <!-- 原Grid内容保持不变 --> </Border>然后在
CollectionViewTestBorder.xaml.cs中实现TapGesture_Tapped方法,代码更清晰,也能避免跨平台实例复用问题。验证资源字典加载
Windows平台对XAML资源字典的加载时机有要求,通过每次新建实例的方式,能确保每个控件实例都完整初始化XAML中的资源字典和绑定逻辑。
为什么try/catch未捕获异常?
异常是在UI布局线程的后续渲染阶段触发的,并非在构造函数代码执行阶段,所以构造函数中的try/catch无法捕获。可通过全局异常捕获机制(如App.xaml.cs中的UnhandledException事件)捕获这类UI线程异常。
内容的提问来源于stack exchange,提问作者0fail

