Wednesday, February 20, 2008

This is an intermezzo from the MVC with WF series. I have added a new sample to the project, which I hope demonstrates the flexibility of using WF.

The rest of this series can be found here:

  • 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

    It is a simple 4 screen 'wizard' where logic determines that it should skip one screen. Also, when the save button is hit, a popup will show that asks if you are sure. If you are, you will be sent to the first screen, otherwise you will return to your last screen. It has buttons on the left that determine where you can go, as well as 'next' and 'previous' buttons.
    All of this was done with a minimum of code and a maximum of dragging and dropping activities. The whole reason for doing this, is that when you now get a new feature request ("We have a new screen that sits in between the client and adres screen!!!"), using WF it will be dead-simple to add it.

    I have uploaded the executable here, just in case you don't feel like opening up the project and building yourself.
    The application looks like this:

    image

    And when you reach the 'Car' screen, it will look like this:

    image

    Hitting the Save button here:

    image

    A few things to notice about this sample:

    • There are 2 controllers doing their job here:
      • The usersettings controller, with a view on top. It allows you to check a checkbox. Doing so makes you an administrator. Notice how, when you do so, you are able to browse to the 'Role' screen. You see, if you are not an administrator, you are not allowed to enter the role screen.
      • The 'ImportantWizardController', which handles the mainview. It shows a few buttons (Client, Adres, Role and Car) on the left, which will allow you to go to the screens you have already passed. It also shows a previous and next screen button. Finally, it defines a contentpresenter where our subviews will be injected.
    • The buttons react immediately. Go to the Car screen, and then check your checkbox to make yourself Administrator. This means you have the right to visit the Role screen, and it immediately pops up.
    • No code behind. Nowhere. The only codebehind is on the ImportantWizardControl to 'load data' (actually returning an empty client, but you get the drift).
      It felt really cool to build this plumbing without coding.

    Let's look at the steps to produce this application:

    1. I added a Controller project (type workflow), a Domain project (with a few very simple classes), a shell project that will be used to start us up and a view project which holds the views we are going to use:
      image
    2. I created a ClientService and a UserService class which will be classes used by our workflows:
          [Serializable]
          public class UserService
          {
              public bool IsAdministrator { get; set; }

          }
          [Serializable]
          public class ClientService
          {
              public Client CurrentClient { get; set; }

          }
    3. Then the shell was used to inject our main view and also inject a global userservice class:

        1 <Window x:Class="EditLogicShell.Window1"
        2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        4     xmlns:logic="clr-namespace:EditLogicControllers;assembly=EditLogicControllers"      
        5     xmlns:c="clr-namespace:ControllersAdapters;assembly=ControllersAdapters"
        6     Title="Window1" Height="300" Width="300">
        7     <Window.Resources>
        8         <logic:UserService x:Key="globalUserService" />
        9     </Window.Resources>
       10     <StackPanel>
       11         <Border BorderThickness="1" BorderBrush="Black" Background="Beige">
       12             <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:ManageUserSettingsController}" />
       13         </Border>
       14         
       15         <c:GenericWorkflowAdapter WorkflowController="{x:Type logic:ImportantClientWizard}" />
       16     </StackPanel>
       17 </Window>

      Note line 8, where we place a UserService instance in the resources section
      On Line 12 we start our UserSettings controller
      On Line 15, we start our main wizard.

    4. Let's not go into the usersettings controller. It's just too simple. It will inject a view, and set the datacontext to the userservice class. He retrieved that class using the 'RetrieveObjectFromResources' activity.

    5. I then asked our designer (yup, that was me too... could you tell??) to design our individual views. Everywhere the designer knew he had to interact with the system, a command was created in a static class. That class turned out to be like this:

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


              }
          }
      And on individual screens, the commands were used like this:
                  <Border Background="Beige" BorderThickness="1" BorderBrush="Black" DockPanel.Dock="Left" Width="120" >
                      <ListView>
                          <Label FontWeight="Bold">Previous screens</Label>
                          <Button Command="{x:Static local:ImportantWizardInteractions.GotoClientScreen}">Client</Button>
                          <Button Command="{x:Static local:ImportantWizardInteractions.GotoAdresScreen}">Adres</Button>
                          <Button Command="{x:Static local:ImportantWizardInteractions.GotoRoleScreen}">Role</Button>
                          <Button Command="{x:Static local:ImportantWizardInteractions.GotoCarScreen}">Car</Button>
                      </ListView>
                  </Border>
      (This is the list of buttons that are shown on the left hand side of the screen)
    6. The views were passed to the developer (guess who) and the following state machine was created:
      image 

      Obviously a thing of beauty.

      1. The state initialization will retrieve the userservice, load data, set our maincontent to the mainView and set our next state to clientDetails

      2. The clientdetail has an initialization as well: it will set the datacontext to our customer and inject the ClientView as a datatemplate. Then it waits for only one command: Next. If that is triggered, it will simply move to the AdresDetails State.
        When moving out of a state, the state finalization is triggered, which will remove the view from the resources.
        Note how great it is never to have to think about that cleanup code again, it is always executed when moving out of a state.

      3. The adresDetails state has a few more commands it will listen to. When moving 'Next', a piece of logic is executed:
        image
        There is a declerative rule in the IF/ELSE that goes a little something like this: this.GlobalUserService.IsAdministrator == True
        That rule is automatically put in the rules repository and can be used by others. It determines if the next screen will be the Role screen or the CarDetails screen.

      4. Role is simple.

      5. CarDetails also reacts to the Save-command. When it get's triggered, it will inject our popup into the resources section, and move on to the save state.

      6. The Save state will react to 'SaveYes' and 'SaveNo'. It will remove the popup from the resources, and go to another state.

    7. The views are dead simple, just binding to properties. However, the ImportantWizardMainView does require our attention. It has this definition for our subview:

                  <!-- our subview, uses name: CurrentWizardScreen -->
                  <ContentPresenter 
                  Content="{Binding RelativeSource={RelativeSource FindAncestor, 
                  AncestorType={x:Type local:ImportantWizardMainView}, AncestorLevel=1}, Path=DataContext}" 
                  ContentTemplate="{DynamicResource CurrentWizardScreen}" />


      Apparently, when using the contentTemplate, the datacontext is not inherited. So I have to set it up to react to the changing datacontext of our main screen. So, when the DataContext of ImportantWizardMainView is changed, the Content of our presenter is changed to match it. (Leave comment on how I could do this simpler, if you know how).

    8. Also interesting is that I used a Grid on that view, with two children that overlay eachother. The other child is our popup screen:

              <!-- our popup lives on top of that -->
              <ContentPresenter ContentTemplate="{DynamicResource PopupScreen}" />

      When we set a datatemplate with name Popupscreen, it will be shown on top of our regular screen. I like it!

    I have added a new activity, InjectViewAsDataTemplate. We already had the InjectControllerAsDataTemplate, but there are times you don't want a whole controller.

    I've replaced the original project file with the most recent. It can be found here.
    If you are interested in seeing more about this subject, please leave a short comment!

    kick it on DotNetKicks.com

  • Wednesday, October 22, 2008 6:18:52 PM (Romance Standard Time, UTC+01:00)
    This is certainly a commendable effort. I just stumbled across this today, and found out that using WF as a controller is something that I never thought about. Please continue to share your ideas. I would be interested to know more, and to compare this with the other (more traditional) approaches.
    George
    Thursday, March 18, 2010 12:26:31 PM (Romance Standard Time, UTC+01:00)
    Nice design but I doubt how many developers will understand this. Some times it gets very hard to maintain a very intelligent design with average resources. I never understood the WPF style Binding etc.

    Pinakin
    Monday, May 24, 2010 9:12:27 AM (Romance Standard Time, UTC+01:00)
    cheap mbt shoes online seller.
    Tuesday, September 28, 2010 2:39:47 AM (Romance Standard Time, UTC+01:00)
    http:///www.coachusabags.com
    Tuesday, September 28, 2010 2:40:14 AM (Romance Standard Time, UTC+01:00)
    http://www.mbt-usa.com
    Tuesday, September 28, 2010 2:40:39 AM (Romance Standard Time, UTC+01:00)
    http://www.ukuggus.com
    Saturday, October 23, 2010 2:01:41 AM (Romance Standard Time, UTC+01:00)
    href="http://www.feetlockers.com/specials.html">Discount Reebok zigtech</a> trouble getting out of their own way.The folks who are not succeeding are the ones whose first question is “where do I find a good deal?” Early stage syndicators think they need to find a great deal, then raise capital, and they will be on their way toward success and profit. I usually counter that with some tough love and a reality check. Instead of looking for deals and Reebok shoes, a prudent <a href="http://www.feetlockers.com/reebok-easytone-reebok-zig-pulse-running-shoes-c-25_26.html">Reebok EasyTone</a> syndicator needs to put the horse in front of the deal, and start with the question of how to raise capital.
    Saturday, October 23, 2010 2:02:44 AM (Romance Standard Time, UTC+01:00)
    href="http://www.zigtechshoesonsale.com/mbt-men-shoes-mbt-sandals-shoes-c-8_11.html"><strong>Mbt shoes clearance</strong></a>, is demanding.However, they are not only for people on their feet but for anyone who wants to wear a pair of comfortable <a href="http://www.zigtechshoesonsale.com/mbt-women-shoes-mbt-sports-shoes-c-15_26.html"><strong>Mbt shoes outlet</strong></a> that takes the pain away from your body when you are standing and walking. That’s why you need to wear <a href="http://www.zigtechshoesonsale.com/mbt-men-shoes-mbt-casual-shoes-c-8_9.html"><strong>MBT Sandals Shoes</strong></a>.
    Tuesday, October 26, 2010 6:45:15 AM (Romance Standard Time, UTC+01:00)
    Nice article, i would like to tell all of my friends about it. By the way, i would like to introduce everyone of you a very nice website, it offers cheap replica rolex paypal for men and women. Such as rolex replica,
    rolex replica watch,
    replica rolex watch,
    fake rolex watch,
    fake watches .You can find almost all the replica Rolex series there. They have Latest style and classic style. Though their price
    are low,don’t worry about it’s quality.Every detail of replica rolex shows its original marvelous designs and the best imitation
    technology. I have bought from them for so many times, and very satisfied with the their goods and service. Come on, choose one you love it.

    http://www.excelwatch.com/9-replica-omega-watches omega watches
    http://www.excelwatch.com/10-replica-tag-heuer-watches tag heuer watches
    Thursday, October 28, 2010 3:38:08 PM (Romance Standard Time, UTC+01:00)
    http://www.mbt-usa.com/mbt-chapa-c-4.htm
    Monday, November 01, 2010 8:14:30 AM (Romance Standard Time, UTC+01:00)

    I am happy to find this post very useful for me, as it contains lot of information. I always prefer to read the quality content and this thing I found in you post. Thanks for sharing
    Saturday, November 13, 2010 1:49:01 AM (Romance Standard Time, UTC+01:00)
    It’s good to see you posting on this topic, I need to bookmark this web site.
    Tuesday, November 16, 2010 9:20:42 AM (Romance Standard Time, UTC+01:00)
    A wide range of selection ugg uk online for you.http://www.uggsalebootsuk.com/
    Monday, November 22, 2010 5:53:10 AM (Romance Standard Time, UTC+01:00)
    Ugg Australia story began in 1978, when Brian Smith, a young surfer from Australia, took a trip to the United Sates with a bag full of sheepskin boots - <a href="http://www.uggsalebootsuk.com">UGG Boots</a>.Nowadays <a href="http://www.uggsalebootsuk.com">Ugg sale</a> is the best popular sheepskin boots loved by people from all over the world.<a href="http://www.uggsalebootsuk.com/products">Ugg sale boots</a> are traditionally made from the australia finest twinface sheepskin.The unique design makes .<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products">Ugg uk boots</a> extremely warm especially in cold weather.A good pair of <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=1">new style ugg boots</a> will be the most comfortable footwear on your feet..If your feet cold, it is easy to lead to disease.So please put on the <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=2">new uggs</a> to keep your feet warm and keep healthy!Keep in the latest fashion from our great selection of <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=3">Ugg new products</a>.<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/ugg-bailey-button-5803">UGG bailey button</a>,<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/ugg-classic-tall-5815">UGG classic tall</a>,<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/ugg-classic-short-5825">ugg classic short</a>,<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/ugg-fringe-cardy-boot-1878">ugg cardy</a>,<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/ugg-classic-mini-5854">ugg classic mini</a> A wide range of selection ugg uk online for you.

    Tuesday, November 23, 2010 2:21:24 AM (Romance Standard Time, UTC+01:00)
    Days are getting colder.Are you looking for a pair of great boots to keep your feet warm?The answer for you is ugg boots.boots cheap sale</a>?Here is a great <a href="http://www.uggonlinebootssale.com">ugg boots store</a> for you now.We provide you <a href="http://www.uggonlinebootssale.com/ugg-boots-classic-crochet-5833">cheap ugg boots on sale</a>.A great selection of <a href="http://www.uggonlinebootssale.com/ugg-classic-short-5825">cheapest ugg boots sale</a> for you.What are you waiting for?Do not miss the chance,find your <a href="http://www.uggonlinebootssale.com/products">ugg boots cheap online</a> here.<a href="http://www.uggonlinebootssale.com/ugg-boots-nightfall-5359">UGG boots on sale cheap</a>.<a href="http://www.uggsalebootsuk.com">ugg sale uk</a>.<a href="http://www.fashionuggboots.co.uk">ugg sale</a> here.
    Saturday, November 27, 2010 5:49:15 AM (Romance Standard Time, UTC+01:00)
    Ugg Australia story began in 1978, when Brian Smith, a young surfer from Australia, took a trip to the United Sates with a bag full of sheepskin boots - <a href="http://www.uggsalebootsuk.com">UGG Boots</a>.Nowadays <a href="http://www.uggsalebootsuk.com">Ugg sale</a> is the best popular sheepskin boots loved by people from all over the world.<a href="http://www.uggsalebootsuk.com/products">Ugg sale boots</a> are traditionally made from the australia finest twinface sheepskin.The unique design makes .<a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products">Ugg uk boots</a> extremely warm especially in cold weather.A good pair of <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=1">new style ugg boots</a> will be the most comfortable footwear on your feet..If your feet cold, it is easy to lead to disease.So please put on the <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=2">new uggs</a> to keep your feet warm and keep healthy!Keep in the latest fashion from our great selection of <a href="http://www.uggsalebootsuk.com/ugg-sale-catalog/new-products?page=3">Ugg new products</a>.<a
    Saturday, December 04, 2010 2:52:49 AM (Romance Standard Time, UTC+01:00)
    this thing is worth to read. we enjoy it.
    <a href="http://www.salemonclerstore.com/">discount moncler coats</a>
    <a href="http://www.salemonclerstore.com/">cheap moncler vest</a>
    <a href="http://www.designerbagsonsale.net/">designer handbags wholesale</a>
    <a href="http://www.designerbagsonsale.net/">cheap designer handbags</a>
    <a href="http://www.ghdchiirons.com/">ghd flat irons</a>
    <a href="http://www.ghdchiirons.com/">discount ghd</a>
    Saturday, December 04, 2010 2:53:10 AM (Romance Standard Time, UTC+01:00)
    It sounds well for this paper.
    <a href="http://www.ugg-classic-boots.us/">uggs classic short</a>
    <a href="http://www.ugg-classic-boots.us/">uggs classic</a>
    <a href="http://www.uggadirondackboot.com/">ugg adirondack</a>
    <a href="http://www.uggadirondackboot.com/">ugg adirondack tall</a>
    <a href="http://www.cheapuggbootstore.com/">cheap uggs</a>
    <a href="http://www.cheapnorthfaceoutlet.com/">cheap north face jackets</a>
    <a href="http://www.cheapnorthfaceoutlet.com/">north face outlet</a>
    <a href="http://www.cheapnorthfaceoutlet.com/">north face sale</a>
    Comments are closed.