wpf PRISM:如何将视图模型添加到区域并自动创建视图?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15924930/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PRISM: How to add a view model to a region and create the view automatically?
提问by Roman
I′m new to PRISM and trying some things out. I′m struggeling a little bit with MVVM.
我是 PRISM 的新手,正在尝试一些东西。我正在努力使用 MVVM。
The way you "connect" a view with a viewmodel is clear:
您将视图与视图模型“连接”的方式很清楚:
injection through unity, or
set the data context manually (ServiceLocator)
通过统一注入,或
手动设置数据上下文 (ServiceLocator)
Everything is working fine if I add a view to a region (the viewmodel is created automatically). But that′s not the use case.
如果我将视图添加到区域(视图模型是自动创建的),一切正常。但这不是用例。
Let′s take a look at the example:
让我们看一个例子:
public class MyViewModel : NotificationObject
{
public ObservableCollection<AnotherViewModel> OrderModel { get; private set; }
}
I have to create view models and add them to the collection. This view models have to be displayed (AnotherView) in a region (OrderRegion). My problem is, how can I achieve that now the view is created when I am adding a viewmodel to a region. This region is a TabControl, so it could happened that different views must be displayed.
我必须创建视图模型并将它们添加到集合中。这个视图模型必须在一个区域(OrderRegion)中显示(AnotherView)。我的问题是,我现在如何在将视图模型添加到区域时创建视图。该区域是一个 TabControl,因此可能需要显示不同的视图。
I have already took a look to the PRISM Quickstarts and the StockTrader example. What I am looking for is quite similar to
我已经看过 PRISM 快速入门和 StockTrader 示例。我正在寻找的内容与
virtual protected void StartOrder(string tickerSymbol, TransactionType transactionType)
{
if (String.IsNullOrEmpty(tickerSymbol))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.StringCannotBeNullOrEmpty, "tickerSymbol"));
}
this.ShowOrdersView();
IRegion ordersRegion = _regionManager.Regions[RegionNames.OrdersRegion];
var orderCompositeViewModel = ServiceLocator.Current.GetInstance<IOrderCompositeViewModel>();
orderCompositeViewModel.TransactionInfo = new TransactionInfo(tickerSymbol, transactionType);
orderCompositeViewModel.CloseViewRequested += delegate
{
OrderModels.Remove(orderCompositeViewModel);
commandProxy.SubmitAllOrdersCommand.UnregisterCommand(orderCompositeViewModel.SubmitCommand);
commandProxy.CancelAllOrdersCommand.UnregisterCommand(orderCompositeViewModel.CancelCommand);
commandProxy.SubmitOrderCommand.UnregisterCommand(orderCompositeViewModel.SubmitCommand);
commandProxy.CancelOrderCommand.UnregisterCommand(orderCompositeViewModel.CancelCommand);
ordersRegion.Remove(orderCompositeViewModel);
if (ordersRegion.Views.Count() == 0)
{
this.RemoveOrdersView();
}
};
ordersRegion.Add(orderCompositeViewModel);
OrderModels.Add(orderCompositeViewModel);
commandProxy.SubmitAllOrdersCommand.RegisterCommand(orderCompositeViewModel.SubmitCommand);
commandProxy.CancelAllOrdersCommand.RegisterCommand(orderCompositeViewModel.CancelCommand);
commandProxy.SubmitOrderCommand.RegisterCommand(orderCompositeViewModel.SubmitCommand);
commandProxy.CancelOrderCommand.RegisterCommand(orderCompositeViewModel.CancelCommand);
ordersRegion.Activate(orderCompositeViewModel);
}
The view model is created inside the code and added to the region. The whole type regestring is happend through the "ViewExportAttribute" so it makes it harder to understand the pattern behind it.
视图模型在代码中创建并添加到区域中。整个类型的regestring 是通过“ViewExportAttribute”发生的,因此很难理解其背后的模式。
EDIT:
编辑:
I have found a way to do this manually but it′s not very nice:
我找到了一种手动执行此操作的方法,但这不是很好:
var view = (FrameworkElement) ServiceLocator.Current.GetInstance<AnotherView>();
var model = ServiceLocator.Current.GetInstance<AnotherViewModel>();
view.DataContext = model;
regionManager.Regions["OrderRegion"].Add(view, null, true);
regionManager.Regions["OrderRegion"].Activate(view);
Roman
罗马
EDIT2:
编辑2:
Hi, I′m sorry, maybe I wasn′t clear enough.
嗨,对不起,可能我说得不够清楚。
My goal was to create a view model and then to configure it like in the example from the StockTrader above: Subscribe events, commands etc. After that I want to add this view model to the region, so that′s it could be displayed. This region might be a tabcontrol where different views with different view models are displayed. The order is:
我的目标是创建一个视图模型,然后像上面 StockTrader 的示例一样配置它:订阅事件、命令等。之后我想将此视图模型添加到该区域,以便它可以显示。该区域可能是一个选项卡控件,其中显示具有不同视图模型的不同视图。顺序是:
- Create a view model in the controller class
- Configure the view model
- Add the view model to local collection inside the controller
- Add the view model to the region
- 在控制器类中创建视图模型
- 配置视图模型
- 将视图模型添加到控制器内部的本地集合
- 将视图模型添加到区域
The missing piece what I was looking for was how to make it happen, that the view is created “automatically” with all stuff like binding etc. I have found an approach in this article (http://www.codeproject.com/Articles/229931/Understand-MVVM-Using-PRISM-by-Hello-World-Silverl). I have to create own interfaces for the view and the view model (IAnotherViewModel, IAnotherView).
我正在寻找的缺失部分是如何实现它,即使用绑定等所有内容“自动”创建视图。我在本文中找到了一种方法(http://www.codeproject.com/Articles /229931/Understand-MVVM-Using-PRISM-by-Hello-World-Silverl)。我必须为视图和视图模型(IAnotherViewModel、IAnotherView)创建自己的接口。
Another approach can be found here: http://paulstovell.com/blog/viewmodel-first-prism
另一种方法可以在这里找到:http: //paulstovell.com/blog/viewmodel-first-prism
回答by Rachel
Is there any reason not to use implicit DataTemplatesfor this?
有什么理由不为此使用隐式DataTemplates吗?
They are DataTemplatesthat define a DataTypeproperty, but not a Keyproperty, and they are used anytime WPF tries to draw an object of the specified DataType
它们DataTemplates定义了一个DataType属性,而不是一个Key属性,它们在 WPF 尝试绘制指定 DataType 的对象的任何时候使用
For example,
例如,
<TabControl ItemsSource="{Binding MyViewModelCollection}"
SelectedItem="{Binding SelectedViewModel}">
<!-- This could also go elsewhere, like Application.Resources -->
<TabControl.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</TabControl.Resources>
</TabControl>
If the TabControlis displaying an object of type ViewModelA, it will draw it using ViewA, and if it's displaying ViewModelB, it will draw it using ViewB
如果TabControl正在显示 类型的对象,ViewModelA它将使用 绘制它ViewA,如果正在显示ViewModelB,它将使用 绘制它ViewB
回答by Inga
yI you are using MEF then you can automate your view registration using Attributes:
yI 您正在使用 MEF,那么您可以使用属性自动进行视图注册:
/*YOUR VIEW*/
[ExportViewToRegion("MyView", "MyRegion")]
[Export(typeof(MyView))]
public partial class MyView : UserControl
{
....
}
/*IMPLEMENTATION*/
public interface IExportViewToRegionMetadata
{
string ViewName { get; }
string TargetRegion { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false)]
public class ExportViewToRegionAttribute : ExportAttribute
{
public ExportViewToRegionAttribute(string viewName, string targetRegion)
: base(typeof(UserControl))
{
ViewName = viewName;
TargetRegion = targetRegion;
}
public string ViewName { get; private set; }
public string TargetRegion { get; private set; }
}
[Export(typeof(IFluentRegionManager))]
public class FluentRegionManager : IFluentRegionManager, IPartImportsSatisfiedNotification
{
public IRegionManager RegionManager { get; set; }
[ImportingConstructor]
public FluentRegionManager(IRegionManager regionManager)
{
RegionManager = regionManager;
}
/*This Import will find all views in the assembly with attribute [ExportViewToRegion("ViewName", "RegionName")]*/
[ImportMany(AllowRecomposition = true)]
public Lazy<UserControl, IExportViewToRegionMetadata>[] Views { get; set; }
private readonly List<string> _processedViews = new List<string>();
private Lazy<UserControl, IExportViewToRegionMetadata> _GetViewInfo(string viewName)
{
return (from v in Views where v.Metadata.ViewTypeForRegion.Equals(viewName) select v).FirstOrDefault();
}
public IExportViewToRegionMetadata this[string viewName]
{
get
{
return (from v in Views
where v.Metadata.ViewName.Equals(viewName, StringComparison.InvariantCultureIgnoreCase)
select v.Metadata).FirstOrDefault();
}
}
public void ExportViewToRegion(string viewName)
{
if (viewName==null)
{
throw new ArgumentNullException("viewName");
}
var viewInfo = _GetViewInfo(viewName);
string targetRegion;
UserControl _view;
if (viewInfo != null)
{
targetRegion = viewInfo.Metadata.TargetRegion;
_view = viewInfo.Value;
}
if (string.IsNullOrEmpty(targetRegion) || _processedViews.Contains(viewName)) return;
RegionManager.RegisterViewWithRegion(targetRegion, _view.GetType());
_processedViews.Add(viewName);
}
/*All required views has been discovered and imported */
/*Loop true collection and register view with the region */
public void OnImportsSatisfied()
{
foreach (var viewName in from view in Views where !_processedViews.Contains(view.Metadata.ViewName)
select view.Metadata.ViewName)
{
ExportViewToRegion(viewName);
}
}
}
/* finally call IFluentRegionManager import in the bootstrapper to kick off registration*/

