A day in the life...

public class GeekEarth : Earth { }
posts - 14, comments - 22, trackbacks - 0

Models, views, controllers

I’m working on a new app which will have WPF as its user interface.  With the slight nag in the back of my mind that someone might say “No, we want WinForms” (I hope not) I decided there should be as little code in the UI as possible so I opted for a view/controller approach where my controller will perform all of the logic and the view will only do “viewy” things.

One of the features of this app is that some of the views should appear as modal windows (such as a splash window during start-up) but others should appear as tabs in a client area so that the user can switch between different tasks.  So I created a UserControl which data bound to a list of IView, there were two problems with this approach.

Firstly I wanted my IView implementers to be UserControls so that when I data bind to the List<IView> the content of the TabItem would show the correct control.  The problem was that I needed the header of the TabItem to be data bound too…

 

   1:  <TabControl.ItemTemplate>
   2:      <DataTemplate>
   3:          <TextBlock Text="{Binding ViewTitle}"/>
   4:      </DataTemplate>
   5:  </TabControl.ItemTemplate>

 

So all I need is a string property on IView named ViewTitle and all would be well.  Or so you would think!  Data binding a TabControl to a list of objects which happen to be UserControl descendants doesn’t work very well, as a result the ItemTemplate was unable to read my property.  Even if I converted it to a DependencyProperty it still didn’t work.  It seems it is a problem with the way the WPF TabControl was written.

Secondly there was a problem when it came to wizard like views.  When one of my tabs needs multiple steps how can I change the view?  I can’t!

The solution was instead to data bind the TabControl to a list of controllers instead.  Seeing as I already have IController for controllers which have modal views I went for List<IEmbeddedController> instead.

 

   1:  public interface IController : INotifyPropertyChanged
   2:  {
   3:      IView CurrentView { get; }
   4:  }
   5:   
   6:  public interface IEmbeddedController : IController
   7:  {
   8:      string ViewTitle { get; }
   9:  }

 

Now I can bind my TabItem.ItemTemplate to ViewTitle with no problem because it is not binding to a UserControl but a plain object instead.  In addition the IEmbeddedController can set its CurrentView property at any point in order to show a different presentation to the user.

 

   1:  <TabControl.ContentTemplate>
   2:      <DataTemplate>
   3:          <ContentPresenter Content="{Binding CurrentView}" />
   4:      </DataTemplate>
   5:  </TabControl.ContentTemplate>

 

Now my tabbed views function correctly.  I see a title on the tab item and the contents of the view.  The next thing to address is how does the controller know which view to set CurrentView to?  I do that using Unity dependency injection.  So, for example, if I were to create a controller named “WizardTest” with two steps I would have the following items…

  1. IStep1View
  2. Step1Control (implements IStep1View)
  3. IStep2View
  4. Step2Control (implements IStep2View)
  5. IWizardTestController
  6. WizardTestController (implements IWizardTestController)

 

In the start-up phase of my application I would register which classes implement those interfaces…

 

   1:  static void RegisterWizardTestViewAndController(IUnityContainer container)
   2:  {
   3:      container.RegisterType
   4:          <
   5:              MyApp.Views.WizardTest.IWizardTestController,
   6:              MyApp.Views.WizardTest.WizardTestController
   7:          >(new TransientLifetimeManager());
   8:      container.RegisterType
   9:          <
  10:              MyApp.Views.WizardTest.Steps.IStep1View,
  11:              MyApp.Views.WizardTest.Steps.Step1
  12:          >(new TransientLifetimeManager());
  13:      container.RegisterType
  14:          <
  15:              MyApp.Views.WizardTest.Steps.IStep2View,
  16:              MyApp.Views.WizardTest.Steps.Step2
  17:          >(new TransientLifetimeManager());
  18:  }

 

When I need to show one of the states I write code something like this…

 

   1:  IStep1View Step1View;
   2:  private void SetStep1View()
   3:  {
   4:      if (Step1View == null)
   5:      {
   6:          Step1View = ViewFactory.CreateView<IStep1View>();
   7:          Step1View.Initialize(SomeModelObjectToEdit);
   8:      }
   9:      CurrentView = Step1View;
  10:  }

 

I can update the view on the screen simply by changing the CurrentView property.  Let’s say that the Step1Control needs a NEXT button.  This is easily achieved like so…

 

   1:  public interface IStep1View : IView
   2:  {
   3:      ICommand NextCommand { get; set; }
   4:  }

 

ICommand is an interface that WPF uses to bind UI actions to, for example in Step1Control I implement the property required by IStep1View and in the XAML I add a button like this…

 

   1:  <Button Content="Go to step 2" Command="{Binding CurrentView.NextCommand}"/>

 

