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

时间:2017-07-10 12:27:24

标签: xamarin.forms prism

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

我试图将一个特定的绑定上下文从View的代码隐藏设置为ViewModel - 但是由于viewmodel的实例化需要依赖注入导航服务而无法这样做。 / p>

我有以下内容:

  • 包含带导航栏的内容视图的.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的依赖注入。

3 个答案:

答案 0 :(得分:0)

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

首先,您要注意PrismApplication中的ConfigureViewModelLocator方法。您会注意到默认情况下Prism只处理一个视图&#39;对象是一个页面。您将要更新此处理以处理它是一个视图的情况,然后您可以走向ParentView,直到您到达作为Page的Parent。

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));

答案 1 :(得分:0)

只是更新这个问题,以防其他人偶然发现(如我所做的那样)。

Prism v7.x现在可以选择使用以下方式将XAML中的局部视图的视图模型连接到父页面: prism:ViewModelLocator.AutowirePartialView 属性。

  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中注册视图及其匹配的视图模型,因此: 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?的链接 让我走上正确的路。

答案 2 :(得分:-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>();