Introducing the Pageflow Sample

  • Most people think of workflows as a tool to represent and automate back-end business processes. Back-end business processes normally require some user interaction but their main purpose is not to drive the user experience or manage the UI. However, there is a growing type of application that leverages workflow as a tool to drive the user interaction and drive the user experience of an interactive process. This type of technology is called page flow.

  • Last year at TechEd, we showed off some bits we had been working on internally that were designed to make that possible, the ability to model the user interaction of an application using workflow. This approach provides developers the ability to continue managing the complexity of their application in a structure and scalable manner. It turned out that the code we showed at TechEd wasn't going to end up in any of the product releases, so the dev team requested permission to release that code as a sample of how one can implement a generic navigation framework using WF that can support multiple UI technologies (i.e. ASP.NET and WPF).  This year, I just finished giving a talk showing this off and talking about how it will be available today!

  • Thanks go to Shelly Guo, the developer and Israel Hilerio, the PM who had worked on this feature, and to Jon Flanders for providing packaging and quality control

  • Now for the good stuff, download the bits from here!

  • Navigate to setup.exe and run the setup, this will copy the sample projects and the source code for the sample, as well as some new visual studio project templates.

  • Now, let's open up a sample project, so navigate to the samples directory and open the ASPWorkflow sample, this will show off both an ASP.NET Front end as well as a WPF Controller (you can actually use the two together). Let's get to the good stuff right away, and open up the workflow file.

  • Wow… what's going on here? It kind of looks like a state machine, but not really. What has been done here is to create a new base workflow type. Things like SequentialWorkflow and StateMachineWorkflow aren't the only ways to write workflows, they are just two common patterns of execution. A NavigatorWorkflow type has been created (and you can inspect the source and the architecture document to see what this does) and a WorkflowDesigner has been created for it as well (again, this source is available as a guide for those of you who are creating your own workflow types).

  • Each of the activities you see on the diagram above is an InteractionActivity, representing the interaction between the user (via the UI technology of their choosing) and the process. A nice model is to think of the InteractionActivity as mapping to a page within a UI. The output property is the information that is sent to that page (a list of orders or addresses to display) and the input is the information that is received from the page when the user clicks "submit". The InteractionActivity is a composite activity, allowing one to place other activities within the activity to be executed when input is received. The interesting property of the InteractionActivity is the Transitions collection. By selecting this and opening its designer, we are presented with the following dialog:

  • This allows us to specify n-transitions from this InteractionActivity or "page" to other InteractionActivities. And we can specify this via a WF activity condition. This way, we could forward orders greater than $1000 to a credit verification process, or orders containing fragile goods through a process to obtain insurance from a shipper. What's cool about this, my page does not know about that process, it just says "GoForward" and my process defines what comes next. This de-couples the pages from the logic of your process.

  • We then need to wire things up in config:

  •  <configSections>
    <section name="NavigationManagerSettings" 
    type="Microsoft.Samples.Workflow.UI.NavigationManagerConfigSection, Microsoft.Samples.Workflow.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=40B940EB90393A19"/>
    <section name="AspNavigationSettings" 
    type="Microsoft.Samples.Workflow.UI.Asp.AspNavigationConfigSection, Microsoft.Samples.Workflow.UI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=40B940EB90393A19"/>
    </configSections>
    
  •  <NavigationManagerSettings StartOnDemand="false">
      <Workflow mode="Compiled" value="ASPUIWorkflow.Workflow1, ASPUIWorkflow"/>
      <!--<Workflow mode="XOML" value="WebSite/XAMLWorkflow.xoml" rulesFile="WebSite/XAMLWorkflow.rules" />-->
      <Services>
        <add type="System.Workflow.Runtime.Hosting.DefaultWorkflowCommitWorkBatchService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
        <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConnectionString="Initial Catalog=WorkflowStore;Data Source=localhost;Integrated Security=SSPI;" UnloadOnIdle="true"/>
      </Services>
    </NavigationManagerSettings>
    <AspNavigationSettings>
      <PageMappings>
        <add bookmark="Page1" location="/WebSite/Default.aspx"/>
        <add bookmark="Page2" location="/WebSite/Page2.aspx"/>
        <add bookmark="Page3" location="/WebSite/Page3.aspx"/>
        <add bookmark="Page4" location="/WebSite/Page4.aspx"/>
        <add bookmark="Page5" location="/WebSite/Page5.aspx"/>
        <add bookmark="LastPage" location="/WebSite/LastPage.aspx"/>
      </PageMappings>
      <ExceptionMappings>
        <add type="Microsoft.Samples.Workflow.UI.WorkflowNotFoundException" location="/WebSite/ErrorPage.aspx"/>
        <add type="Microsoft.Samples.Workflow.UI.WorkflowCanceledException" location="/WebSite/ErrorPage.aspx"/>
        <add type="System.ArgumentException" location="/WebSite/ErrorPage.aspx"/>
        <add type="System.Security.SecurityException" location="/WebSite/ErrorPage.aspx"/>
      </ExceptionMappings>
    </AspNavigationSettings>
    
  •  

  • Finally, let's look inside an ASP.NET page and see what we need to do to interact with the process:

  •  AspNetUserInput.GoForward("Submit", userInfo, this.User);
    
  • This code is specifying the action and is submitting a userInfo object (containing various information gathered from the page) to the InteractionActivity (in this case, it submits to the Page2 InteractionActivity). If we look at what we've configured as the Input for this InteractionActivity, we see the following, which we can then refer to in the transition rules in order to make decisions about where to go next:

  • Plenty of other stuff we could talk about here, support for back button, persistence, etc and I could continue to ramble on about this in another record-length blog post, but I will stop here for now. I will continue to blog about this, look forward to hearing any and all types of feedback, and what you'd be interested in seeing in this. Moving forward, there aren't any formal plans around this, but if there is enough interest in the community, we could get it created as a project on codeplex. If that sounds intriguing either contact me through this blog, leave a comment so that I can gauge the interest in such a scenario.

  • Go, grab the bits!  And, if you have feedback, please contact me.