CurrentView because (remember) the control is bound to the IEmbeddedController so we need the NextCommand of the CurrentView.  When the user clicks this button it will execute the ICommand.Execute.  This means that I don’t need a Button.Click event in the XAML, I don’t need to handle the event in the code-behind class, and I don’t need some kind of NextButtonClicked event on IStep1View - although I do instead need a NextCommand property instead, but all in all there’s less work…

 

   1:  IStep2View Step1View;
   2:  private void SetStep1View()
   3:  {
   4:      if (Step1View == null)
   5:      {
   6:          Step1View = ViewFactory.CreateView<IStep1View>();
   7:          Step1View.Initialize(SomeModelObjectToEdit);
   8:          Step1View.NextCommand = new ActionCommand(p => State = States.Step2);
   9:      }
  10:      CurrentView = Step1View;
  11:  }

 

ViewFactory is a very simple class which asks Unity to Resolve<> the type passed.  This not only gives me simple construction but also injects any dependencies.  ActionCommand is also a simple class which uses a lambda expression as its action to execute:

 
   1:  public class ActionCommand : ICommand
   2:  {
   3:      readonly Action<object> Action;
   4:      public event EventHandler CanExecuteChanged;
   5:   
   6:      public ActionCommand(Action<object> action)
   7:      {
   8:          Action = action;
   9:          Enabled = true;
  10:      }
  11:   
  12:      bool enabled;
  13:      public bool Enabled
  14:      {
  15:          get { return enabled; }
  16:          set
  17:          {
  18:              enabled = value;
  19:              var canExecuteChanged = CanExecuteChanged;
  20:              if (canExecuteChanged != null)
  21:                  canExecuteChanged(this, EventArgs.Empty);
  22:          }
  23:      }
  24:   
  25:      public bool CanExecute(object parameter)
  26:      {
  27:          return Enabled;
  28:      }
  29:   
  30:      public void Execute(object parameter)
  31:      {
  32:          Action(parameter);
  33:      }
  34:  }

 

Clicking the button in the UserControl executes the NextCommand.Execute method, this is a lambda which sets State = States.Step2.  The property “State” has a switch statement and calls SetStep1View, SetStep2View etc depending on the state that has been set.

Finally the management of the tabbed views, and how to show new views from other controllers.  I did this with a service I called “IEmbeddedControllerPresenterController” because it is a controller which presents Embedded Controllers. 

 

   1:  public interface IEmbeddedControllerPresenterController
   2:  {
   3:      void AddController(IEmbeddedController controller);
   4:      void RemoveController(IEmbeddedController controller);
   5:      void FocusController(IEmbeddedController controller);
   6:      UserControl UserControl { get; }
   7:  }

 

I’m going to change the “UserControl UserControl” property to “IView View'” very soon.  But the idea is that this is registered in Unity with a ContainerControlledLifetimeManager (effectively a singleton).  I can add and remove controllers from the list which my TabControl is bound to, and also specify which one should be focused. 

Now when it comes to creating new views I use an IEmbeddedControllerRepository.  It’s a repository because I don’t always create the embedded controller.  When asking for a controller I pass a unique “InstanceID” which could be something simple like “NewCustomer” / “NewSupplier”, or when editing an instance of a user it could be the primary key of that user instance.  This is to ensure that when you want a controller to edit “User A” and the interface already has a view editing that object the existing view will focus, if there is no view present for this object a new one will be created by calling a factory (simply delegates to Unity).

 

   1:  public interface IEmbeddedControllerRepository
   2:  {
   3:      TController GetController<TController>(string instanceID) where TController : IEmbeddedController;
   4:      void ReleaseController(string instanceID);
   5:  }

 

Two examples of using this might be…

 

   1:  var repository = EmbeddedUserRepository;
   2:   
   3:  //Example 1 - only create 1 new user at a time
   4:  repository.GetController<INewUserController>("NewUser");
   5:   
   6:  //Example 2 - edit any number of users, but never the same one twice
   7:  User user = .........;
   8:  var controller = repository.GetController<IEditUserController>(user.UniqueID);
   9:  controller.Initialize(user);

 

All of this is really just a slight variation of an approach I took for a wizard driven application I once wrote for the Pocket PC.  The only difference is that the views are tabbed (whereas the PPC they were stacked and only 1 was visible at a time), and in this I am using Unity so that I can modularise the code a bit and potentially get some mocked objects in there via the interface definitions so that I can unit test more easily.

I’ve read (http://tech.groups.yahoo.com/group/domaindrivendesign/message/13738) that you should only have one controller per application.  I simply don’t think this is true.  While I do believe it is sometimes necessary to have a single controller responsible for multiple views I don’t think there are many apps which require only a single controller.  E.g. if you are modelling a 3D scene and want to see a live preview of the scene in one window and sliders etc to control the camera angle you would use one controller, but if you are editing a customer, a supplier, and an employee you will need  three different types of controller, and one instance of a controller per object you are editing in isolation.

Print | posted on Friday, July 17, 2009 4:38 PM | Filed Under [ Code ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 3 and type the answer here:

Powered by: