Windows Phone 7 Developer Launch - Learn More
kick it on DotNetKicks.com   Shout it  

MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

In my last post I addressed regions in the Composite Application Library for WPF/Silverlight (Prism). I looked at what they were and how they were used. But by the end of the post I felt that the concept of View Injection needed further attention. I’ve almost exclusively used View Discovery up to this point in my development. But when I’ve run into a need to use View Injection I’m uncomfortable with what seems to be a tightly coupled relationship.

View Injection is used to provide programmatic control over how and where a view is created and activated. Which means that often we use View Injection in response to some sort of event, most likely user-driven. I haven’t gotten this far in the discussion of the MVVM pattern, but I hope you’ll bear with me for a brief moment. When developing an application using the MVVM pattern your goal is to use Commands to communicate between the View and the View-Model. When you do this, and need to create another MVVM triad (“triad” – because of the 3 objects Model, View and View-Model) then your View-Model is coupled not only to the creation and activation of the triad, but to the region where it will be displayed. Here’s an example of the code required for the View Injection method:

   1: string viewName = employeeId.ToString(CultureInfo.InvariantCulture);
   2: IRegion detailsRegion = regionManager.Regions["DetailsRegion"];
   3: object view = detailsRegion.GetView(viewName);
   4:  
   5: if(view == null)
   6: {
   7:     view = Container.Resolve<IEmployeeView>();
   8:     detailsRegion.Add(view, viewName);
   9: }
  10:   
  11: detailsRegion.Activate(view);

The problem with this is that our View-Model now knows too much about the other view (type, region name, and model). Additionally, if logic is required to remove the view from the region then that also becomes the responsibility of our View-Model. This then violates the principal of single responsibility.

Controller Pattern

I’ve kinda stepped in *it* at this point. I didn’t really have a name for the pattern at use other than the name used by the Prism team in their QuickStart. When I went around to look for a good definition of Controller which matched with how it was used I didn’t find one. Turns out I was just blindly following the examples in the Prism Guidance. After searching around a bit, it turns out no one has actually formalized this pattern in the context of Silverlight/WPF. That’s not to say I or the Prism team are inventing anything new here. Just that I don’t have a formalized pattern to discuss here, nor to I have documentation or collective community behind me.

What I do have though, are a couple of good blog posts by Ward Bell discussing a pattern for which he is still looking for a good name. For now, he’s calling it ScreenFactory. I’m not one to get into big discussions on what to name things. In fact for me, if it takes me more than a couple minutes to come up with a proper name for a class or pattern, I punt. I’ll either name it AClassThatDoesSomethingIWantItToDo (thanks to intellisense this unwieldy name is not a big deal) or more often than not I’ll come up with something that is good enough and then add comments and remarks to explain my intent clearly.

Here are the posts by Ward Bell, I recommend reading them as it’s a good to help understand what the goal here is. In my view the Controller pattern in the Prism QuickStarts is very close if not exactly the same thing as Bell’s ScreenFactory:

The basic idea here is that part of the goal of the View-Model is to create a class which is completely independent of the UI. No references at all in the View-Model to any UI classes. For me, I try to stick pretty close to the rule and when there are exceptions (FileDialog for example) I first look to the view’s code behind.

That said, while we aren’t actually referencing any UI classes, View Injection deals with the concept of opening/closing and activating/deactivating views. The less your View-Model knows about any view, including the one it represents the better because you have the greater chance of reuse without complication when you abide by this principle.

The Point

So the controller here is responsible for the life-cycle of the related views. It creates a view (if it doesn’t already exist) and activates it. In the example here there is no addressing of closing and cleanup of the view. However, that also would be the responsibility of the controller.

Forgive me for jumping so far ahead, but I don’t want to gloss over anything important. As I was writing my last post I felt uncomfortable with presenting the concept of View Injection without properly looking at how to use it. For me it’s like demonstrating an SQL snippet that is open to an injection attack. I’m far from perfect in many areas, but when I can I try to show how to do things properly instead of promulgating bad practices.

Back to View Injection...

View Injection

Instead of coming up with a contrived example of using View Injection I thought I’d use the Prism team’s contrived example (their words, not mine ;) ). Actually, I don’t think it’s so bad. I think it’s a pretty good example of decoupling the responsibility over a related from a View-Model. I’m referring to the View Injection QuickStart in the Prism Guidance samples. If you’d like to follow along, you can download it from MSDN. Just download the .exe package and run the installer. Make note of where it installs the Guidance and run the batch file to start up the View Injection QuickStart.

