如何在具有棱镜的 Xamarin 表单中为内容视图创建单独的视图模型?
How to create a separate view model for a contentview in Xamarin forms having prism?
问题陈述
我想创建一个 contentview 用户控件,它有自己的视图模型,可以在多个内容页面中使用。
以下实施中的问题
我已经扩展了我的 App.xaml.cs 如下所述。但是一旦导航从具有 contentview 用户控件的内容页面开始工作,但如果我再次导航到该页面,导航将不起作用。补充一下,view.Parent 在下面的代码中也显示为 null。
请帮忙。
using OEP.Views;
using Prism;
using Prism.Common;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Unity;
using Unity.Resolution;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace OEP
{
public partial class App : PrismApplication
{
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
//await NavigationService.NavigateAsync("NewOrderPage");
await NavigationService.NavigateAsync("LoginPage");
//await NavigationService.NavigateAsync("HomePage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<LoginPage>();
containerRegistry.RegisterForNavigation<ForgotPasswordPage>();
containerRegistry.RegisterForNavigation<HomePage>();
containerRegistry.RegisterForNavigation<CustomerDetailsPage>();
containerRegistry.RegisterForNavigation<NewOrderPage>();
//Container.Resolve<HomePageCustomersUserControl>("Customers");
//containerRegistry.Register<HomePageCustomersUserControl, HomePageCustomersUserControlViewModel>();
//ViewModelLocationProvider.Register<HomePageCustomersUserControl>(() => Container.Resolve<HomePageCustomersUserControlViewModel>());
}
protected override void ConfigureViewModelLocator()
{
ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) =>
{
Page page = null;
switch (view)
{
case Page page1:
page = page1;
break;
case Element customView:
page = GetPageFromElement(customView);
// Existing parameter with the Page
break;
}
var navService = CreateNavigationService(page);
ParameterOverrides overrides = new ParameterOverrides
{
{ "navigationService", navService }
};
return Container.GetContainer().Resolve(type, type.GetType().Name, overrides);
});
}
// Currently exists
protected INavigationService CreateNavigationService(Page page)
{
var navigationService = NavigationService;
((IPageAware)navigationService).Page = page;
return navigationService;
}
protected INavigationService CreateNavigationService(object view)
{
switch (view)
{
case Page page:
return CreateNavigationService(page);
case Element element:
var parentPage = GetPageFromElement(element);
if (parentPage == null)
{
return null;
}
return CreateNavigationService(parentPage);
default:
return null;
}
}
private Page GetPageFromElement(Element view)
{
switch (view.Parent)
{
case Page page:
return page;
case null:
return null;
default:
return GetPageFromElement(view.Parent);
}
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Prism 7.1 支持此功能。以下内容直接取自 Prism 单元测试。如果您遵循命名约定,您实际上不需要注册任何内容,您只需设置 ViewModelLocator.AutowirePartialView 并引用父页面。
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.PartialView">
<StackLayout>
<Label Text="{Binding SomeText}" />
<Button Command="{Binding NavigateCommand}"
x:Name="navigateButton" />
</StackLayout>
</ContentView>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Prism.DI.Forms.Tests.Mocks.Views"
xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:converters="using:Prism.Forms.Tests.Mocks.Converters"
Title="{Binding Title}"
x:Name="xamlViewMock"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlViewMock">
<ContentPage.Resources>
<ResourceDictionary>
<prism:ContainerProvider x:TypeArguments="converters:MockValueConverter" x:Key="mockValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<local:PartialView mvvm:ViewModelLocator.AutowirePartialView="{x:Reference xamlViewMock}" />
<Entry x:Name="testEntry"
Text="{Binding Test,Converter={StaticResource mockValueConverter}}" />
</StackLayout>
</ContentPage>
如果您需要遵循一些自定义命名方案,您只需调用:
ViewModelLocationProvider.Register<MyView, SomeViewModel>();
更新
Prism 8 将引入对区域导航的支持。因此,Prism 7.1 中引入的 "Partial Views" 将被删除。您将需要迁移才能使用区域。这很容易做到,尽管在如何将参数传递到区域方面存在一些根本差异。
我认为没有必要为 ContentView
定义新的 ViewModel
。您只需要在使用 ContentView
的 Page
中使用 AutowirePartialView
属性。像这样
<DataTemplate>
<ViewCell>
<local:CardViewTemplatePage prism:ViewModelLocator.AutowirePartialView="true"/>
</ViewCell>
</DataTemplate>
您的 ContentView
应该像这样绑定字段
<StackLayout HorizontalOptions="Fill" VerticalOptions="Start">
<Label TextColor="Denim" Text="{Binding Title}" />
<Label TextColor="DimGray" Text="{Binding Description}" />
</StackLayout>
问题陈述 我想创建一个 contentview 用户控件,它有自己的视图模型,可以在多个内容页面中使用。
以下实施中的问题 我已经扩展了我的 App.xaml.cs 如下所述。但是一旦导航从具有 contentview 用户控件的内容页面开始工作,但如果我再次导航到该页面,导航将不起作用。补充一下,view.Parent 在下面的代码中也显示为 null。
请帮忙。
using OEP.Views;
using Prism;
using Prism.Common;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Unity;
using Unity.Resolution;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace OEP
{
public partial class App : PrismApplication
{
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
//await NavigationService.NavigateAsync("NewOrderPage");
await NavigationService.NavigateAsync("LoginPage");
//await NavigationService.NavigateAsync("HomePage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<LoginPage>();
containerRegistry.RegisterForNavigation<ForgotPasswordPage>();
containerRegistry.RegisterForNavigation<HomePage>();
containerRegistry.RegisterForNavigation<CustomerDetailsPage>();
containerRegistry.RegisterForNavigation<NewOrderPage>();
//Container.Resolve<HomePageCustomersUserControl>("Customers");
//containerRegistry.Register<HomePageCustomersUserControl, HomePageCustomersUserControlViewModel>();
//ViewModelLocationProvider.Register<HomePageCustomersUserControl>(() => Container.Resolve<HomePageCustomersUserControlViewModel>());
}
protected override void ConfigureViewModelLocator()
{
ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) =>
{
Page page = null;
switch (view)
{
case Page page1:
page = page1;
break;
case Element customView:
page = GetPageFromElement(customView);
// Existing parameter with the Page
break;
}
var navService = CreateNavigationService(page);
ParameterOverrides overrides = new ParameterOverrides
{
{ "navigationService", navService }
};
return Container.GetContainer().Resolve(type, type.GetType().Name, overrides);
});
}
// Currently exists
protected INavigationService CreateNavigationService(Page page)
{
var navigationService = NavigationService;
((IPageAware)navigationService).Page = page;
return navigationService;
}
protected INavigationService CreateNavigationService(object view)
{
switch (view)
{
case Page page:
return CreateNavigationService(page);
case Element element:
var parentPage = GetPageFromElement(element);
if (parentPage == null)
{
return null;
}
return CreateNavigationService(parentPage);
default:
return null;
}
}
private Page GetPageFromElement(Element view)
{
switch (view.Parent)
{
case Page page:
return page;
case null:
return null;
default:
return GetPageFromElement(view.Parent);
}
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Prism 7.1 支持此功能。以下内容直接取自 Prism 单元测试。如果您遵循命名约定,您实际上不需要注册任何内容,您只需设置 ViewModelLocator.AutowirePartialView 并引用父页面。
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.PartialView">
<StackLayout>
<Label Text="{Binding SomeText}" />
<Button Command="{Binding NavigateCommand}"
x:Name="navigateButton" />
</StackLayout>
</ContentView>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Prism.DI.Forms.Tests.Mocks.Views"
xmlns:prism="clr-namespace:Prism.Ioc;assembly=Prism.Forms"
xmlns:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:converters="using:Prism.Forms.Tests.Mocks.Converters"
Title="{Binding Title}"
x:Name="xamlViewMock"
x:Class="Prism.DI.Forms.Tests.Mocks.Views.XamlViewMock">
<ContentPage.Resources>
<ResourceDictionary>
<prism:ContainerProvider x:TypeArguments="converters:MockValueConverter" x:Key="mockValueConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<local:PartialView mvvm:ViewModelLocator.AutowirePartialView="{x:Reference xamlViewMock}" />
<Entry x:Name="testEntry"
Text="{Binding Test,Converter={StaticResource mockValueConverter}}" />
</StackLayout>
</ContentPage>
如果您需要遵循一些自定义命名方案,您只需调用:
ViewModelLocationProvider.Register<MyView, SomeViewModel>();
更新
Prism 8 将引入对区域导航的支持。因此,Prism 7.1 中引入的 "Partial Views" 将被删除。您将需要迁移才能使用区域。这很容易做到,尽管在如何将参数传递到区域方面存在一些根本差异。
我认为没有必要为 ContentView
定义新的 ViewModel
。您只需要在使用 ContentView
的 Page
中使用 AutowirePartialView
属性。像这样
<DataTemplate>
<ViewCell>
<local:CardViewTemplatePage prism:ViewModelLocator.AutowirePartialView="true"/>
</ViewCell>
</DataTemplate>
您的 ContentView
应该像这样绑定字段
<StackLayout HorizontalOptions="Fill" VerticalOptions="Start">
<Label TextColor="Denim" Text="{Binding Title}" />
<Label TextColor="DimGray" Text="{Binding Description}" />
</StackLayout>