开发者

Inheritance / "Componization" in WPF views

开发者 https://www.devze.com 2023-04-12 05:54 出处:网络
Assume I have several Views in my WPF application that all show a report, but each View shows a different report.

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 add开发者_如何学Pythonitional 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?


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/


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.
0

精彩评论

暂无评论...
验证码 换一张
取 消