If you are following along, then once you’ve opened the QuickStart, open the UIComposition.Modules.Employee.XXX project and under Views\EmployeesView\EmployeesPresenter.cs and Controllers\EmployeesController.cs. For the rest of you here’s the source code:

public interface IEmployeesController
{
    void OnEmployeeSelected(BusinessEntities.Employee employee);
}
 
public class EmployeesPresenter
{
    private IEmployeesListPresenter listPresenter;
    private IEmployeesController employeeController;
 
    public EmployeesPresenter(
        IEmployeesView view,
        IEmployeesListPresenter listPresenter,
        IEmployeesController employeeController)
    {
        this.View = view;
        this.listPresenter = listPresenter;
        this.listPresenter.EmployeeSelected += new EventHandler<DataEventArgs<BusinessEntities.Employee>>(this.OnEmployeeSelected);
        this.employeeController = employeeController;
 
        View.SetHeader(listPresenter.View);
    }
 
    public IEmployeesView View { get; set; }
 
    private void OnEmployeeSelected(object sender, DataEventArgs<BusinessEntities.Employee> e)
    {
        employeeController.OnEmployeeSelected(e.Value);
    }
}
 
public class EmployeesController : IEmployeesController
{
    private IUnityContainer container;
    private IRegionManager regionManager;
 
    public EmployeesController(IUnityContainer container, IRegionManager regionManager)
    {
        this.container = container;
        this.regionManager = regionManager;
    }
 
    public virtual void OnEmployeeSelected(BusinessEntities.Employee employee)
    {
        IRegion detailsRegion = regionManager.Regions[RegionNames.DetailsRegion];
        object existingView = detailsRegion.GetView(employee.EmployeeId.ToString(CultureInfo.InvariantCulture));
 
        if (existingView == null)
        {
            IProjectsListPresenter projectsListPresenter = this.container.Resolve<IProjectsListPresenter>();
            projectsListPresenter.SetProjects(employee.EmployeeId);
 
            IEmployeesDetailsPresenter detailsPresenter = this.container.Resolve<IEmployeesDetailsPresenter>();
            detailsPresenter.SetSelectedEmployee(employee);
 
            IRegionManager detailsRegionManager = detailsRegion.Add(detailsPresenter.View, employee.EmployeeId.ToString(CultureInfo.InvariantCulture), true);
            IRegion region = detailsRegionManager.Regions[RegionNames.TabRegion];
            region.Add(projectsListPresenter.View, "CurrentProjectsView");
            detailsRegion.Activate(detailsPresenter.View);
        }
        else
        {
            detailsRegion.Activate(existingView);
        }
    }
}

What we’re seeing here is our View-Model (EmployeesPresenter) receives an instance of a Controller (IEmployeesController) and IEmployeesListPresenter.

I know what you’re thinking, “didn’t you just say the less the View-Model knows about other View-Models the better?”. Yes I did, but I didn’t write this code and I would do it differently but this post is already further down the road than I’d like for what we’ve covered so far in this series. I will just say that a better (although slightly more complicated) solution would be to use Aggregate Events since EmployeesPresenter doesn’t need to know anything about IEmployeesListPresenter – it just needs to know when the selected employee has changed. For now, my assumption is that the Prism team didn’t want to muddy the waters here but wanted to keep things familiar and thus easier to understand.

The important piece here is that we’re subscribing to an event that exposes the selected employee for us. Then just forwarding it to our controller – we just fire and forget because what the controller does with it at this point is up to the controller and we get nice clean separation.

What the controller *does* do here should look familiar. Below are some screenshots of the application running in Silverlight and WPF. You’ll notice that there is a “Select Employee” list at the top of the page and the employee details are shown below in a Tab control. The Tab control comprises multiple views: employee details and project lists. So when the Controller’s OnEmployeeSelected method is called in response to the EmployeeSelected event the controller first looks to see if the view has already been created. If not, it builds the MVVM triads required (Details and ProjectsList), injects each view into the appropriate region and activates the view.

7fdb5d2f-dc5d-4942-88a6-9a20bae6399b 40d642f4-6170-4e5b-b237-adaa386c669f

Honestly, for the longest time I simply accepted the coupling of views in this situation as “technical debt” and I didn’t really understand what the Prism team was doing with these Controller classes and interfaces in the QuickStarts. And it seems like I’m not alone. As I was researching for the series I found a dearth of information on the topic. In fact when I googled  the topic today my post from yesterday was in the top 10 results. So I hope that this clears things up for those with questions about how to properly use View Injection as well as those like me who looked at the controllers and said, “what?”.

