Prism 6.3:重用具有不同视图模型的视图

Prism 6.3: Reuse View with different View Model's

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

例如,您可以认为通用列表视图在某个地方显示苹果,在其他地方显示汽车。没关系。

在 Prism.Forms 中,对于 Xamarin,我能够像这样将视图与 viewModel 粘合在一起。

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

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

@AdamVincent 发布的 link 和 "missing" 方法对于使用 ViewModelLocationProvider 的正常 view/viewmodel 导航非常有用。但是,当尝试对同一视图使用两个视图模型时,它们不起作用。这是因为在扩展方法中有一个调用将视图模型注册到视图以供 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。

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

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 属性,因此注入 属性 会将视图模型直接注入到视图的 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");

所以对于正常的单一 View/Viewmodels,我使用 ViewModelLocationProvider.AutoWireViewModel 属性 并且在我有多个视图模型的地方,我使用这种替代方法。

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