Adding activity event status to a workflow image

Yesterday I showed in my previous blog post the minimal code required to create an image from a workflow definition. As I mentioned the most interesting reason for doing so is showing the status of an executing workflow in a sort of monitoring web site. So the next step is to add the ExecutionStatus of each activity to the image. The following image is an example of the result.


 




 


This can be done using an IDesignerGlyphProvider in the WorkflowView type. The WorkflowView is already equipped with a IDesignerGlyphProviderService service to with we need to add our IDesignerGlyphProvider implementations. As we need to use the protected GetService to get a reference to the IDesignerGlyphProviderService we need to create a subclass of WorkflowView and add some code.


 


Imports System.Workflow.ComponentModel.Design
 
Class GlyphProvidedWorkflowView
    Inherits WorkflowView
 
    SubNew(ByVal serviceProvider As IServiceProvider)
        MyBase.New(serviceProvider)
    EndSub
 
    PublicSub AddGlyphProvider(ByVal provider As IDesignerGlyphProvider)
        Dim service As IDesignerGlyphProviderService
        service = GetService(GetType(IDesignerGlyphProviderService))
        service.AddGlyphProvider(provider)
    EndSub
EndClass


 


The next step is to create the IDesignerGlyphProvider implementation itself. Again no big deal. In this example I decided to use the Workflow SqlTrackingQuery to retrieve an actual SqlTrackingWorkflowInstanceinstead instead of a dummy workflow created on the spot. Using this SqlTrackingWorkflowInstance gives me the history of the activity events to display. After all that is wat we want in a monitoring web site. The class I created is DesignerGlyphProvider and it contains a function to add the activity event and the GetGlyphs as gefined by the IDesignerGlyphProvider interface. Again the code is no big deal [:)]


 


Imports System.Workflow.ComponentModel.Design
Imports System.Workflow.Runtime.Tracking
Imports System.Workflow.ComponentModel
 
PublicClass DesignerGlyphProvider
    Implements IDesignerGlyphProvider
 
    Private _activityExecutionStatus AsNew Dictionary(OfString, ActivityExecutionStatus)
 
    PublicFunction GetGlyphs(ByVal activityDesigner As ActivityDesigner) As ActivityDesignerGlyphCollection Implements IDesignerGlyphProvider.GetGlyphs
        Dim result AsNew ActivityDesignerGlyphCollection()
        SyncLock _activityExecutionStatus
            If _activityExecutionStatus.ContainsKey(activityDesigner.Activity.QualifiedName) Then
                Dim status As ActivityExecutionStatus = _activityExecutionStatus(activityDesigner.Activity.QualifiedName)
                SelectCase status
                    Case ActivityExecutionStatus.Executing
                        result.Add(New ExecutingStatusGlyph())
                    Case ActivityExecutionStatus.Closed
                        result.Add(New ClosedStatusGlyph())
                    Case ActivityExecutionStatus.Faulting
                        result.Add(New FaultingStatusGlyph())
                    Case ActivityExecutionStatus.Canceling
                        result.Add(New CancelingStatusGlyph())
                EndSelect
 
            EndIf
        EndSyncLock
        Return result
    EndFunction
 
    PublicSub AddActivityEvents(ByVal trackingRecords As IList(Of ActivityTrackingRecord))
        SyncLock _activityExecutionStatus
            _activityExecutionStatus.Clear()
 
            ForEach record As ActivityTrackingRecord In trackingRecords
                If _activityExecutionStatus.ContainsKey(record.QualifiedName) Then
                    _activityExecutionStatus(record.QualifiedName) = record.ExecutionStatus
                Else
                    _activityExecutionStatus.Add(record.QualifiedName, record.ExecutionStatus)
                EndIf
            Next
        EndSyncLock
    EndSub
EndClass


 


Of course we need the glyphs to draw on the design surface. I kept things really simple and just used the Winding font to write an appropriate character for each status.


 


Imports System.Drawing
Imports System.Workflow.ComponentModel.Design
 
PublicClass StatusDesignerGlyph
    Inherits DesignerGlyph
 
    Protected _text AsString = “”
    Protected _brush As Brush = Brushes.Black
 
    ProtectedOverridesSub OnPaint(ByVal graphics As Graphics, ByVal activated AsBoolean, ByVal ambientTheme As AmbientTheme, ByVal designer As ActivityDesigner)
        Using font AsNew Font(“Wingdings”, 12)
            graphics.DrawString(_text, font, _brush, designer.Bounds)
        EndUsing
    EndSub
EndClass
 
 
PublicClass ClosedStatusGlyph
    Inherits StatusDesignerGlyph
 
    SubNew()
        _text = “J”
        _brush = Brushes.Green
    EndSub
EndClass
 
PublicClass ExecutingStatusGlyph
    Inherits StatusDesignerGlyph
 
    SubNew()
        _text = “F”
    EndSub
EndClass
 
 
PublicClass FaultingStatusGlyph
    Inherits StatusDesignerGlyph
 
    SubNew()
        _text = “N”
        _brush = Brushes.Red
    EndSub
EndClass
 
 
PublicClass CancelingStatusGlyph
    Inherits StatusDesignerGlyph
 
    SubNew()
        _text = “I”
        _brush = Brushes.Red
    EndSub
EndClass


 


That only leaves us with the main function that was slightly updated from yesterday. Of course I added the SqlTrackingQuery to load an actual SqlTrackingWorkflowInstance object. Next I changed the standard WorkflowView used to the custom GlyphProvidedWorkflowView type. The final addition is adding the DesignerGlyphProvider to the GlyphProvidedWorkflowView.


 


Imports System.ComponentModel.Design
Imports System.ComponentModel.Design.Serialization
Imports System.Drawing.Imaging
Imports System.Workflow.Activities
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Design
Imports System.Workflow.Runtime.Tracking
 
Module Module1
    Sub Main()
        Dim query AsNew SqlTrackingQuery(“Data Source=.\sqlexpress;Initial Catalog=WF_Tracking;Integrated Security=True”)
        Dim options AsNew SqlTrackingQueryOptions
        Dim instances As IList(Of SqlTrackingWorkflowInstance) = query.GetWorkflows(options)
        Dim instance As SqlTrackingWorkflowInstance = instances(instances.Count – 1)
        Dim workflow As Activity = instance.WorkflowDefinition
 
        Dim loader AsNew WorkflowLoader(workflow)
        Dim surface AsNew DesignSurface
        surface.BeginLoad(loader)
        Dim view AsNew GlyphProvidedWorkflowView(CType(surface, IServiceProvider))
 
        Dim glyphProvider AsNew DesignerGlyphProvider()
        glyphProvider.AddActivityEvents(instance.ActivityEvents)
        view.AddGlyphProvider(glyphProvider)
 
        view.SaveWorkflowImage(“workflow.png”, ImageFormat.Png)
 
        Process.Start(“workflow.png”)
    EndSub
EndModule


 


Enjoy!


 


Update: Continued here.




8 thoughts on “Adding activity event status to a workflow image

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>