Monday, June 25, 2007

Adding icons to custom activities

In a few places I've worked, it seems like some people's jobs pretty much entails selecting and adding icons to development projects. Nice work if you can get away with it and that's what I spent some time doing the other day.

It's actually ridiculously easy to add an icon to a custom activity. In fact it's just the same as adding an icon for any component. Add the image to your project, set its 'Build Action' property to 'Embedded Resource', then add the following attribute to your activity class.

[ToolboxBitmap(typeof(UserActivity), "user.png")]

The icon will then appear in the toolbox and will be shown when the activity is rendered in the workflow designer. It's possible to completely change the rendering of the activity and I may investigate that at a later date, but this simple addition seems to provide the biggest bang for buck.

Sunday, June 24, 2007

Property Snake

I remember Fucked Company appearing on the web around the time of the dot-com bubble bursting so perhaps the appearance of the Property Snake website (along with House Price Crash) is evidence that the property bubble is finally about to burst. That and the more bearish coverage in the press...

Thursday, June 21, 2007

Windows Workflow isn't workflow

Ask anybody who's had any experience of workflow software what it is and they'd likely give you a different answer to the next person you ask. But I'm fairly certain if they took a look at Windows Workflow they'd probably say it's not what they understand workflow to be about.

I'm the same and it's one reason why when I first looked at WF I was a little disappointed. But as time goes on and I play with it more and more I'm realising what a really nice piece of technology it is. In fact I'd say I'm actually very pleased that Microsoft didn't build another workflow system because it very likely wouldn't have met my needs either. There are hundreds of workflow systems out there, all implemented differently from each other and all meeting certain needs and failing to meet other needs.

I guess somebody at Microsoft realised this and rather than producing yet another workflow system (YAWFS for short), they wrote a framework for developing any kind of long-running process you'd like to build on top of it. As far as I'm aware nobody else has tried to come up with anything similar and the outcome is far more impressive than YAWFS. The book 'Essential Windows Workflow' explains it a whole lot better than I can but essentially it's a whole new way of writing software. No longer do you need to worry about what to do when you need to wait for a day before moving onto the next step in your code and it seems like threading issues will be much reduced. I'm almost inclined to think that WF may be the answer to a lot of the multi-threading pain we suffer today and really need to solve PDQ in a world of multi-core processors, although I need to delve deeper to know for sure. If you come from a .NET background, you can reuse those skills in WF, although it has to be said the learning curve can be quite steep. 

And it seems like they've succeeded in their aims. Not only are people using WF for workflow products, they are using it for things that you wouldn't necessarily associate with workflow. In conclusion, I think it rocks.

Wednesday, June 20, 2007

Compiling a XOML workflow

In my last post I talked about validating a XOML workflow. Another way to validate a workflow is to just try and compile it and see what you get back from the compiler. The disadvantage of this approach is the fact you have to start to write things out to file. So I've been validating using the previous method and then just compiling when necessary*. The downside of this approach is the x:Class attribute which can't be present when trying to execute a XOML workflow and must be present when trying to compile it. The simple workaround for this is to add the x:Class attribute before writing the XOML out to a temporary file. Anyway here's the code -

        // copy to a temporary file and add the x:Class attribute
        string tempFileName = Path.GetTempPath() + "temp.xoml";
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xoml);
        doc.DocumentElement.SetAttribute("Class", "http://schemas.microsoft.com/winfx/2006/xaml", WorkflowName);
        doc.Save(tempFileName);
        try
        {
          // Compile the workflow
          WorkflowCompiler compiler = new WorkflowCompiler();
          WorkflowCompilerParameters parameters = new WorkflowCompilerParameters();
          parameters.LibraryPaths.Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
          parameters.ReferencedAssemblies.Add("MyActivities.dll");
          parameters.OutputAssembly = string.Format("{0}.dll", WorkflowName);
          compilerResults = compiler.Compile(parameters, tempFileName);
        }
        finally
        {
          File.Delete(tempFileName);
        }
        
        StringBuilder errors = new StringBuilder();
        foreach (CompilerError compilerError in compilerResults.Errors)
        {
          errors.Append(compilerError.ToString() + '\n');
        }

        if (errors.Length != 0)
        {
          MessageBox.Show(this, errors.ToString(), this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
          compileOK = false;
        }

* Another reason I did it this way was because I implemented the validation piece before I implemented the compilation piece and didn't realise I could just re-use the compiling code...

Tuesday, June 19, 2007

Validating a XOML workflow

If you're using workflow assemblies, there is no need to validate your workflow since the compilation process has already done this for you. But if you're working with XOML files you'll probably want to validate it at some point before deploying it. Unfortunately there doesn't seem to be anything provided by Windows Workflow just to validate a workflow. The workaround is to start up the runtime and load up your XOML and see what errors you get. You can use something like the following (ripped off and modified from a newsgroup posting) -

      WorkflowRuntime workflowRuntime = new WorkflowRuntime();
      workflowRuntime.StartRuntime();

      StringReader stringReader = new StringReader(xomlString);
      XmlTextReader reader = new XmlTextReader(stringReader);
      try
      {
        instance = workflowRuntime.CreateWorkflow(reader);
      }
      catch (WorkflowValidationFailedException exp)
      {

        StringBuilder errors = new StringBuilder();

        foreach (ValidationError error in exp.Errors)
        {
          errors.AppendLine(error.ToString());
        }

        MessageBox.Show(errors.ToString(), "Validation errors");
        retVal = false;
      }

One thing to be aware of is that you can't add the x:Class attribute when you use this technique (which you must add if you want to compile your XOML later on). This Catch-22 can be quite easily solved and I'll talk about it in a later post.

If you're wondering why you'd use XOML, I think it probably makes sense when you're automatically generating your workflows from some other source.

Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))

