【问题标题】:Unable to use NavigationService in ViewModel when binding View to ViewModel with view code-behind使用视图代码隐藏将视图绑定到 ViewModel 时,无法在 ViewModel 中使用 NavigationService
【发布时间】:2019-03-11 18:30:04
【问题描述】:

我正在一个项目中使用 Prism MVVM 架构和 Autofac,试图创建一个可以被其他视图使用的通用页脚导航栏。

我正在尝试将特定的绑定上下文从 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


    【解决方案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&lt;MyViewModel&gt;();

    【讨论】:

    • 这个例子展示了不好的做法......假设您有正当理由从视图中的代码中使用容器,您应该注入容器而不是尝试静态访问它。答案也无法解决将工作 NavigationService 实例放入 ViewModel 的问题,因为 Container.Resolve&lt;MyViewModel&gt;() 本身并不能做到这一点。
    • @DanS。 Container.Resolve() 解析导航服务,因为需要注册导航服务以及您的视图模型。在告诉它不起作用之前,您应该尝试一下。这不是一个不好的做法,因为 Xamarin Forms 中没有机制可以注册另一个容器而不是它自己的依赖服务......您应该先阅读一些文档,然后再告诉它是不好的做法......
    • 我告诉您,作为 Prism 团队的成员,您的回答会宣传不良做法,并且不会提供 INavigationService 的工作实例。请参阅我提供的答案以获得更多解释。
    • 我可以补充一点,INavigationService 是一个命名服务。我们特别不使用容器宣传名称,这意味着即使您的 ViewModel 具有 navigationService 名称,取决于容器,它会将 INavigationService 解析为 null 或通过异常解析。它正确解决的唯一方法是通过 ViewModelLocationProvider。如果您已阅读文档或对 Prism 的工作原理有适当的了解,那么您现在可能会这样做。
    【解决方案2】:

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

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

    这将部分与主机页面连接起来,因此部分的视图模型可以声明为 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?的链接 让我走上正确的道路。

    【讨论】:

      【解决方案3】:

      首先,只要您保持与任何其他视图相同的约定,您就可以自动装配 ViewModel。您遇到问题的地方是 Prism 目前不支持将 NavigationService 注入 ViewModel 以实现开箱即用的自定义视图。 Xamarin Forms 中的导航取决于对您从哪个页面进行导航的理解。简单地解析 INavigationService 会导致 NavigationService 没有它正在导航的页面的概念,并且会完全打乱您的导航。

      首先,您需要注意 PrismApplication 中的 ConfigureViewModelLocator 方法。您会注意到,默认情况下 Prism 只处理一个“视图”对象,它是一个页面。您需要对其进行更新以处理它是视图的情况,然后您可以沿着 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));
      

      【讨论】:

        猜你喜欢
        • 2016-08-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-31
        • 2017-08-03
        • 1970-01-01
        相关资源
        最近更新 更多