January 2010 - Posts
During last years CodeCamp in Rotterdam I recorded my presentation on doing iPhone development using jQTouch and ASP.NET MVC. After looking at several options I have posted the recording on Vimeo. You can view the Dutch language recording here.
If you are interested in doing iPhone development but don’t want to get started using Objective-C that jQTouch might just be what you need. Its a cool way to build HTML 5 and JavaScript application for the iPhone. And, thanks to HTML 5, it will even let you take the application and data offline if you want that. Best of all you can deploy them without going through the Apple AppStore if you prefer not to. And if you still want to go the AppStore route a product like PhoneGap will even let you do that.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Note: This blog post is written using the .NET framework 4.0 Beta 2
In this previous blog post I showed how to create an asynchronous activity using the NativeActivity and CreateBookmark to pause a workflow execution. Using that in a WorkflowApplication was easy but what about WorkflowInvoker or WorkflowServiceHost?
So what about using a WorkflowInvoker or a WorkflowServiceHost?
Neither the WorkflowInvoker not the WorkflowServiceHost contain a ResumeBookmark function so how do we resume a bookmark using either of those execution hosts? The trick is to use a IWorkflowInstanceExtension.
Using a IWorkflowInstanceExtension
Workflow extensions can be of any type you want, there is no base class or interface requirement. But that also means they are not aware of the workflow runtime environment and can’t do much more that respond to calls from a workflow. The way do be able to do a little more is by implementing the IWorkflowInstanceExtension interface. This interface has just two methods. One of those, the SetInstance, is passed a wrapper object around the running workflow allowing us do do some more work. The most important is that this also lets us resume bookmarks.
My workflow extension basically waits for the activity to call it and once done wait one second and resume the bookmark. A
class MyExtension : IWorkflowInstanceExtension
{
private WorkflowInstanceProxy _instance;
public IEnumerable<object> GetAdditionalExtensions()
{
return null;
}
public void SetInstance(WorkflowInstanceProxy instance)
{
_instance = instance;
}
public void WaitSome(Bookmark bookmark)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(1000);
var ias = _instance.BeginResumeBookmark(bookmark, 42, null, null);
var result = _instance.EndResumeBookmark(ias);
Console.WriteLine("BookmarkResumptionResult: '{0}'", result);
});
}
}
Note that the GetAdditionalExtensions() function just returns null as we are not adding extra extensions.
The new version of the activity
With the required extension done we still need to make sure it gets added to the workflow runtime of choice. The easiest option is to let the activity itself do so in the CacheMetadata() function by using the AddDefaultExtensionProvider() function. The new activity looks like this:
public class MyBookmarkedActivity : NativeActivity
{
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
metadata.AddDefaultExtensionProvider<MyExtension>(() => new MyExtension());
}
protected override bool CanInduceIdle
{
get { return true; }
}
protected override void Execute(NativeActivityContext context)
{
var bookmark = context.CreateBookmark("MyBookmark", BookmarkResumed);
var extension = context.GetExtension<MyExtension>();
extension.WaitSome(bookmark);
}
private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)
{
Console.WriteLine("Bookmark resumed with '{0}'.", value);
}
}
Now this is all we need to run the activity as it will automatically add the required extension:
WorkflowInvoker.Invoke(new MyBookmarkedActivity());
or when using a WorkflowServiceHost
var wsh = new WorkflowServiceHost(new MyBookmarkedActivity());
wsh.AddDefaultEndpoints();
wsh.Open();
Console.WriteLine("Listening");
Console.ReadLine();
wsh.Close();
And just in case you where wondering, neither of these last two use any configuration file.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Note: This blog post is written using the .NET framework 4.0 Beta 2
Windows Workflow Foundation 4 introduces the concept of bookmarks to temporarily pause activities. A bookmark is basically a named pause point in an activity. The result is that the workflow runtime doesn’t consider an activity to be finished until all its bookmarks are either resumed or removed.
That last statement isn’t completely true though as it is only the case when the optional BookmarkOptions aren’t specified or specified as None. With BookmarkOptions.MultipleResume a bookmark can be resumed multiple times, de default is just once, so just resuming a bookmark isn’t enough it actually needs to be removed. The other option is BookmarkOptions.NonBlocking which means that the activity is finished even if the bookmark was never resumed. This last option is useful when you want to be able to receive messages while child activities are executing but you don’t need to receive the message per se. The BookmarkOptions enum is a flag so you can combine both NonBlocking and MultipleResume if you so desire.
One annoying thing with using bookmarks is that you need to use a NativeActivity and you have to override the CanInduceIdle property and return True as the default of False doesn’t permit bookmarks to be created, even when the NonBlocking option is used.
public class MyBookmarkedActivity : NativeActivity
{
protected override bool CanInduceIdle
{
get { return true; }
}
protected override void Execute(NativeActivityContext context)
{
var bookmark = context.CreateBookmark("MyBookmark", BookmarkResumed);
}
private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)
{
Console.WriteLine("Bookmark resumed with '{0}'.", value);
}
}
Running the workflow and resuming a bookmark is easy when using a WorkflowApplication as seen below.
var wa = new WorkflowApplication(new MyBookmarkedActivity());
wa.Run();
var data = Console.ReadLine();
wa.ResumeBookmark("MyBookmark", data);
Console.ReadLine();
Nice and easy right?
Well yes except we also have WorkflowInvoker and WorkflowServiceHost to host our activity and neither contains a ResumeBookmark function. More about that later.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu
Note: This blog post is written using the .NET framework 4.0 Beta 2
When I create custom activity designers the icon that appears is usually one of the first things I want to change. Doing so in WF4 isn’t hard once you know where to look but if you don’t can be a bit of a challenge.
Suppose I have a simple write line activity like this:
[Designer(typeof(MyWriteLineDesigner))]
public sealed class MyWriteLine : CodeActivity
{
// Define an activity input argument of type string
public InArgument<string> Text { get; set; }
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
string text = context.GetValue(this.Text);
Console.WriteLine(text);
}
}
I have used the Designer attribute to attach my own designer to this activity.
The standard designer looks like this:
<sap:ActivityDesigner x:Class="WorkflowConsoleApplication3.MyWriteLineDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation">
<Grid>
</Grid>
</sap:ActivityDesigner>
And dropping that on a workflow designer surface results in the following:
So far so good except now I want to change the icon that appears at the top left of the designer.
The designer has an Icon property. Unfortunately the property sheet doesn’t do us a lot of good here.
In order to change the icon we need to add some XAML to the designer telling it what to draw. The new designer looks like this
<sap:ActivityDesigner x:Class="WorkflowConsoleApplication3.MyWriteLineDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation">
<sap:ActivityDesigner.Icon>
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0" Size="16,16" ></Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="Clipboard_edit.png" ></BitmapImage>
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</sap:ActivityDesigner.Icon>
<Grid>
</Grid>
</sap:ActivityDesigner>
Inside of the sap:ActivityDesigner.Icon element we need to add a DrawingBrush using an BitmapImage pointing to a file. The file, Clipboard_edit.png in this case, has been added to the project and its Build Action has been changed to “Resource”.
Once this has been done and the project rebuild the new icon will show up on the design surface.
Sweet 
The topic of Visual Basic expression in Windows Workflow Foundation 4 seems to be a bit of an item for some people as you can see in the comments to my blog posts about it here. Now personally I don’t think it is such a big deal as it only concerns the expressions entered in the workflow, the main project can still be C# if you so desire.
Anyway the team responsible at Microsoft have created a survey asking for feedback on the subject. So if you have an opinion, and many seem to, please take 5 minutes go here and complete survey.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu