将视图绑定到具有视图代码隐藏的ViewModel时,无法在ViewModel中使用NavigationService

问题描述 投票:0回答:3

我正在开发一个项目,我正在使用带有Autofac的Prism MVVM架构,试图创建一个可供其他视图使用的通用页脚导航栏。

我正在尝试从View的代码隐藏到ViewModel设置特定的绑定上下文 - 但无法这样做,因为viewmodel的实例化需要依赖注入导航服务。

我有以下内容:

  • 包含带导航栏的内容视图的.xaml(NavigationBarView.xaml)
  • 包含不同视图的内容页面的.xaml,包括导航栏视图(其他视图)

我通过执行以下操作来插入局部视图:

<ContentPage 
    ...
    xmlns:pages="clr-namespace:MyProject.Views;assembly=Myproject"
    .../>

<Layout
    ...
    <pages:NavigationBarPage />
    ...
</Layout>

以及我的代码隐藏中的构造函数是这样的:

public CodeBehindConstructor() {
     BindingContext = new NavigationBarViewModel(requires 
     navigationservice);
}

如果我可以在NavigationBarView后面的代码中创建一个INavigationService的实例,那将是可以解决的,但是我还没有找到这样做的方法。

我的ViewModel构造函数如下所示:

public NavigationBarViewModel(INavigationService navigationService)
    {
        _navigationService = navigationService;
        ...
        ...
    }

_navigationService设置为未初始化的INavigationService,这使服务无法使用。

NavigationBarView和NavigationBarViewModel之间的绑定有效,但我无法弄清楚如何处理INavigationService的依赖注入。

xamarin.forms prism
3个回答
0
投票

首先,只要您保持与任何其他视图一样的约定,您就可以自动浏览ViewModel。遇到问题的地方是,Prism目前不支持将NavigationService注入到自定义视图的ViewModel中。 Xamarin Forms中的导航取决于您对要导航的页面的理解。简单地解决INavigationService会导致NavigationService没有导航的页面概念,并且会完全弄乱你的导航。

首先,您需要注意PrismApplication中的ConfigureViewModelLocator方法。你会注意到默认情况下Prism只处理一个Page的'view'对象。你需要更新它来处理它是一个视图的情况,然后你可以走向ParentView,直到你到达一个Page的父。

protected override void ConfigureViewModelLocator()
{
    ViewModelLocationProvider.SetDefaultViewModelFactory((view, type) =>
    {
        NamedParameter parameter = null;

        switch(view)
        {
            case Page page:
                parameter = new NamedParameter("navigationService", CreateNavigationService(page));
                break;
            case View customView:
                var page = GetPage(customView);
                parameter = new NamedParameter("navigationService", CreateNavigationService(page));
                break;
        }

        return Container.Resolve(type, parameter);
    });
}

private Page GetPage(View view)
{
    switch(view.ParentView)
    {
        case Page page:
            return page;
        case null:
            return null;
        default:
            return GetPage(view.ParentView)
    }
}

如果由于某种原因您没有遵循约定并且不想显式设置AutoWire属性,您可以执行以下操作:

ViewModelLocationProvider.Register(typeof(NavigationBarView).ToString(),
                                   typeof(NavigationBarViewModel));

0
投票

只是为了更新这个问题以防万一其他人偶然发现(就像我一样)。

Prism v7.x现在可以选择使用prism:ViewModelLocator.AutowirePartialView属性将XAML中partialview的viewmodel连接到父页面。

  1. 按照正常情况为它创建局部视图和视图模型。
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.MyPartial">
  <StackLayout>       
    <Label Text="Please wait...." IsVisible="{Binding IsSearchLabelVisible}"/>
  </StackLayout>    
</ContentView>
  1. 注册视图,它在App.xaml.cs中匹配viewmodel:ViewModelLocationProvider.Register(typeof(MyPartial).ToString(), typeof(MyPartialViewModel));
  2. 现在将局部视图放入主页。请务必在标题区域中声明命名空间
    ...
    xmlns:uc="clr-namespace:MyApp"
    xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
    prism:ViewModelLocator.AutowireViewModel="True"
    x:Class="MyApp.Views.HostPage"
    x:Name="HostPageName"
    Title="Input ISBN"
    ....
<uc:MyPartial prism:ViewModelLocator.AutowirePartialView="{x:Reference HostPageName}"/>

这将部分与主页连接起来,因此部分的viewmodel可以声明为ContentPage类型,因此能够实例化所有常用的页面服务,例如navigationService和eventAggregator。

public class MyPartialViewModel : BaseViewModel
...
public bool IsSearchLabelVisible {get; set;}

public MyPartialViewModel(INavigationService navigationService,
                          IEventAggregator eventAgregatorService,
                          IPageDialogService dialogService)
            : base(navigationService, eventAgregatorService)
...

这个AutoWirePartialView with prism does not work or badly used?的链接让我走上了正确的道路。


-1
投票

我在我的Xamarin.Forms应用程序中做了什么:*创建一个位于Application类中的静态容器(始终是来自XF的Application类的后代)。

public class BaseApplication : Application
{
    public static IContainer Container { get; set; }

    ...
    ...
}

public BaseApplication(ContainerBuilder builder)
{
    ... register all dependencies here ... (don't forget your navigation service)

    Container = builder.Build();
}

此类由您的MainActivity(Android),MainPage(UWP)等创建。

LoadApplication(new BaseApplication(_containerBuilder));

当您需要从视图中创建视图模型时,您只需要

BindingContext = BaseApplication.Container.Resolve<MyViewModel>();

© www.soinside.com 2019 - 2024. All rights reserved.