Inheritance / “Componization” in WPF views
假设我的WPF应用程序中有几个视图都显示一个报表,但是每个视图都显示一个不同的报表。
我创建了一个基本的ViewModel,其中包含所有报表共有的所有内容。每个报表View都有自己的ViewModel,该ViewModel是从基本ViewModel派生的。这些派生的ViewModel具有以下职责:
生成报告数据
格式化报告数据以进行打印
提供报告参数的属性
提供可在生成的报告上执行的其他操作的命令(可选)
这一切都很好而简洁,但是,Views完全是一团糟。
基本上,每个视图都是相同的,只有一些小的更改,例如:
为报告参数提供不同的控件
提供绑定到命令的按钮以执行其他操作
我想实现以下目标:
具有一个基本视图,该基本视图定义了所有报表视图共有的所有内容,类似于ASP.NET中的Site.master。这将包含搜索按钮,打印按钮,报表在其中显示的网格等。
拥有具体的视图(每个报告一个),仅定义搜索参数的控件和其他操作的按钮
如何执行此操作?在WPF主页上进行谷歌搜索会带来很多定制解决方案-当然必须有一种标准方法吗?
- CAF GUI或PRISM使用此主区域和本地区域概念吗?不能使用吗?
-
我不使用这些框架,所以我认为我不能利用这个概念。
在WPF中,如果Content设置为相同的DataType实例,则可以根据其DataTemplate.DataType自动设置占位符ContentControl.ContentTemplate。
http://www.japf.fr/2009/03/thinking-with-mvvm-data-templates-contentcontrol/
-
1,这通常是我要做的。我将为所有ViewModel创建一个视图,并为特定数据使用ContentControls / DataTemplates,或者为DataTriggers w /一个ObjectToTypeConverter返回对象的类型。然后,根据转换后的值是否等于{x:Type local:MyViewModelA},我可以使用触发器
我不确定这是创建复合视图的标准方法,但是我已经成功定义了一个主视图(在本例中为Window),该主视图包含一个视图族所需的通用控件并通过使用ContentControl等效于ASP.NET母版页中的ContentPlaceholder,为每个不同的子视图将不同的UserControl注入到母版中。就我而言,我定义了一个所有子视图都实现的接口:
1 2 3 4
| public interface ISubView
{
BaseViewModel ViewModel { get; set; }
} |
这允许我的ApplicationController在处理显示特定视图的请求时将视图模型推入子视图。然后,子视图在显示之前通过主视图上的setter属性与主视图组成。
ApplicationController是集中于打开和关闭视图的任务的类;每当应用程序中的任何内容想要显示特定视图时,它都会询问ApplicationController。当它收到显示特定视图的请求时,它会从内部"注册表"中查找并构造相应的子视图和"视图模型"子类,并将各个部分组合在一起。在应用程序启动时,您创建ApplicationController并注册ViewModel子类和View子类的组合。部分示例实现为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class ApplicationController
{
private IDictionary <string, Tuple <Func <ISubView >, Func <BaseViewModel >> _registry ;
private Func <IShellWindow > _shellActivator ;
public ApplicationController (Func <IShellWindow > shellActivator )
{
_registry = new Dictionary <string, Tuple <Func <ISubView >, Func <BaseViewModel >>();
_shellActivator = shellActivator ;
}
public void RequestShow (string viewName )
{
var shell = _shellActivator ();
var viewToModel = _registry [viewName ];
var view = viewToModel .Item1();
var viewModel = viewToModel .Item2();
view .ViewModel = viewModel ;
shell .Display(view );
}
public Register (string name, Func <ISubView > subViewCreator, Func <ViewModel > viewModelCreator )
{
_registry .Add(name, new Tuple (subViewCreator, viewModelCreator ));
}
} |
在shell程序的Display方法(在IShellView接口上定义)中,您将执行以下操作:
1 2 3 4 5 6
| public void Display(ISubView view)
{
contentHolder.Content = view;
DataContext = view.ViewModel;
Show();
} |
应用程序启动中将包含以下内容:
1 2 3 4 5
| var appController = new ApplicationController (() => new ShellWindow ());
appController .Register("EmployeesReport", () => new EmployeesReportView (), () => new EmployeesReportViewModel );
appController .Register("OrdersReport", () => new OrdersReportView (), () => new OrdersReportViewModel ());
//etc. |
- 将用户控件注入内容控件的工作方式如何?是谁做的
-
我扩大了答案,以包括有关ApplicationController角色的更多信息。
-
在我看来,这就像一个自定义的CAF GUI实现。它具有用于维护视图及其模型的注册表字典的shell(如果是CAF GUI,则为区域)
-
我正在使用的实现相当轻巧,因为它是完全根据我的需求量身定制的。我已经合并了示例中未显示的其他概念,因为我需要它们,例如ViewLifeCycle和ShellActivator类,这很容易实现,因为它是我控制的代码。
-
感谢您的广泛回答。但是,我采用了AngelWPF的方法。