首页 > 解决方案 > 如何为具有棱镜的 Xamarin 表单中的内容视图创建单独的视图模型?

问题描述

问题陈述 我想创建一个 contentview 用户控件,它有自己的视图模型,可以在多个内容页面中使用。

下面的实现中的问题我已经扩展了我的 App.xaml.cs,如下所述。但是,一旦导航从具有 contentview 用户控件的 contentpage 工作,但如果我再次导航到该页面,导航将不起作用。只是为了添加它, view.Parent 在下面的代码中也为空。

请帮忙。

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
        }
    }
}

标签: xamarin.formsprism

解决方案


这在 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 中引入的“局部视图”将被删除。您需要迁移才能使用区域。这很容易做到,尽管在如何将参数传递到区域方面会有一些根本性的差异。


推荐阅读