I've been caught out by this twice now. If you create a new web control by creating a new class and then inheriting from Control (or some other derived class) it will appear in your toolbox, but when you try and put it on a web form, either nothing happens or you'll get the error:

Invalid FORMATETC structure (Exception from HRESULT: 0x80040064 (DV_E_FORMATETC))

This somewhat baffling error message is caused by the fact that when you create a new class Visual Studio 2005 makes it private by default. I'm not sure if this is a good or bad thing, it sure as hell beats the "everything is public, every parameter is passed by reference, every variable is a variant" ethos that used to cause so much horrendous VB code to come into existence. Just a shame the error message doesn't give a clue to the real problem.

Implementing your own workflow type in Windows Workflow

Windows Workflow is extensible in almost every way. But one thing I didn't realise until seeing this example is that it is possible to implement your own workflow type as well. This looks really interesting. Although the state machine workflow models what I want to do pretty well, I'm not too happy with the UI of the designer so I might have to look into this further.

Saturday, June 16, 2007

Validator for a custom activity in Windows Workflow

Last time I talked about adding a property to a custom activity, this time I'll talk about validating the value of that property.

So we have a custom activity called UserActivity with a property called Form and we want to ensure the workflow designer has specified a value for this property. First thing to do is write our validator class, which looks like this - 

  class UserActivityValidator : ActivityValidator
  {
    public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
    {
      UserActivity activity = (UserActivity)obj;
      ValidationErrorCollection validationErrorCollection = base.Validate(manager, obj);

      // Don't validate when the activity is standalone  
      if (activity.Parent == null)
      {
        return validationErrorCollection;
      }
      
      // check Form property has been set
      if (string.IsNullOrEmpty(activity.Form))
        validationErrorCollection.Add(ValidationError.GetNotSetValidationError("Form"));

      return validationErrorCollection;
    }
  }

All we do is override the Validate method, call the base implementation, then check to see if the activity has a parent. This check ensures we don't validate the activity when we are designing and coding the activity itself. The next line does the actual work, using a helper method provided by the ValidationError class. This can be used in the most common type of validation, checking to ensure the property has a value. If that's not what you're doing, create an instance of the ValidationError class instead, using whichever constructor you need. 

Once we've written the class all that remains is to tell WF what class should be used to validate the activity, which we do by adding an attribute to the activity declaration.

[ActivityValidator(typeof(UserActivityValidator))]
public partial class UserActivity: HandleExternalEventActivity

And that's it. Now compiling a workflow assembly using the custom activity should show a compiler error if the Form property has not been set.

Friday, June 15, 2007

Activity properties in Windows Workflow

I initially thought Windows Workflow was horrendously complicated, needlessly so. I've since realised that it's just exceedingly extensible but one problem is that there aren't a great deal of internet resources to search through to find the answer. No surprise I guess since this is pretty new technology. So I've decided to add some little tidbits. I'll present a few very short posts about a very small subset of the functionality, a kind of Windows Workflow for dummies. Given that I'm a dummy myself, I am perfectly placed to do this I reckon. Of course since I'm still learning all about WF, these posts may be incomplete or plain wrong.

