Thursday, February 21, 2008

This is the third of a series about using Workflow Foundation to control your UI Logic in a WPF application. The full table of contents:

  • Workflow as controller: Introducing <M,V,C> where M: ViewModel, V : WPF, C : WF
  • Part II, starting the application, and the adapter
  • Intermezzo: new sample application
  • Part III, your first view
  • Part IV, decoupling view from controller
  • Part V, marshalling commands from WPF to WF
  • Part VI, Injecting a controller in a subview / workspace
  • Part VII, IOC on the cheap: injecting and retrieving objects
  • Part VIII, Broadcasting for all to see
  • Recap

    In the first post, the complete solution was presented. I am presenting a solution to use workflow as the controller part in your MVC inspired WPF application. It is inspired on the thought that you do not need complex frameworks, because WPF already gives you great power (routed eventing, resources). So, no IOC is used, no event aggregator etcetera: it's taken care of by WPF and WF, a natural fit.
    The solution is very decoupled and I feel it's a great advantage to be able to visual your control logic.
    In the previous post, I showed a wizard style application.

    This post follows Part II, starting the application and the adapter. In that post we started our shell and explained how the adapter communicates with a workflow instance, how it can react to commands (normal RoutedUI events from wpf controls) and react to events by the command service.

    We will now continue, by looking at a simple view.

    View responsibility

    Let's first look at how we perceive a view in the MVC paradigm.
    A view should be nothing more than the visualization of your data. The only authority it has, is the authority to decide how to represent a piece of data on the screen. That means it should not contain any business logic. Be very strict about this: the responsibility of a view is the visualization of data.

    So, let's take a look at a common scenario where these lines may blur.
    Take a list of products and let's say that if we have a new product-line that has been introduced within the past month, we want to use another background color, to alert our customers to this new hot product.

    We could solve this in our binding perhaps (let's just assume that is easy), but we should not do that. That would mean the view is deciding when a product is new and hot. It should not.
    The only thing the view should do is create the two visual representations of products and use a datatemplate selector to decide which is hot or not. The datatemplate selector could be injected by our controller. Another way to solve this, is for the controller to put this information in the ViewModel itself. Like, add a boolean 'new' which the view uses.

    If you do not do it this way, and you are embedding logic inside of your view, you will quickly end up with scattered logic, never knowing where something is defined. Changing rules becomes hard and your application will break at some point.
    Now, I understand, and have done many times, that sometimes you just do not have the time to do it right. But always remember that in the long run, you will get burned. Try to setup a situation where it is easy to do the right thing, by making it easy to use datatemplate selectors or use the viewmodel.

    View decoupling

    MVC advocates not letting your view have any knowledge whatsoever of the controller. It does this, because tight coupling of the view to the controller will destroy maintainability and flexibility. If you tight couple, you are unable to swap controllers or views. Most importantly, if you couple the view to the controller (by making it call specific methods on the controller), it becomes harder to maintain/refactor.

    There are certainly approaches that do couple view to controller. If you look at the very powerful Caliburn framework, you will see that the framework has 'action messages' that directly call methods on the controller. I have yet to work with that extensively, so I can not be sure, but it feels to me there should be a very explicit layer between view and controller, which defines how the view will communicate with the controller.

    Our goals in this project are to use the tools WPF provides us to communicate with the rest of the system. We do so with Commands.
    A command can be seen as a message that is passed upward (and downward) the visual tree. Since our adapter lives just above the view and is part of the visual tree, it will have the opportunity to react to the command.

    When building a view, you should also explicitly define all the interactions that view expects to have with the outside world. Do that in a static class like so:

        public static class ImportantWizardInteractions
        {
            public static readonly RoutedUICommand Next;
            public static readonly RoutedUICommand Back;

            public static readonly RoutedUICommand GotoClientScreen;
            public static readonly RoutedUICommand GotoAdresScreen;
            public static readonly RoutedUICommand GotoRoleScreen;
            public static readonly RoutedUICommand GotoCarScreen;

            public static readonly RoutedUICommand Save;
            public static readonly RoutedUICommand SaveYes;
            public static readonly RoutedUICommand SaveNo;


            static ImportantWizardInteractions()
            {
                Next = new RoutedUICommand("Next", "Next", typeof(ImportantWizardInteractions));
                Back = new RoutedUICommand("Back", "Back", typeof(ImportantWizardInteractions));

                GotoClientScreen = new RoutedUICommand("GotoClientScreen", "GotoClientScreen", typeof(ImportantWizardInteractions));
                GotoAdresScreen = new RoutedUICommand("GotoAdresScreen", "GotoAdresScreen", typeof(ImportantWizardInteractions));
                GotoRoleScreen = new RoutedUICommand("GotoRoleScreen", "GotoRoleScreen", typeof(ImportantWizardInteractions));
                GotoCarScreen = new RoutedUICommand("GotoCarScreen", "GotoCarScreen", typeof(ImportantWizardInteractions));

                Save = new RoutedUICommand("Save", "Save", typeof(ImportantWizardInteractions));
                SaveYes = new RoutedUICommand("SaveYes", "SaveYes", typeof(ImportantWizardInteractions));
                SaveNo = new RoutedUICommand("SaveNo", "SaveNo", typeof(ImportantWizardInteractions));


            }
        }

    By being explicit about your interactions like this, you will be able to unit test more easily as well.

    Use in your view like this:

    <Button Command="{x:Static local:ImportantWizardInteractions.GotoClientScreen}">Client</Button>

    A command is great for buttons and other stuff, but how do you do for instance communicate that a customer was selected from a listview?

    1. well, you bind to a selectedCustomer property hopefully, and when the customer was selected, that property changed on the viewmodel. The controller might pick that up.
    2. More explicitly though: do use the SelectedItemChanged event and use the codebehind of your view as a translation layer to talk to the outside world:
    3.         private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
              {
                  // send a command
                  CustomerQueueInteractions.SelectNewCustomer.Execute(e.AddedItems, this);
              }

    You can call me on that. It's not a very elegant solution. I'd rather be able to do away with the codebehind of a view entirely. But using the codebehind is actually fine: it is part of the view, and it should not be allowed to do anything else than to act as a translator for view specific things to commands.

    So, how to show a view

    Well, building a view is nothing else than just deriving from usercontrol and doing your thing. Using commands and going wild on the visuals. (try to animate everything!!! your client loves it).

    It depends now how you want to show it.

    1. Let's say your building a project where you don't care about fancy composition and pluggable modules in your application, and you just want your shell to show your view. The shell might have the following code:
      <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:AControllerForYourView}" >
         
      <v:YourView/>
      </c:GenericWorkflowAdapter>

      I am assuming you do want a controller around your view.

      Here a controller is instantiated and it's content is set to your view. Easy.
    2. Let's say we want our controller to choose what view it uses. That seems to me to be the nicest way to go about it. We will again put a controller in the visual tree, but will not set a view already:

      1. <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:ImportantClientWizard}" />

      Then, in the workflow, we might use some fancy logic to determine which view we will show (perhaps looking at the role of the user). To actually set a view, we will use the SetMainContentActivity. Drag that to the canvas and select a type.

      image

      Selecting a type is easy, because of the typebrowser I included:
      image 

    3. Yet another way, that is suitable for 'subviews', is to define a contentpresenter on some view anywhere:
    4. <ContentPresenter 
      ContentTemplate="{DynamicResource CurrentWizardScreen}" />


    And use the InjectViewAsDataTemplate activity in a controller to place a contenttemplate in the resources section with the same resourcekey.
    image

     

    I'll follow up with another take on decoupling the view from the controller, by looking at the SetDataContext activity and talking a bit more about the viewmodel.

    Thursday, February 21, 2008 5:49:24 PM (Romance Standard Time, UTC+01:00)
    Very interesting articles. I'm not at all familiar with WF, so to really understand all of this I have some further reading to do. But the ideas are interesting.

    You mention Caliburn, and warn that coupling the View and the Controller is something to be avoided and that you think Caliburn fails to do this. This brings up a lot of things to discuss. Which components should be decoupled, and how much should the be decoupled? In MFC, for example, the View and the Controller are so tightly coupled, they're the same object (Document-View pattern). You're approach is probably more classic MVC, but I'm not certain that Caliburn is any more coupled. The action messages specify a Command or Event, and a method name, then through reflection creates the appropriate event/command bindings. You can easily swap out one controller for another, provided the new controller contains the same methods. There's pros and cons to both designs, but I don't think you can make the claim that tight coupling is a con for a Caliburn style design.

    I'm working on a MVC "framework" for WPF myself, so all topics on the subject are quite interesting to me. At some point, I think it might be beneficial to get like minded folks together to DESIGN a framework by evaluating all the various approaches we've found.
    Thursday, February 21, 2008 6:05:35 PM (Romance Standard Time, UTC+01:00)
    thanks!

    Please note that the blog series is not presenting a framework. It just tries to show how you can achieve a loosely coupled design using wpf and wf features.
    I mention Caliburn like this: "I have yet to work with that extensively, so I can not be sure, but it feels to me there should be a very explicit layer between view and controller, which defines how the view will communicate with the controller.".
    So I do not I make the claim that Caliburn it too thightly coupled, I may hint in that direction though ;-)

    First I want to mention that I think the action messages are a fantastic powerful mechanism, and I'm thinking of doing something like that myself. However, it is thighly coupled when you use reflection to automatically bind to specific methods. Yes, you might be able to swap out a different controller, because it is late binding, but it is pushing something inside a controller. Whereas using wpf commands, you just bubble up through the visual tree and let a controller decide for it self how it wants to react to a message. Kind of like the IOC, DI discussion, I think the latter has the same advantages as using IOC.

    Also, when building the sample applications that go with the solution, I've found it refreshing to build the interaction class, which defines all the interactions a view will have with it's outside world. It makes for some very maintainable and testable code. Time will tell how the action messages approach fares in that respect.
    Ruurd Boeke
    Thursday, February 21, 2008 7:12:13 PM (Romance Standard Time, UTC+01:00)
    I still have to disagree about the action messages being tightly coupled. IoC is viewed as a way to reduce coupling. How does that work? By using an interface. The action messages do the exact same thing, but instead of a static interface, you have late binding. When uses a compile time interface, the other a runtime interface. The only thing "pushed inside a controller" is the interface.

    I think the command design here is cleaner, and it's always good when you can avoid late binding (though one could argue that event/command subscriptions are a form of late binding). But I simply can not accept the premise that action messages cause tight coupling. Coupling, yes, but not tight.

    I also realize your not calling this a framework, but it really is. To work, you need the GenericWorkflowAdapter at a minimum. I'd also consider some of the activities you reference to be essential to the concept here. These constitute a framework to me, even if "mini" and easy enough to code from scratch. Just brain storming, I can see some of the concepts from your GenericWorkflowAdapter as being the basis for a MVC framework that didn't require the use of WF! I realize that's NOT what you're posts are about, but that's precisely what I meant when I suggested like minded folks get together to discuss these ideas.
    Thursday, February 21, 2008 7:26:51 PM (Romance Standard Time, UTC+01:00)
    interesting.

    I think that commands are a distant form of late binding indeed. And I do find them very clean.
    The coupling exists when a runtime error is thrown when there is no controller there to catch your action. But then again, this might be wanted behaviour!

    But, let's not dwell too long on how tight 'tight' is ;-)

    I'm interested in your suggestion that it could be used without WF. Yes indeed, I have been thinking about this, but I did not want to hint at it yet. The main learning point here is that the controller (or the adapter) needs to participate in the visual tree. If you can live with that, then you no longer have the need for a concept like 'action message' that does a translation.
    And if that group of like minded folks would ever meet, I think that is the takeaway.
    Ruurd
    Comments are closed.