tags: , , , , ,

kick it on DotNetKicks.com   Shout it  

Feedback

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Martin Fowler defined the Application Controller design pattern in his immortal PoEAA. Whilst researching PRISM myself I just figured this was their implementation of his pattern. Here is a link to the definition. www.martinfowler.com/.../...icationController.html

Nice posts! Keep writing! 10/22/2009 10:03 AM | Ken

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Thanks! I'm glad you're enjoying it.

As for the controller pattern, I believe that referring to Fowler's definition does have benefit. But I agree with Bell that this is something just a little bit different. I haven't read Fowler's definition in full (just what's on his site) but the Prism team does use multiple controllers in their quick starts and the reference implementation.

It seems a lot like splitting hairs to me, however my view is that is Gossman renamed Fowler's Presentation Model to MVVM because it was "not quite the same" (not quoting Gossman, just emphasising) then something like this seems to be also a case for renaming a pattern.

That said I'm not really religious about this, mostly conveying my understanding of what I've read. It seems you are right that the intent of the Prism team was to implement Fowler's Application Controller since they named it Controller and not "Screen Factory". I just really like Bell's take on it. 10/23/2009 3:25 PM | MarkJMiller

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Hi Mark, good series about Prism! Keep them coming, as they seem very useful for the community, and it's good feedback for the Prism team (which I'm part of).
As Ken suggested, this is in fact an implementation of Application/Front Controller, and deals with the iteraction between the views (or presenters in this case).
I also agree it's a contrived example, because we're showing extreme composition of views (in the case of EmployeesPresenter, that has a separate view for the eployees list, and another for the details). In a normal scenario, you'd probably have 1 view for both, and just keep the extension point (the Details Region).
Also, you may notice we didn't use ViewModels, just plain old Presenters, as this is the first QuickStart we wrote, and after we started using VMs we decided to keep it so we know this guidance work without them.
Also, we are aware of the missing infrastructure to build new MVVM triads, and you can expect that to be tackled in future versions of Prism. As you said, having a custom controller does the job, but it's doing a little too much for our taste.
I'll stop because this is an already long comment, but thanks again, we're driven by community feedback! 10/26/2009 7:52 PM | Julian Dominguez

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Julian,

Thanks for clarifying. I went back and looked at the Prism docs on Supervising Presenter and Presentation Model after reading your comment. I haven't really read anything on the MVP pattern up to this point and so I hadn't really noticed it in the Controller QuickStart. I might just need to introduce a sidebar for the series at some point comparing the two. The docs are a good starting point. It'll give me a chance to look at them in more detail and understand the distinction better.

As for the Controller, like I said in my previous comment, I figured your intent was Application Controller. I've just really had difficulty in nailing it down in the context of WPF/Silverlight. Bell's posts were the best I read to help me understand the purpose. Once I read his posts, the implementation in the QuickStarts finally made sense to me. 10/27/2009 11:14 AM | MarkJMiller

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Hi Mark,

I'm trying to reuse the Reference Implementation News Module but I would like to use view discovery instead of view injection for the ArticleView.
My first approach was to use the IRegionManager.RegisterViewWithRegion method instead of the IRegion.Add method in the NewsController class but the result is that ArticleView and ArticlePresentationModel are instancied twice and the view doesn't respond anymore...

My sensation is that the conception of a module strongly depends on the strategy you will choose to add views to regions, reducing the concept of modularity. In this example, what would be the changes to make this module working with view discovery ?

Regards 10/29/2009 10:29 AM | Sebastien ORGEAS

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Sebastien, I'll need some time to look into your question a little more. I won't be able to look at it this weekend, but I'll try to get back to you middle of next week. 10/31/2009 6:59 AM | MarkJMiller

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Hi Mark,

I found an implementation : keeping the NewsController class only managing the NewsReaderPresenter and registering the ArticleView in the NewsModule class.

In this implementation, the NewsController class has no Run method anymore and is no more "ContainerControlledLifetimeManaged" because it is injected during the construction of the ArticlePresentationModel. The subscription to the TickerSymbolSelectedEvent is also declared by the ArticlePresentationModel.

Regards 10/31/2009 12:38 PM | Sebastien ORGEAS

# re: MVVM with Prism 101 – Part 3b: View Injection and the Controller Pattern

Gravatar Great Work. I think you should use more VARS 11/9/2009 4:05 PM | Jason Bunting

Comments have been closed on this topic.
 

 

Copyright © Mark J. Miller