XAML中绑定ObservableCollection内嵌List报指定强制转换无效异常
异常成因
这个强制转换错误和你的Model类定义无关,是Xamarin.Forms嵌套虚拟化列表控件的已知问题:
- 从绑定逻辑看,
CarouselView每个子项的绑定上下文确实是ObservableCollection<List<Model>>里的单个List<Model>,{Binding .}的写法语法上没有问题。 - 触发异常的核心原因是:iOS平台下
ListView开启虚拟化复用单元格时,会错误复用外层CarouselView的模板创建逻辑TemplatedItemsList.ActivateContent,这个方法预期拿到的是CarouselView的项容器类型,实际传入的是内层ListView的Model实例,类型不匹配直接抛出强制转换无效异常。 - 如果你没有给内层
ListView显式声明ItemTemplate,或者两个可滚动控件没有明确的布局尺寸约束,这个bug的触发概率会接近100%。
解决方案
按优先级从高到低尝试,一般第一个方案就能解决问题:
显式声明内层ListView的ItemTemplate,指定缓存策略
不要让Xamarin.Forms自动推断内层列表的单元格模板,同时显式设置ListView的虚拟化缓存策略为RecycleElement,避免上下文错乱,示例代码:<CarouselView ItemsSource="{Binding Data}"> <CarouselView.ItemTemplate> <DataTemplate> <StackLayout> <!-- 保留你原有其他布局内容 --> <ListView ItemsSource="{Binding .}" CachingStrategy="RecycleElement"> <ListView.ItemTemplate> <DataTemplate> <!-- 替换成你实际需要的Model单元格布局,示例如下 --> <ViewCell> <Label Text="{Binding 你的Model中string类型属性名}" /> </ViewCell> </DataTemplate> </ListView.ItemTemplate> </ListView> </StackLayout> </DataTemplate> </CarouselView.ItemTemplate> </CarouselView>注意:包裹ListView的StackLayout一定要设置明确的高度/宽度约束,不要让两个可滚动控件在布局阶段争抢尺寸,否则iOS渲染层仍可能出现上下文错乱。
替换
{Binding .}写法规避旧版本绑定bug
部分4.8之前的Xamarin.Forms版本对嵌套场景下的.绑定支持有缺陷,可以把绑定写法改成{Binding},或者在外层容器上先捕获当前绑定上下文再传递给ListView:<DataTemplate> <StackLayout x:Name="ItemContainer"> <ListView ItemsSource="{Binding BindingContext, Source={x:Reference ItemContainer}}" CachingStrategy="RecycleElement"> <!-- 显式ItemTemplate内容同上 --> </ListView> </StackLayout> </DataTemplate>升级Xamarin.Forms版本
如果当前项目使用的Xamarin.Forms版本低于4.8.0.1821,直接升级到4.8分支的最新稳定版,这个嵌套列表的类型转换bug在该版本之后已经修复了绝大多数触发场景。
内容的提问来源于stack exchange,提问作者philipp8230
相关产品推荐
相关产品推荐

