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);
}
}
我在一个场景中要重用具有两个完全独立的视图模型的视图。
例如,您可以认为通用列表视图在某个地方显示苹果,在其他地方显示汽车。没关系。
在 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);
}
}