So first up, adding a property to a custom activity. Properties in WF don't work like properties in normal .NET classes, they are based around dependency properties, which are properties that can be attached to any class deriving from DependencyObject. All activities inherit from DependencyObject so they can use dependency properties. I believe dependency properties must be used when you want to let your workflow designers be able to bind their properties together. Normal properties would have no way of knowing how to update other bound properties when their value had changed.

What is probably worth mentioning at this point is that WPF also uses dependency properties and dependency objects. They look very similar to the WF ones, but they are defined in a different place (System.Windows for WPF, System.Workflow.ComponentModel for WF) so I can only assume there are some subtle differences between the two.

Anyway, here's some code for a simple WF property.

    public static DependencyProperty FormProperty = DependencyProperty.Register("Form", typeof(string), typeof(UserActivity));
    
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Browsable(true)]
    [Description("The form to be displayed when the user initiates the activity")]
    public string Form
    {
      get
      {
        return ((string)(base.GetValue(UserActivity.FormProperty)));
      }
      set
      {
        base.SetValue(UserActivity.FormProperty, value);
      }
    }

So we have a custom activity called UserActivity with a property called Form. It looks like a normal property except the getters and setters use the GetValue and SetValue methods defined in the DependencyObject class. Beyond that all we need to do is register the dependency property. Once that is done, the property should appear just like any other in your workflow.

Thursday, June 14, 2007

Metastorm BPM 7.5 review

It's been around a while so it's about time I wrote down my thoughts. If you want to see the official line take a look at the Metastorm Press Release, I'm just going to talk about things that interest me, which in general means the core product and any .NET integration pieces.

One of the main reasons I wasn't particularly keen on version 7.0 was the requirement to disable DEP before installing. Although this was rectified in the first service release, installing still required disabling DEP, installing 7.0, re-enabling DEP then installing the service release. Thankfully this has all been fixed in 7.5 and I presume 7.5 can be installed straight over 6.6, although I haven't tried this yet.

I've been running with 7.5 web client against some 6.6 engines to get a feel for it. Although this isn't supported and it does seem to have a few issues, it's pretty useful for testing out the various client-side hacks we've employed in the past. There's not been any major innovation in the web client but it has had a few minor tweaks and I'm very much of the opinion that small things matter. The things I like are

  • the ability to kick off an action without opening the folder, this is a quite a productivity boost.
  • fixed headers on a grid. I'm sure it took an afternoon to implement but it will please many many people.
  • the error message when a required field has not been populated tells you the name of the field. Again, a minor change but shows someone is paying attention to the details

You can also add links from the web client to other sites. It's seem a bit of a random feature but I'm sure some people will love it.

The Designer on the other hand seems somewhat unloved, which is understandable given Metastorm have started on a complete rewrite of it. There aren't really any new features to speak of (except the .NET event delegation which I'll talk about shortly). There are some serious performance issues when using it, it seems to flicker constantly when moving around and adding fields to a form takes forever. I've had it crash a few times but I'm not sure it's any less stable than previous versions. The pointless dialog count is about the same, although I did find a new one when it couldn't locate the ELB file in the database so it asked if it should look for an XEL instead. Yes of course you should, do you really need to ask???

So onto the .NET integration. Initially I thought this was pretty poor but upon closer examination it is actually reasonably good. Tell the Designer you want to delegate your map events to a .NET assembly then go into Visual Studio and create an assembly for the map via a wizard. I thought this was the end of it, which left me wondering what would happen when I updated the procedure. It turns out there is some integration with Visual Studio but the installer didn't activate the add-in. Once I had that running, I was able to synchronise the procedure and assembly and deploy it.

There's a few problems with this integration though

  • not all events are covered. When button pressed and when the user selects a row in a grid are the obvious ones, but calculated fields and basically all places where e-Work formulae are used need to be available.
  • deploying the assembly requires stopping and starting the engine. Oddly, Metastorm have worked around this by stopping and starting the engine for you from Visual Studio rather than going down the more obvious route of using shadow copies. This is fine for a development machine but deployment to a production machine would be much easier without this requirement. ASP.NET has been doing it for years...
  • I think the Process Orchestrator requires a separate license purchase. Really this should be part of the main product and until it is, I don't think we'll be interested in using it.

.NET 3, worst... name... ever...

