继承/“组合化”在 WPF 视图中

发布于 2024-12-09 13:13:28 字数 615 浏览 0 评论 0原文

假设我的 WPF 应用程序中有多个视图,它们都显示一个报告,但每个视图显示不同的报告。
我创建了一个基本 ViewModel,其中包含所有报告共有的所有内容。每个报表视图都有自己的 ViewModel,该 ViewModel 派生自基本 ViewModel。这些派生的 ViewModel 具有以下职责:

  1. 生成报告数据
  2. 格式化报告数据以进行打印
  3. 提供报告参数的属性
  4. 提供可在生成的报告上执行的其他操作的命令(可选)

这一切都很好且简洁,但是,视图一团糟。 基本上,每个视图都是相同的,只有一些细微的变化,例如:

  1. 为报告参数提供不同的控件
  2. 绑定到命令以执行附加操作的按钮

我想实现以下目标:

  1. 提供 一个基本视图,定义所有报表视图共有的所有内容,类似于 ASP.NET 中的 Site.master。这将包含搜索按钮、打印按钮、报告显示的网格等...
  2. 具有具体视图 - 每个报告一个 - 仅定义搜索参数的控件和附加操作的按钮

如何做这?谷歌搜索 WPF 母版页会带来很多定制的解决方案 - 肯定有一个标准的方法吗?

Assume I have several Views in my WPF application that all show a report, but each View shows a different report.
I created a base ViewModel that contains all the stuff all reports have in common. Each report View gets its own ViewModel that is derived from the base ViewModel. These derived ViewModels have the following responsibilities:

  1. Generate the Report Data
  2. Format the Report Data for printing
  3. Provide properties for the report parameters
  4. Provide Commands for additional actions that can be executed on the generated report (optional)

That's all fine and concise, however, the Views are a complete mess.
Basically, each View is the same with only some minor changes, for example:

  1. Provide different controls for the report parameters
  2. Provide buttons that bind to the Commands for the additional actions

I would like to achieve the following:

  1. Have a base View that defines all the stuff all report Views have in common, similar to a Site.master in ASP.NET. This would contain the search button, the print button, the grid the report is shown in etc...
  2. Have concrete Views - one for each report - that defines only the controls for the search parameters and the buttons for the additional actions

How to do this? Googling for WPF master page brings up a lot of custom made solutions - surely there must be a standard way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

给不了的爱 2024-12-16 13:13:28

在 WPF 中,如果将 Content 设置为相同的 ,则可以根据其 DataTemplate.DataType 自动设置占位符 ContentControl.ContentTemplate DataType 实例。

http://www.japf.fr/2009/ 03/思考-with-mvvm-data-templates-contentcontrol/

In WPF, a placeholder ContentControl.ContentTemplate can be set automatically based on its DataTemplate.DataType if the Content is set to that same DataType instance.

http://www.japf.fr/2009/03/thinking-with-mvvm-data-templates-contentcontrol/

独享拥抱 2024-12-16 13:13:28

我不确定这是创建复合视图的标准方法,但我已经成功定义了一个主视图(在我的例子中是一个 Window ),其中包含一个系列所需的公共控件视图,并使用 ContentControl 作为 ASP 中的 ContentPlaceholder 的等效项,为每个不同的子视图注入不同的 UserControl 到主视图中.NET 母版页。就我而言,我定义了一个所有子视图都实现的接口:

public interface ISubView
{
   BaseViewModel ViewModel { get; set; }
}

这允许我的 ApplicationController 在处理显示特定视图的请求时将视图模型推送到子视图中。然后,在显示之前,子视图通过主视图上的 setter 属性与主视图组合。

ApplicationController 是集中打开和关闭视图任务的类;每当应用程序中的任何内容想要显示特定视图时,它都会询问 ApplicationController。当它收到显示特定视图的请求时,它会从内部“注册表”查找并构造相应的子视图和视图模型子类,并将各个部分组合在一起。在应用程序启动时,您创建 ApplicationController 并注册 ViewModel 子类和 View 子类的组合。部分示例实现是:

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 接口上定义)中,您将执行以下操作:

   public void Display(ISubView view)
   {
      contentHolder.Content = view;
      DataContext = view.ViewModel;
      Show();
   }

应用程序启动将包括以下内容:

var appController = new ApplicationController(() => new ShellWindow());
appController.Register("EmployeesReport", () => new EmployeesReportView(), () => new EmployeesReportViewModel);
appController.Register("OrdersReport", () => new OrdersReportView(), () => new OrdersReportViewModel());

//etc.

I'm not sure that this is the standard way to create composite views, but I've had some success defining a master view (a Window in my case) that contains the common controls needed by a family of views and injecting a different UserControl for each different sub-view into the master by using a ContentControl as the equivalent to a ContentPlaceholder in an ASP.NET masterpage. In my case, I've defined an interface that all sub-views implement:

public interface ISubView
{
   BaseViewModel ViewModel { get; set; }
}

This allows my ApplicationController to push the view model into the child view when handling a request to show a particular view. The sub-view is then composed with the master view via a setter property on the master view before being shown.

The ApplicationController is the class that centralizes the tasks of opening and closing views; whenever anything in the application wants to show a particular view it asks the ApplicationController. When it receives a request to show a particular view it looks up and constructs the corresponding sub-view and View Model sub-class from an internal "registry", and composes the parts together. At application start-up you create the ApplicationController and register the combinations of ViewModel subclass and View subclass. A partial example implementation is:

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

In the shell's Display method (defined on the IShellView interface) you'd do something like:

   public void Display(ISubView view)
   {
      contentHolder.Content = view;
      DataContext = view.ViewModel;
      Show();
   }

The application start-up would include something like:

var appController = new ApplicationController(() => new ShellWindow());
appController.Register("EmployeesReport", () => new EmployeesReportView(), () => new EmployeesReportViewModel);
appController.Register("OrdersReport", () => new OrdersReportView(), () => new OrdersReportViewModel());

//etc.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文