【问题标题】:Prism 6.3: Reuse View with different View Model'sPrism 6.3:重用具有不同视图模型的视图
【发布时间】:2018-09-12 15:53:23
【问题描述】:

我在一个场景中使用两个完全独立的视图模型重用一个视图。

例如,您可以考虑使用通用列表视图在某处显示苹果,在其他地方显示汽车。真的没关系。

在 Xamarin 的 Prism.Forms 中,我能够将视图与这样的 viewModel 粘合。

 Container.RegisterTypeForNavigation<PageA, ViewModelA>("PageA1");
 Container.RegisterTypeForNavigation<PageA, ViewModelB>("PageA2");

我在 Prism WPF 中找不到等效项,有人可以帮我吗?

【问题讨论】:

  • 对此没有答案,但完全相关的有趣。 Xamarin.Forms 中但 WPF 中没有的此功能实际上已从 Xamarin.Forms 中删除。 github.com/PrismLibrary/Prism/issues/681
  • 看起来如果您有兴趣保留所提供的强类型,您可以修改 RegisterTypeForNavigation 扩展方法并将其放入您的 WPF 应用程序中。
  • 谢谢。这个链接很有帮助。
  • 如果您使用的是视图优先导航,您可以使用prism:ViewModelLocator.AutoWireViewModel 属性自动连接您的视图模型。
  • 我想你误解了我有一个视图和两个视图模型的问题,我不能在这里自动装配。

标签: wpf prism


【解决方案1】:

2022/01/15 更新

首先非常感谢Jason的回答,他的回答很棒,我参考他的设计完美实现了,但是因为Prism版本更新,我做了一些改动

我有一个带有多个视图模型的视图

第 1 步

注册您的视图区域

<ContentControl prism:RegionManager.RegionName="{x:Static hard:RegionNames.PanelPosCameraRegion}"></ContentControl>

第 2 步

编码您的视图模型

private IRegionManager _RegionManager;
private IUnityContainer _UnityContainer;

public ICommand LoadedCommand { get; set; }

public RoboticPageVM(IRegionManager regionManager, IUnityContainer unityContainer)
{
    _RegionManager = regionManager;
    _UnityContainer = unityContainer;
    LoadedCommand = new DelegateCommand(LoadedCommandHandle);
}

private void LoadedCommandHandle()
{
    BindViewModelToView<PanelPosMultiplexView, PanelPosCameraVM>("Camera");
    BindViewModelToView<PanelPosMultiplexView, PanelPosAxisVM>("Axis");
    
    LoadViewIntoRegion<PanelPosMultiplexView>(RegionNames.PanelPosCameraRegion, "Camera");
    LoadViewIntoRegion<PanelPosMultiplexView>(RegionNames.PanelPosAxisRegion, "Axis");
}

private void BindViewModelToView<TView, TViewModel>(string registerName)
{
    if (!_UnityContainer.IsRegistered<TViewModel>())
    {
        _UnityContainer.RegisterType<TViewModel>();
    }
    _UnityContainer.RegisterType<TView>(registerName, new InjectionProperty(nameof(UserControl.DataContext), new ResolvedParameter<TViewModel>()));
}

private void LoadViewIntoRegion<TView>(string regionName, string registerName)
{
    IRegion region = _RegionManager.Regions[regionName];
    object? view = region.GetView(registerName);
    if (view == null)
    {
        view = _UnityContainer.Resolve<TView>(registerName);
    }
    if (!region.Views.Any(v => v.GetType() == typeof(TView)))
    {
        region.Add(view, registerName);
    }
}

【讨论】:

    【解决方案2】:

    @AdamVincent 发布的链接和“缺失”方法对于使用 ViewModelLocationProvider 的普通视图/视图模型导航非常有用。但是,当尝试将两个视图模型用于同一视图时,它们不起作用。这是因为在扩展方法内部有一个调用将视图模型注册到视图以供 ViewModelLocationProvider 使用。

    private static IUnityContainer RegisterTypeForNavigationWithViewModel<TViewModel>(this IUnityContainer container, Type viewType, string name)
    {
        if (string.IsNullOrWhiteSpace(name))
            name = viewType.Name;
    
        ViewModelLocationProvider.Register(viewType.ToString(), typeof(TViewModel));
    
        return container.RegisterTypeForNavigation(viewType, name);
    }
    

    在内部,ViewModelLocationProvider.Register 使用字典来存储视图模型和视图之间的关联。这意味着,当您将两个视图模型注册到同一个视图时,第二个将覆盖第一个。

    Container.RegisterTypeForNavigation<PageA, ViewModelA>("PageA1");
    Container.RegisterTypeForNavigation<PageA, ViewModelB>("PageA2");
    

    所以通过上述方法,当使用 ViewModelLocationProvider 时,它总是会创建一个 ViewModelB 的实例,因为它是最后一个被注册的。

    另外,下一行调用RegisterTypeForNavigation,它本身最终调用Container.RegisterType,只是传递viewType。

    为了解决这个问题,我使用 Injection 属性以不同的方式解决了这个问题。我有以下方法将我的视图模型绑定到我的视图

    private void BindViewModelToView<TView,TViewModel>(string name)
    {
        if (!Container.IsRegistered<TViewModel>())
        {
            Container.RegisterType<TViewModel>();
        }
    
        Container.RegisterType<TView, TViewModel>(name,new InjectionProperty("DataContext", new ResolvedParameter<TViewModel>()));
    }
    

    我们知道每个视图都会有一个 DataContext 属性,因此 Injection 属性会将视图模型直接注入到视图的 DataContect 中。

    注册视图模型时,您将使用以下调用,而不是使用 RegisterTypeForNavigation:

    BindViewModelToView<PageA,ViewModelA>("ViewModelA");
    BindViewModelToView<PageA,ViewModelB>("ViewModelB");
    

    为了创建视图,我已经有了一个方法,用于将适当的视图注入我的区域,它使用视图名称作为获取正确视图模型实例的键。

    private object LoadViewIntoRegion<TViewType>(IRegion region, string name)
    {
        object view = region.GetView(name);
        if (view == null)
        {
            view = _container.Resolve<TViewType>(name);     
            if (view is null)
            {
                view = _container.Resolve<TViewType>();
            }
            region.Add(view, name);
        }
        return view;
    }
    

    我只是用

    调用
    var view = LoadViewintoRegion<PageA>(region,"ViewModelA");
    

    var view = LoadViewintoRegion<PageA>(region,"ViewModelB");
    

    所以对于普通的单个视图/视图模型,我使用 ViewModelLocationProvider.AutoWireViewModel 属性,而我有多个视图模型,我使用这种替代方法。

    【讨论】:

    • 那么这种方法对于 Xamarin Forms 也一样吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-25
    • 1970-01-01
    • 2020-08-07
    • 2014-03-15
    • 1970-01-01
    • 2018-12-21
    • 2016-11-30
    相关资源
    最近更新 更多