People have got their fingers burned in the past. Installing .NET 2 did cause some applications to break (OK, I can only think of Metastorm e-Work off the top of my head but most people I deal with are using that bit of software) so every time I tell them to install .NET 3 to use the wonders of Windows Workflow I have to include a disclaimer that .NET 3 isn't really a new version of .NET, it runs on top of .NET 2 so shouldn't break anything blah blah blah. Worst... name... ever...

Monday, June 11, 2007

Browsers getting interesting again

Well interesting is perhaps stretching it, but certainly confusing. IE7 has been out for a long time now and it seems to have stalled at about 30% usage. Then today Apple released a version of Safari for Windows. So now I have yet another browser to worry about, because I'm guessing quite a few people will start using it, because it's not Microsoft. Yeh, it's based on Mozilla but it's bound to be different to FireFox and it's bound to be different to Safari on the Mac.

And IE7 doesn't make life any easier. I can't forget about IE6, because most people are still using it, but enough people are using IE7 that I can't ignore that either. I will however continue to ignore Opera, Konqueror, Lynx, Netscape and any older versions of IE. Complain if you like, but I have better things to do with my time than worry about browsers with miniscule market share.

Friday, June 08, 2007

House prices to reach ten times salary? Bollocks

According to a report from a government think tank, house prices may be 10 times average salaries by 2025. It's all down to supply and demand apparently. Sure, looking at the figures in a simplistic fashion, with an increasing population and inadequate house building then house prices should continue to increase. But think about it a little longer and it clearly doesn't make any sense. Who will be buying these houses? First time buyers are already priced out of the market. Buy to letters might take up the slack but only if rents rise in line with house prices, otherwise they'd be better off investing their money elsewhere. So will rents rise as well? Given that a lot of our increase in population is driven by economic migrants, who generally are in pretty poorly paid jobs and renting, if rents rise dramatically, will they continue to come here? Probably not, since they'll be better off staying at home. So demand won't increase, meaning prices fall back to a more sensible level. There's also the question of whether the indigenous population would hang around with such ridiculous prices, meaning even less demand for housing.

On reflection, I suspect this report is purely some spin to get the idea of relaxing planning laws onto the agenda, thus enabling more house building. But whatever it's about, it's bollocks.

Wednesday, June 06, 2007

Great photo

IMAG0115How come my 4 year old daughter can take better photos than me? I have no idea what this is a photo of but I love it. She is also available for designing Olympics logos.

Tuesday, June 05, 2007

Bye bye eBay, hello Freecycle

I've sold lots of my old crap on eBay, but I've got a little fed up with it. Most of the stuff I've sold isn't high value so the hassle of eBay just isn't worth it. It takes a long time to put together the sale details, take pictures and package the item and post it. Then eBay takes their cut (whether you manage to sell your item or not) and PayPal takes a cut. Then there are the problem buyers, who fail to pay or have to be chased up. So for quite a lot of work, the returns are pretty slim.

So I've given up on it and I'm going to start giving stuff away on Freecycle. All I need to do is post a message and hopefully get a response. It has the added advantage of working at the local level, so goods aren't going to be transported half way across the world. Apparently there are over 3 million users already, pretty impressive.

Friday, June 01, 2007

Ah, so flickr does have a business model

I'd always presumed flickr was one of the web 2.0 sites that seemed to think it could somehow make money out of thin air. Then I was uploading some photos from the Random Pub Finder when I hit the site's 200 picture limit. Of course I can upgrade to a Pro account, but really it's not worth it for me. The only reason I was using flickr was to get some inbound links to the RPF and that isn't worth $24.95 a year. Because RPF's business model is non-existent...

New version of Windows Live Writer

livewriterThere is a new version of Windows Live Writer available. It looks prettier and has squiggly line spell checking but beyond that I don't know if it offers a lot. My problems with Live Writer in the past has been its inability to upload pictures to Blogger (which by all accounts is Blogger's fault) and its inability to check for new versions. I only found out about this new version after stumbling across a review of it. Admittedly, programs that do check for updates tend to upset me as well since they seem to require a new install every time I run them, so there's no pleasing me...

Anyhoo, I'll see if it uploads images now.

Time passes...

OK, so it can't upload images to Blogger directly but it will upload them to an FTP site and hook them into the page. The FTP integration is pretty sweet, although I guess it'll be no use to a lot of punters. But it solves my problem.

So that and the squiggly line spell-checking means I'll give it the thumbs up, even if it can't spell colour.

Some more time passes...

OK, perhaps I should have used the thing before writing a review. It now supports Blogger tags as well, meaning I'll now very rarely need to use the Blogger web interface, so even more thumbs up.