无法使用mvvm:ViewModelLocator创建XAML视图数组的问题排查
首先来说你遇到的这个XamlParseException错误——找不到Main引用的原因:
XAML的解析是按从上到下的顺序进行的,当解析<x:Array>里的<views:View1>和<views:View2>时,你的ContentPage(也就是x:Name="Main"的那个实例)还没完成初始化,所以此时无法找到名为Main的对象引用,自然就抛出了这个异常。
快速修复当前代码
你可以把AutowirePartialView的引用方式改成相对源绑定,不用依赖x:Name的初始化时机,修改后的代码如下:
<ContentPage x:Class="MainView" xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" xmlns:views="clr-namespace:Views"> <CarouselView> <CarouselView.ItemsSource> <x:Array Type="{x:Type View}"> <views:View1 mvvm:ViewModelLocator.AutowirePartialView="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}}" /> <views:View2 mvvm:ViewModelLocator.AutowirePartialView="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}}" /> </x:Array> </CarouselView.ItemsSource> <CarouselView.ItemTemplate> <DataTemplate> <ContentView Content="{Binding .}" /> </DataTemplate> </CarouselView.ItemTemplate> </CarouselView> </ContentPage>
这样通过RelativeSource向上查找ContentPage类型的父元素,就能避开初始化顺序的问题,正常完成视图的自动绑定。
用MainViewModel创建视图集合(更符合MVVM模式)
当然可以!而且这种方式更贴合MVVM的设计思想,把视图集合的创建逻辑放到ViewModel里,让页面只负责渲染。具体步骤如下:
1. 在MainViewModel中定义视图集合
using Prism.Mvvm; using System.Collections.ObjectModel; using Xamarin.Forms; using Views; public class MainViewModel : BindableBase { private ObservableCollection<View> _carouselViews; public ObservableCollection<View> CarouselViews { get => _carouselViews; set => SetProperty(ref _carouselViews, value); } public MainViewModel() { // 初始化视图集合 CarouselViews = new ObservableCollection<View> { new View1(), new View2() }; } }
2. 修改XAML绑定ItemsSource
把原来的<x:Array>替换成绑定到ViewModel的集合,同时注意保持Autowire的配置:
<ContentPage x:Class="MainView" xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" xmlns:views="clr-namespace:Views" mvvm:ViewModelLocator.AutowireViewModel="True"> <CarouselView ItemsSource="{Binding CarouselViews}"> <CarouselView.ItemTemplate> <DataTemplate> <ContentView Content="{Binding .}" mvvm:ViewModelLocator.AutowirePartialView="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}}" /> </DataTemplate> </CarouselView.ItemTemplate> </CarouselView> </ContentPage>
这里把AutowirePartialView移到了ItemTemplate里的ContentView上,这样集合里的每个视图都会自动关联到主页面的ViewModel。
如果想更纯粹的MVVM,还可以在ViewModel中存放子视图的ViewModel集合,然后在ItemTemplate里用DataTemplateSelector根据不同的ViewModel类型渲染对应的视图,这种方式会进一步分离视图和业务逻辑,完全符合MVVM的关注点分离原则。
内容的提问来源于stack exchange,提问作者Esteban Chornet

