Friday, June 19, 2009

The problem with flags in Metastorm BPM

Flags are a great way to pass data between processes or to pass data from an external application to Metastorm BPM. There are several ways to raise flags, via the eRaiseFlag executable, using the Raise Flag ActiveX control, via the engine’s XML interface or through the engine’s COM interface. FreeFlow provides a wrapper around the last two approaches. Usage is pretty simple

      Connection conn = new Connection();
      // use to switch between TP and COM
      conn.RaiseFlagBy = RaiseFlagBy.TransactionProtocol;
      conn.RaiseFlag("New Data", new string[] {"some data", "1"});

As an aside, eagle eyed C# coders may be wondering why the last parameter of RaiseFlag doesn’t use the params keyword to simplify usage even further. The problem is there are several overloads of this method (taking user name, password, folder ID etc) so adding params would confuse the compiler since there would be multiple matches for a call to RaiseFlag. One solution would be to give the method a different name but this would make the API less discoverable since different versions of the same method would have different names. Another solution would be to just have one version of the method, with all the required parameters, but that wouldn’t really make life any simpler since the simple usage above would require passing in all parameters. API design isn’t an exact science and sometimes compromises are required.

But back to the main point of this post. Say we are going to create a new folder in Metastorm BPM using the flag data passed to populate the custom variables. Typically this might happen when somebody fills in an online ASP.NET form and we want to kick off some kind of process in Metastorm based on that data. So we’ll have a flagged creation action in the process and add some code to read the data, like so.

%tText:=%Session.FlagData[1]
%iNumber:=%Session.FlagData[2]

Which works fine. OK, say we change the data passed in to

conn.RaiseFlag("New Data", new string[] {"some\tdata", "1"});

Now when we run the code we don’t get a folder created. Instead we get an error in the Designer Log saying “'%inumber' failed while evaluating expression '%iNumber:=data' Error setting value for custom folder field 'inumber'”. This is because flag data passed to the engine is tab-delimited, so if your actual data contains tabs, everything gets screwed up.

We have two problems to solve here. First, how do we handle data with tabs in it, since we probably can’t stop tabs being entered by the user of the ASP.NET form. Secondly, how do we deal with any kind of failure to parse the data passed. This is more of a problem, since currently if we fail to parse the data, we lose it all since the folder never gets created.

robust flagsSo tackling the second problem first, we want to do as little work as possible in the flagged creation action. We will just assign the flag data to a temporary memo variable, since the flag data will not be available outside the flagged action.

%xmData:=%Session.FlagData 

You may find this won’t work for you in earlier versions, at some point only each individual item of flag data could be accessed but it looks to have been fixed in version 7.6. If it doesn’t work, you’ll need to manually combine each piece of flag data.

Next in the Parse conditional action (with no condition), we attempt to assign the flag data to the variables, like so

%tText:=%xmData[1]
%iNumber:=%xmData[2]

This will still fail and will stay at the ‘Got Data’ stage, but at least we haven’t lost the data. The Edit action can then be used to manually fix up the data and get the folder on its way.

So back to the first problem, handling tabs in flag data. Really the only solution to this is to use a different delimiter when raising the flag. None of them are perfect, since potentially any of them could be in the data, but %CHR(160) has worked well for us in the past. Another solution might be to pass your data in some other format such as XML. That will be more complicated but more robust. 

Thursday, June 18, 2009

Day 6 – where I take a test

I spent the morning taking a programming test, sent it off to the agent (the second in a chain of agents I’ve had to pass through to get my CV to the actual employer) and he told me they’d probably get back to me in a week. It seems like no-one is in too much of a hurry to actually employ people. My one interview is over a week away, although I’ve now got a telephone interview lined up for next week which will be followed by a real interview the next day if the phone interview goes well.

I did like the agent’s story of how one person who took this test (for a C# position) actually implemented his solution in Perl… It was quicker apparently. I’m sure he probably solved the problem in 3 incomprehensible lines of code, but he didn’t get the job. 

Wednesday, June 17, 2009

Looking for work – day 5

Perhaps the job market isn’t as bad as people are saying, I’ve managed to get myself an interview. Admittedly not the best paying job in the world, but it’s enough to cover the bills. And I’ve still got a few other potentials looking positive.

And going off at a tangent, I’ve got an idea for an application somebody could write. I’d do it myself, but I don’t have time right now. Logging into all these job websites to update my CV is a right pain. Keeping track of all the jobs I’ve applied for and all the conversations I’ve had with agents is also hard work. So an application that helps manage all these things would be most useful. It should be able to store all your login details for job websites, then I should be able to do a search across all the sites and also update my CV on all the sites in one action. It should also be able to keep track of job applications, link them to emails in Outlook and so on.

Tuesday, June 16, 2009

Looking for work – day 4

It occurs to me that having Metastorm BPM plastered all over my CV isn’t really helping in my job search. If a Metastorm job does come up then I’d hope I’d be in a good position to get it, but the fact is there aren’t any Metastorm jobs around at the moment. And most agencies (and potential employers for that matter) don’t have a clue what it is so they probably drop my CV in the bin, since they’ve almost certainly got a big pile of CVs that match their requirements more closely. To be fair, I’d do the same thing myself. So more CV tweaking is required I think. It seems like uploading a new CV to the job websites might trigger an email to agents anyway, so it may make sense to keep updating it, to keep myself at the front of the queue.

Agency quote of the day - “Winsocks are also important as well”

Monday, June 15, 2009

Looking for work – day 3

Things I learned today

  • There are still some jobs available for hackers like myself
  • Some of them are potentially quite interesting. In fact one actually seemed pretty exciting, not necessarily for the work itself but for the company who do something I think could change the face of one of our industries, in a good way.
  • Apparently having an A in Maths A-Level is considered a good thing, even though I took the exam 19 years ago and have forgotten everything about it. Take heed young uns!

Some stats

  • Number of agents I’ve talked to – lots
  • Number of jobs I’ve put myself forward for – several
  • Number of interviews lined up – none…

Sunday, June 14, 2009

Looking for work – day 2

I dunnow, but this may be interesting to people, so I’ve decided to blog about my attempts to find a new job. Given the current economic situation, I suspect it may take a while and as my life may start to mirror ‘Fun with Dick and Jane’, it could get entertaining (a film I ironically saw just a couple of months ago at the ‘Baltimore or bust’ conference…)

It will be interesting for me, for at least one reason. I’ve never had to look for a job during a recession. I was having a wild time at university during the last recession, it all passed me by in a drunken haze.

So what have my first moves been? I’ve uploaded my latest CV to Jobserve, Jobsite and TotalJobs and applied for a few jobs. I’ve also asked a few people on LinkedIn to recommend me. Being a Sunday, I obviously haven’t heard back from any of the agencies, but hopefully tomorrow things will really start to kick off for real.

Debugging authentication scripts in Metastorm BPM

I talked previously about how to simplify editing authentication scripts in Metastorm BPM by using the FreeFlow Administrator. What I didn’t talk about was how to debug what is actually going on in your authentication scripts. There isn’t a way to debug these scripts from within Visual Studio, or at least not that I’m aware of, so the only way to debug them is to write some kind of trace statements. This can help clarify which scripts are being executed and what path is being taken through the scripts.

I guess you could just write to a text file but the way I’ve done this in the past is to use the createLogEntry function. This function is provided in most of the authentication scripts supplied by Metastorm and usage of it is as follows.

    var err = new Object; 
    err.eDetectedByMethod = "eLogin()";
    err.eDetails = "logging in via web";
    createLogEntry( err );

This will write an entry to the eLog table. Typically I’ll add something similar to the above code as the first lines in each eLogin method in each authentication script, to see which script is actually being called, then add it where necessary to see why scripts are failing.

Saturday, June 13, 2009

Looking for work

I am in the market for a job. Ideally I’m after a .NET development role in London, but I’ve also got a lot of Metastorm BPM experience so anything in that area would suit me. You can find my CV here.

Thursday, June 11, 2009

Paste from Visual Studio naughtiness

There’s a nice add-in for Windows Live Writer called Paste from Visual Studio that lets you copy and paste syntax highlighted code from Visual Studio into Live Writer. I’ve used it for a while and had no problems with it. But the other day I looked at the source of the generated HTML and was a little disappointed to find a link to the author’s website with no link text, meaning it never shows up on the page.

I guess it’s OK wanting to get some links to his site, but to do it in such a way as to be invisible to the end user is a little underhand. I’m not sure what a search engine would make of a link like that. Very possibly it will be considered as some kind of black hat SEO and my pages will be downgraded as a result.

So I guess I’ll have to re-implement the plug-in myself. The guy does kindly provide the source code on his website so it shouldn’t be too difficult to get my own version built. Or I guess I can go back to using the excellent C# code format site, which does the right thing by just adding a comment to the generated HTML that includes the URL for the site.

Wednesday, June 10, 2009

Using a different SAP to login to Metastorm BPM via FreeFlow

Something that comes up quite frequently is how to use FreeFlow to login to Metastorm BPM using something other than the default authentication mechanism. This typically happens when SSO has been installed and although it’s possible to use FreeFlow with SSO it can be a hassle to set up, so the user wants to use the standard eUser authentication. This is pretty easy, by using the SAP property, as below. Note, the SAP property is zero based, so if the eUser script is second in your list of authentication scripts, its value needs to be 1.

  class Program
  {
    static void Main(string[] args)
    {
      Connection conn = new Connection();
      conn.HttpServer = "NEWDOOGAL";
      conn.Engine = "NEWDOOGAL";
      conn.ConnectionType = ConnectionType.HTTP;
      conn.SAP = 1;
      conn.LogOn("Doogal", "");
      Console.WriteLine("Session : " + conn.SessionId);
      Console.ReadLine();
    }
  }

There have been some reports of this not working in e-Work version 6. I suspect this is a bug on the Metastorm end, but since version 6 isn’t supported any more, I haven’t investigated too much. I suggest upgrading, it’s not too painful! And if you need help upgrading, you know who to call.

Thursday, June 04, 2009

IIS Search Engine Optimization Toolkit

I love Google Webmaster Tools since it tells me things I’m doing wrong on the websites I maintain (in fact I love any tools that tell me what I’m doing wrong, FxCop, the HTML editor in Visual Studio, compilers… I’m sure a psychiatrist would draw some scary conclusions from this admission). But the problem with the Google webmaster Tools is that they aren’t very responsive. If I fix an issue, I have to wait for the Google bot to crawl that page again before I know it’s been fixed.

So I was pleased to be directed to the Search Engine Optimization Toolkit by ScottGu’s post. It’s only in beta but it’s already a powerful tool. It will tell you about lots of potential problems on your site, such as missing alt tags on images, missing description meta tags, broken links, no h1 tag etc… I’ve spent the day trying it out with the Process Mapping site and have cleaned up a lot of potential problems, that no other tools had told me about. Only time will tell if this improves our ranking a lot, but I’m guessing even in the worst case, it won’t cause our ranking to drop, since all the suggestions seem perfectly sensible.

It’s simple to install, requires nothing special on the website to get it working and provides instant feedback, so I’m sold on it. The only possible downside is that it will only install on IIS 7 I guess (although that doesn’t mean your website needs to be running IIS 7, just the machine running the toolkit). 

Monday, June 01, 2009

The search for some page rank

I played around with bing today to see if it’s any good. I was pleased to see that a search for Metastorm brought up our forum and the FreeFlow web page on the first page, so clearly bing is a complete success. Or perhaps not, the image search brought up a photo of their former CEO, who hasn’t been there for many years.

But it did make me go off and check the same search against some other search engines (if you try this yourself, you may well get different results, I’d be interested to know if they are wildly different). Yahoo brings up the Process Mapping website on the first page, Cuil has Jerome’s book on the first page and puts the Process Mapping website on the second page and Ask has the forum on the second page (although for some reason they think I’m Dutch).

Finally I checked the other search engine that you may have heard of. OK, it’s the only search engine that matters. And we are way down the search rankings (page 5 for the forums, page 4 for FreeFlow). But the weird thing is if I search on Google’s UK site, FreeFlow and the forums are on the first two pages. That may make some sense for the FreeFlow site since it’s hosted in the UK, but the forums are hosted in Australia. Next I changed my search term to ‘Meta storm’ and was asked ‘Did you mean: Metastorm’, and although all the results were related to Metastorm, both the FreeFlow website and the forums now appeared on the first two pages. We also rank highly on searches for ‘Metastorm development’ and ‘Metastorm consultancy’ so it seems odd that we are so low down on that one search.

So is it a cock-up, conspiracy, some weirdness in the ranking algorithm or have we been marked down for doing something bad? I don’t think we’ve employed any bad practices in our attempts to improve our ranking and if we had, I would imagine that would impact all our rankings, not just on one search, so I think we can eliminate the last option. But I have no idea about the other three.

Saturday, May 30, 2009

Intercepting a HTTP call to modify its behaviour

Say you have some application that uses a HTTP call to get hold of some XML data. It could be a web service but might also be an ASHX generic handler, or any kind of server-side HTTP handling code. Say you want to change the behaviour of that code in some way but you don’t have access to the source code but can change the URL that the calling application uses. What to do?

This was my problem to solve. Actually I didn’t realise it was a problem until I thought of the solution, then thought it would be an exceedingly useful thing to do, and may well be useful in other scenarios. The basic idea is to create a generic handler that will replace the original handler and in the simplest case just pass the request onto the original handler and return the response from the original handler. The configuration of the calling app needs to be updated to use the new handler.

There may be other solutions, such as adding a HTTP module to the original handler, if it happens to be an ASP.NET application. This solution may impact performance since two HTTP calls will be made rather than just one, but I still quite like it.

Here’s the code for the ProcessRequest method, that obviously needs updating to make any changes to the response returned by the original handler. It also shows how to deal with POST data, if the call you’re intercepting is a POST call.

    public void ProcessRequest (HttpContext context) 
    {
      context.Response.ContentType = "text/xml";
      
      // create outgoing HTTP request
      HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
        ConfigurationManager.AppSettings["messageHandlerUrl"]);
      req.Method = "POST";
      req.KeepAlive = false;
      req.ContentType = context.Request.ContentType;

      // get POST data from incoming request
      string parameters;
      using (StreamReader postReader = new StreamReader(context.Request.InputStream))
      {
        parameters = postReader.ReadToEnd();
      }

      // add POST data to outgoing request
      using (Stream stream = req.GetRequestStream())
      using (StreamWriter streamWriter = new StreamWriter(stream))
      {
        streamWriter.Write(parameters);
        streamWriter.Close();
      }
      
      // get response
      using (WebResponse resp = req.GetResponse())
      using (Stream respStream = resp.GetResponseStream())
      using (StreamReader reader = new StreamReader(respStream))
      {
        string response = reader.ReadToEnd();
        context.Response.Write(response);
      }
    }

Thursday, May 28, 2009

Saving a control’s image to a file

Saving a WinForms control’s image to a file, should be pretty straightforward. After all, there is a DrawToBitmap method that should do the trick, right? Well, not quite. Unfortunately DrawToBitmap draws the controls in reverse order, i.e. the top controls are drawn first. So first you need to reverse the z-order of the controls on the form and then reverse them again then after generating the image, something like the following

        InvertZOrderOfControls(formControl);
        using (Bitmap bitmap = new Bitmap(formControl.Width, formControl.Height))
        {
          formControl.DrawToBitmap(bitmap, new Rectangle(0, 0, formControl.Width, formControl.Height));
          bitmap.Save(outputFile, ImageFormat.Jpeg);
        }
        InvertZOrderOfControls(formControl);

Ah, but what about the implementation of InvertZOrderOfControls? You can find that here.

Even after that frigging around, the saved image may not be perfect. The output produced is dependant on the underlying operating system. Vista and XP produce reasonably accurate output, but Server 2003 doesn’t look too good.

Tuesday, May 26, 2009

writing-mode: tb-rl broken in IE8

Vertical text in a web page can be pretty damn handy. I’ve used it in our documenter for Metastorm BPM procedures where we have some grids with columns which would become too wide if the column headers used horizontal text. OK, it only ever worked in Internet Explorer, but given the add-on is for an application that only runs in IE on Windows, I wasn’t too concerned by that.

Now, along comes IE8 and vertical text no longer works properly. Grids are incorrectly sized when they contain vertical text. Try this in a HTML page.

  what the...
  <table border="2px">
    <tr>
      <td style="writing-mode:tb-rl">Some text</td>
      <td style="writing-mode:tb-rl">Some more text</td>
    </tr>
  </table>
  .. is going on in that grid?

And this is what that will look like (obviously view in IE8 to see the problem)

what the...
Some text Some more text

.. is going on in that grid?

Which is disappointing… Ah but you’re not here to see the problem are you? You want to know how to solve it I guess. The only solution I’ve found is to add the following to your head element, which forces IE8 to render the page in IE7 emulation, which means you miss out on all the new rendering goodness in IE8.

<meta http-equiv="X-UA-Compatible" content="IE=7" />

Update – You’ll probably need to manually force IE to switch to IE8 standards mode to see the problem, seems that MS has decided BlogSpot sites are not ready for IE8, so this is running in compatibility mode.

Sunday, May 24, 2009

To ID or not to ID

Somebody commented on my post about my implementation of a US states table, saying there was a problem with it because there was no ID field. I can understand the comment but don’t necessarily agree with it. The table has a primary key, the state code. In this instance, this seems a reasonable approach, the state code isn’t likely to ever change and non-numeric primary keys can still be used as foreign keys in other tables. In fact using the state code has its advantages since in some queries, joining to the state table won’t be necessary if only the state code is required in the result set, so SQL is simplified and is probably quicker.

But ID fields are almost expected by many people, but is this purely down to habit, or are there good reasons for having an ID field in tables? In many cases, there is no other obvious column (or number of columns) to use as a primary key in the table. Then it clearly makes sense to add an ID column. But are there other reasons to use an ID column? Does it offer better performance? I don’t actually know the answer to this question, but I’m interested to know.

I do have an example where a seemingly sensible unique column was used as a primary key, but this caused problems down the track and an ID column would have been a better solution. It was a user table that used the user name as the primary key. This seems like a reasonable idea, since it has to be unique, but the problem came when users wanted to change their user name in the system. This can be due to any number of reasons, but the main one is getting married. Since the user name was used as a foreign key on lots of other tables, things got somewhat complicated when trying to update them all. Of course with hindsight a user ID column would have been a better solution in this case.

So perhaps that’s the reason people add ID columns by default, since it’s perhaps impossible to guess beforehand whether the natural primary key will need to be updated. That said, it seems pretty unlikely that state codes will change, so I’m happy to keep the state code as my primary key.

Wednesday, May 20, 2009

Google Friend Connect and the back button

If you wait long enough, generally whatever feature you’ve wanted to add to your website gets implemented by somebody in such a way that you can reuse it yourself, so I was pleased to see Google Friend Connect had the ability to let users add comments to web pages, as this is something I’d wanted to add to the Random Pub Finder for a long time, and hadn’t got round to doing, mainly due to laziness. Admittedly Friend Connect may not be the perfect solution, particularly if you want to keep in control of your data, but it looks good as a quick and dirty hack.

So I added it and almost immediately I noticed a bit of a major problem. Hitting the back button didn’t take me back to the previous page, I needed to press it again. In fact, it turns out that for every Friend Connect gadget that you add to a page, another entry gets added to the history list, making backwards navigation even more difficult. It looks like it’s an IE only issue, but given that most web users are still using IE, I think it’s pretty big problem (unless normal users don’t use the back button?). After doing some searching on the internet, I discovered this problem has been around for quite a while but still hasn’t been fixed. Which is a shame, because Friend Connect looks cool. But it looks like I’ll have to remove it until this issue is addressed.

Tuesday, May 19, 2009

Editing authentication scripts in Metastorm BPM

 Editing Metastorm authentication scripts If you’ve ever had problems with SSO in Metastorm BPM, or been required to implement an unusual authentication mechanism, you’ve probably had to go through the jig of removing, editing and re-adding the script through the System Administrator application. Having found this approach less than optimal, I added support for authentication scripts to the FreeFlow Administrator. This works somewhat differently since it allows you to edit scripts directly. This makes the edit, test and debug process much quicker. Edit the script, apply the changes, test the login (the engine picks up the new script without needing a restart) and then debug and start again if necessary. It’s probably not something you’ll use every day but when you do need it, it makes life much easier.

Thursday, May 14, 2009

Is BPM counter cyclical?

There’s a theory going around that has probably been mentioned enough times now to become a mainstream view. The theory is this – BPM is counter cyclical. And the reasons put forth for it are this. During a recession, companies are looking to cut costs and one way to achieve this is by purchasing and implementing BPM software. BPM promises to improve process efficiency thus reducing costs.

It’s a persuasive argument, if a little simplistic, but is it true? To tell you the truth I have no idea. Rashid Khan argues it’s not true and the only arguments I’ve seen that BPM is counter cyclical seem to have come directly or indirectly from BPM vendors. Certainly something that promises to offer a significant reduction in costs is going to be attractive but BPM software is generally an expensive purchase (although there are free options starting to appear) and then there are costs involved in actually getting it up and running. No single company’s processes are exactly the same as another so some analysis and development will be required. And processes change, so there are on-going costs incurred. Finally, it’s difficult to quantify how much money will be saved by implementing BPM. I’ve seen some impressive figures for savings made, but will those figures be replicated in your organisation? There are so many variables involved, it’s probably impossible to know. Again, that may make it difficult to justify the up front cost.    

Next, some anecdotal evidence. The Lombardi conference was cancelled and became a virtual event, but the Metastorm conference was well attended, so it’s difficult to draw any conclusion from that. Metastorm claim their revenues have increased by 16% over the past year which isn’t explosive growth, but any kind of increase should be considered a good thing at the moment. The company I work for has certainly not been unaffected by the downturn but we are still managing to keep our head above water.

That all said, BPM isn’t going away. And if it isn’t counter cyclical, that won’t necessarily be a bad thing. Perhaps, finally, we might see some consolidation in the area. Some of the weaker companies may disappear or merge and the stronger companies can gain more market share and thus have more money when the recovery comes (whenever that maybe) to produce even better software.

Of course, if you are thinking about or are actively implementing BPM in your organisation, Process Mapping is here to help.

Tuesday, May 12, 2009

Lastminute trademarks the colour magenta

When browsing the Lastminute website, I spotted this notice at the bottom.

"lastminute.com", "lastminute" and the colour magenta are all trade marks owned by Last Minute Network Limited and/or its group companies

Bizarrely it turns out that trademarking colours is possible. But we’re OK, we can all continue to use the colour so long as we aren’t in the same business as Lastminute, which is, er, selling pretty much anything. Oh.

Monday, May 11, 2009

A generic error occurred in GDI+ when saving an image

This has to be one of the most useless error message I’ve ever encountered in the .NET framework. It seems that pretty much any problem that occurs when using Image.Save will produce this error message. Looking at the code in Reflector, the error is returned from the GDI+ function GdipSaveImageToFile. For me the solution was pretty simple, the folder I was trying to save my image to didn’t actually exist but it took some head scratching before I realised.

Saturday, May 09, 2009

New look JobStats site

Not sure when it happened, but JobStats has got a new look. This is the best site to find out about the health of the IT job market in the UK. From looking at the site, it’s clear they now have some issues with their data (or calculations) since some of the average rates are clearly ridiculous. And if they’re not, I really should be getting into doing SAP consultancy. That said, the graph showing the number of advertised jobs is truly terrifying, since there are now fewer jobs advertised than at any time in the last ten years. If you consider that probably many more of the total jobs available are now advertised online than ten years ago makes that graph even more depressing viewing. Lets hope I don’t need to to look for a new job any time soon… (and that is really tempting fate)

Sunday, May 03, 2009

Adding trace to ASHX files

This is almost certainly caused by me being an idiot, but I know there are other people out there who are idiots on occasion so I thought I’d post this. I’ve never used Trace.Write in ASP.NET before and the first place I needed to use it was in a generic handler ASHX page. I didn’t look too closely at what was required and assumed that I needed to use Trace.Write in System.Diagnostics. That was my first mistake. This doesn’t add any trace output to the trace.axd page (though it would be cool if it did since I could then add trace to my assemblies that would show up, perhaps adding a custom trace listener could fix this?).

So then I looked at the code for System.Web.UI.Page and realised it has a Trace property but a generic handler doesn’t have this property so I thought that I might be out of luck. Eventually I realised that the HttpContext passed into the ProcessRequest method has a Trace property so I could just use that for all my tracing needs. Problem solved.

Update: I’ve now realised it’s pretty easy to include trace output from System.Diagnostics.Trace calls in ASP.NET apps. Just add the following to web.config

<configuration>
  <system.diagnostics>
    <trace autoflush="false" indentsize="4">
      <listeners>
        <add name="WebPageTraceListener"
            type="System.Web.WebPageTraceListener, System.Web, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
      </listeners>
    </trace>
  </system.diagnostics>

Baltimore skyline

Baltimore skyline panorama

There’s an advert for Microsoft on currently that shows a kid taking some photos and with a few clicks stitching them together into a panorama. All thanks to the ease of use of Vista. So when I was in Baltimore this week I took some photographs to attempt to do the same thing. After all, if some kid can do it, I’m sure I should be able to. But when I fired up Windows Photo Gallery I was unable to find any options to make a panorama. So I fired up Windows Live Photo Gallery instead and that did have the option. In fact it was pretty damn simple to create a panorama, as you can see above.

But two things bother me. Why are Microsoft advertising Windows by showing the capabilities of an application that doesn’t ship with the operating system? And why are there two different photo gallery applications which seemingly do the same thing but are subtly different?

Saturday, May 02, 2009

A simple WinForms numeric edit control

There are plenty of controls out there that will allow only numeric entry but they may not meet your needs. If you don’t want the up/down buttons, the built-in NumericUpDown control won’t be of use and using one of those huge libraries just for their numeric control may be overkill. If that describes your position, this uber-simple control may fit the bill.

  public class NumberControl : TextBox
  {
    /// <summary>
    /// Creates a new <see cref="NumberControl"/> instance.
    /// </summary>
    public NumberControl()
    {
      TextAlign = HorizontalAlignment.Right;
    }

    /// <summary>
    /// Triggered when a key is pressed. Swallows all keys except for digits.
    /// </summary>
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
      base.OnKeyPress(e);
      string decimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
      if (e.KeyChar.ToString() == decimalSeparator)
      {
        if (Text.IndexOf(decimalSeparator) > -1)
          e.Handled = true;
      }
      else if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
      {
        e.Handled = true;
      }
    }
  }

Wednesday, April 22, 2009

Adding Street View to web pages

Street View has been available for a while but it didn’t interest me too much until recently when it launched in the UK. Then I noticed it was available as a part of the Google Maps API so I decided I’d add it to the Random Pub Finder. I initially thought it would work like the user interface available on the Google Maps website, but it doesn’t seem such an integrated solution is possible. To be fair, dragging the little man around your map probably isn’t what you’ll want to do generally so this isn’t a big issue, and if you’re clever you could probably implement this kind of solution anyway. I was pleased to find it was very easy to add to the site. First add a div to your page that will contain the Flash control

  <div name="pano" id="pano" style="width: 750px; height: 300px">
    <div id="panoError" style="text-align: center; margin-top: 100px;color: #aaa;">Loading...</div>
  </div>

Then add  a reference to the Google Maps API script.

<script src="http://maps.google.com/maps?file=api&amp;v=2.x&amp;key=ABQIAAAAjtZCgAx5i04BiZDO6HlxhRSDF8NMhBf90dVWYNzYEfop4QQs3RSkYVE7vnmvtIBRRQjoFXq4kz15Mg" type="text/javascript"></script>

Finally add some JavaScript to initialise the control.

    function load()
    {
      var latLong = new GLatLng(51.412640159832, -0.30039334012124);
      var panoramaOptions = { latlng:latLong };
      var myPano = new GStreetviewPanorama(document.getElementById("pano"), panoramaOptions);
      GEvent.addListener(myPano, "error", handlePanoError);
    }

    function handlePanoError(errorCode)
    {
      var error = "An error occurred";
      if (errorCode == 600)
      {
        error = "No street view available";
      }
      else if (errorCode == 603)
      {
        error = "The Flash plugin is not available";
      }

      document.getElementById("panoError").innerText = error;
    }

Actually a lot of this JavaScript isn’t even needed, you can get away with just the first three lines of the load function, but the rest is useful for error handling. Error 600 can happen quite frequently so is best handled in some manner. I’ve not seen error 603 myself but I’ve shown it here because some of the code examples I’ve seen use the constant FLASH_UNAVAILABLE, which doesn’t seem to be defined anywhere, meaning when any error occurs you’ll actually get a script error if you use this constant. I guess there are more errors that can occur but I’ve not come across them yet.

Friday, April 10, 2009

Renaming a user in Metastorm BPM

Rename Metastorm user

People change their names for all sorts of reasons, marriage being the most common, but there are plenty of other reasons, such as a change of religious belief or wanting to get rid of an unfortunate surname. Whatever the reason, the Users and Roles utility that ships with Metastorm BPM doesn’t have any support for changing user names. The FreeFlow Administrator can help out here. Just fire it up, change the user’s name and apply the changes. This will change the user name in all relevant Metastorm tables. However it can’t change every reference to that user. For instance if you’ve stored a user name in a custom variable, that won’t get updated, since there isn’t really any way of knowing that the custom variable value refers to a user.

This can also be achieved programmatically. This may be useful if your corporate standards have changed and you need to change every user’s name in the system. Or you may want to prefix the users’ names with the domain name because you’re moving to SSO. Whatever the reason, here’s some sample code to achieve this.

using System;

using FreeFlow.Administration;

namespace ChangeAllUsers
{
  class Program
  {
    static void Main(string[] args)
    {
      Server server = new Server();
      server.Connect("sa", "a", "Metastorm");
      foreach (User user in server.Users)
      {
        Console.WriteLine("Processing " + user.Name);
        user.Name = "domain\\" + user.Name;
        user.ApplyChanges();
      }
    }
  }
}

Monday, April 06, 2009

Documenting Metastorm procedures

You may be aware of the procedure documenter produced by Process Mapping, the company I work for. If not, have a look. It’s a Designer add-in, which means a menu item is added to the Designer’s Tools menu that kicks off the documenter and generates the HTML documentation. I spent a little time today extracting the HTML generation code from the add-in assembly into a separate assembly. This means that the code can be called from anywhere you like, which leads to the possibility of automating your documentation generation. Combine this with FreeFlow and you can automate the documentation of all your published procedures. The following console application demonstrates how this could be achieved.

using System;
using FreeFlow.Administration;
using ProcessMapping.ProcedureDocumentationGenerator;

namespace DocumentProcedures
{
  class Program
  {
    static void Main(string[] args)
    {
      Server server = new Server();
      server.Connect("sa", "a", "Metastorm");
      foreach (Procedure proc in server.Procedures)
      {
        Console.WriteLine("Processing " + proc.Name);

        string filename = "c:\\temp\\" + proc.Name + ".xep";
        proc.Versions.LatestVersion.SaveToFile(filename);

        string htmlFilename = "c:\\temp\\html\\" + proc.Name + ".html";
        DocumentationGenerator generator = new DocumentationGenerator();
        generator.IncludeMapImages = true;
        generator.Generate(filename, htmlFilename);
      }
    }
  }
}

I believe later versions of SQL Server allow the execution of .NET code from within the database, so I would imagine it is possible to add a trigger to the eProcedure table that kicks off this code whenever a new record is added, so documentation will always be up to date.

Of course the procedure documenter isn’t a silver bullet. To generate useful documentation, some work will be required to ensure the notes in you procedures contain useful information.

Friday, April 03, 2009

Debugging server-side scripts in Metastorm BPM

Script Debugging in Metastorm BPM

Debugging server-side scripts in the Metastorm BPM can be difficult. One thing that can help with debugging JScript/VBscript scripts is the FreeFlow Administrator. When an error occurs in your script, generally you’ll be given a line number where the error occurred. This may not relate to a line number in the Designer because scripts are merged together when they get published to the database. The FreeFlow Administrator provides a simple way to view your scripts and see the line numbers and hence track down bugs more easily.

Unfortunately in the world of JScript.NET, when an error occurs you won’t get a line number telling you where it happened. If you want to learn more about debugging .NET code in Metastorm BPM, I would recommend Process Mapping’s .NET course (generally presented by myself).

Tuesday, March 31, 2009

Saving latest versions of procedures from a Metastorm BPM database

Save Metastorm Procedures

One thing that comes up quite often is how to get all the latest procedures from a Metastorm database. The FreeFlow Administrator makes this easy. Simply load it up, connect to the database and use the ‘Save procedures…’ action. Select the folder to save the procedures to and everything will be saved there, included all libraries.

Like everything else in the FreeFlow Administrator, this can all be achieved programmatically, with something like the following.

      Server server = new Server();
      server.Connect("sa", "a", "Metastorm75");
      server.RetrieveLatestProcedures("c:\\temp");

Why would you want to do this? Perhaps you regularly want to get all the latest versions of the procedures in your Metastorm database before you head out of the office and want a script/console app that does this for you.

Finally, you can also get the latest versions of a specific procedure, using something like this

      server.Procedures["Flight"].Versions.LatestVersion.SaveToFile("c:\\temp\\flight.xep");

Saturday, March 28, 2009

XSLT to generate an HTML listing of your iTunes library

The data for the music in an iTunes library is stored in an XML file which means that it should be simple to produce an XSLT file to generate an HTML document listing all the albums in your iTunes library. Well, it would be, but the XML format used by iTunes is, how can I put this, idiosyncratic. Other people may use more fragrant language to describe it…

Anyway I found this very useful article describing how to create an XSLT file to do almost what I wanted, but I wasn’t interested in grouping by genre, since the genre data is often not very accurate or helpful. I’m also a little anal about my music collection and wanted it sorted by artist name, rather than album name. This part was slightly tricky because the data may not be complete. The album artist field is often not entered and the artist field may not be the album artist, even when the album is not a compilation. And finally the compilation flag may be set when you don’t expect it to be or conversely not set when you expect it to be. e.g. a greatest hits album may be flagged as a compilation. This is arguably correct, but I wanted these kind of albums to be grouped with the artist. So quite a few changes were required which is why I’m posting this, it’s not just me trying to get some reflected glory (and hence me linking to the original article repeatedly)

Anyway, the basic idea is the same as the original article. First put an XML file in the same directory as your iTunes library, as follows

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="albumList.xsl" type="text/xsl"?>
<wrapper>
  <incl file="iTunes Music Library.xml"/>
</wrapper>

Next is the XSLT, in a file called albumList.xsl in the same directory, which looks like this.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html" encoding="UTF-8" indent="yes"/>

  <!-- match the wrapper and apply templates to the <incl> xml file -->
  <xsl:template match="/wrapper">
    <xsl:apply-templates select="document(incl/@file)/plist/dict/dict"/>
  </xsl:template>
  
  <xsl:key name="songsByAlbum" match="dict" use="string[preceding-sibling::key[1]='Album']"/>

  <xsl:template match="dict">
    <html>
      <head>
        <title>iTunes Album Listing</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <style>
          body
          {
            font-family:Arial;
          }
        </style>
      </head>
      <body>
        <table>
          <thead>
            <tr>
              <td><b>Artist</b></td>
              <td><b>Album</b></td>
            </tr>
          </thead>
          <xsl:variable name="song" select="/plist/dict/dict/dict"/>

          <!-- output the albums that aren't compilations -->
          <xsl:for-each select="$song[generate-id(.)=
        generate-id(key('songsByAlbum',string[preceding-sibling::key[1]='Album'])[1])]">
            <xsl:sort select="concat(string[preceding-sibling::key[1]='Album Artist'], string[preceding-sibling::key[1]='Artist'])"/>

            <xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
          [not(true[preceding-sibling::key[1]='Disabled'])]
          [not(true[preceding-sibling::key[1]='Compilation'])]            
          [1]">
              <xsl:call-template name="outputAlbum" />
            </xsl:for-each>
          </xsl:for-each>

          <!-- output each compilation -->
          <xsl:for-each select="$song[generate-id(.)=
        generate-id(key('songsByAlbum',string[preceding-sibling::key[1]='Album'])[1])]">
            <xsl:sort select="string[preceding-sibling::key[1]='Album']"/>

            <xsl:for-each select="key('songsByAlbum',string[preceding-sibling::key[1]='Album'])
          [not(true[preceding-sibling::key[1]='Disabled'])]
          [true[preceding-sibling::key[1]='Compilation']]
          [not(string[preceding-sibling::key[1]='Album Artist'])]
          [1]">
              <xsl:call-template name="outputAlbum" />
            </xsl:for-each>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="outputAlbum">
    <tr valign='top'>
      <!-- the artist: -->
      <td>
        <xsl:choose>
          <xsl:when test="string[preceding-sibling::key[1]='Album Artist']">
            <xsl:value-of select="string[preceding-sibling::key[1]='Album Artist']"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:choose>
              <xsl:when test="true[preceding-sibling::key[1]='Compilation']">
                <i>Compilation</i>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="string[preceding-sibling::key[1]='Artist']"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:otherwise>
        </xsl:choose>
      </td>
      <!-- the album name: -->
      <td>
        <xsl:value-of select="string[preceding-sibling::key[1]='Album']"/>
      </td>
    </tr>
  </xsl:template>
  
</xsl:stylesheet>

Now open the XML file in your favoured browser (as long as your favoured browser is IE or FireFox) and the HTML should be generated. It can be slow, it might be the XSLT can be optimised somewhat but I’m not hugely experienced in optimising XSLT so haven’t investigated further. And of course the XSLT can’t cope with all rubbish data in your iTunes library, garbage in, garbage out.

One other improvement would be to improve the sorting so that artists such as The Fall came under F, rather than T, but I have no idea how to achieve that.

Tuesday, March 24, 2009

Saving all folder attachments from a Metastorm database

A question came up on the Metastorm BPM forums about saving all folder attachments from a Metastorm database. Folder data is saved in a particularly strange format so this wouldn’t be very straightforward but with the FreeFlow .NET library things are much simpler. I thought I’d post it here just to show how easy it is. In fact I may start posting some more simple FreeFlow examples here, rather than posting them as downloads on the FreeFlow site, because it’s much easier than fiddling with ASP.NET pages.

It’s a console application that should work with .NET 2 upwards.

using System;

using FreeFlow.Administration;

namespace GetAllAttachments
{
  class Program
  {
    static void Main(string[] args)
    {
      Server server = new Server();
      server.Connect("sa", "a", "Metastorm75");
      foreach (Map map in server.Maps)
      {
        foreach (Folder folder in map.Folders)
        {
          foreach (Attachment attachment in folder.Attachments)
          {
            string fileName = "C:\\temp\\" + folder.FolderId + attachment.FileName;
            Console.WriteLine("Processing " + fileName);
            attachment.SaveToFile(fileName);
          }
        }
      }
    }
  }
}

Sunday, March 22, 2009

When will Delphi die?

I have a theory. As with all my theories, it probably doesn’t stand up to close scrutiny but I’ll put it out there none the less. And this is it. The more trouble a company is in, the more marketing emails they will send out. and if they are in really big trouble, then they start phoning people. 

If this theory is in any way correct then Embarcadero (purchasers of Delphi) are in big trouble, or their Delphi division is at least. Not only do I constantly receive emails from them or their partners telling me how great the new version of Delphi is, but I’ve also had at least three phone calls from people desperate to sell me Delphi licenses. Unfortunately I have no interest in buying Delphi licenses, since I haven’t touched Delphi code for years. Ten years ago Delphi was a great product, it beat Visual Studio hands down for its power to create Windows apps. But along came .NET, which in its original incarnation was fairly decent and has been getting better and better as time goes on. Now Delphi is essentially an irrelevance, except for people needing to maintain an existing code base. Who in their right mind would choose Delphi to build a new product? At this point I was going to direct you to the JobStats page showing demand for Delphi skills in the UK but they don’t even bother to list it anymore. There may be a small niche area where native compilation is required for performance reasons and Delphi may fit the bill, but I suspect this niche is getting smaller and smaller. Another problem that Embarcadero have is that it is almost impossible to make money from development tools. Microsoft essentially provide their tools as a loss leader and also provide damn good free versions. There are also lots of other good free development tools out there, so why pay for a development tool at all, particularly one for an obscure language that is no longer cool and happening? To be fair, you can pick up a fairly decent version of Delphi for free.

So the only question I have is when will Delphi die? I guess, like Cobol and FoxPro and a hundred other seemingly redundant technologies, it will continue to trudge along for a long time yet. Or perhaps I’m completely wrong and there is some place where Delphi can still be a winning technology, but I can’t think of one myself.

Saturday, March 21, 2009

Error logging in ASP.NET using a HTTP module

I wrote many moons ago about logging errors from an ASP.NET website but even then I knew there was a better way of doing it. The problem with that implementation was that it assumed the ASP.NET application was under my control. What happens if you want to add error logging to somebody’s else application for which you don’t have the source? Or what about an application you’ve written yourself that you’re selling to other people but also using internally, and you don’t want to prescribe to your customers how they should implement their logging? What is required is a more flexible approach to logging, something that can be plugged into any ASP.NET application.

Fortunately ASP.NET provides such a mechanism, via HTTP modules. Another reason I’d never looked at implementing logging this way was that I thought it might be tricky to do, but in fact it is so simple it took me all of an hour to produce a working solution.

The first thing to do is create a new class library and add a class that implements the IHttpModule interface. This interface has only two methods, Init and Dispose. Here's how I implemented it.

  public class ErrorLogger : IHttpModule
  {
    public void Init(HttpApplication context)
    {
      context.Error += new EventHandler(context_Error);
    }

    public void Dispose()
    {
    }

    private void context_Error(object sender, EventArgs e)
    {
      Exception ex = HttpContext.Current.Server.GetLastError();
      // log the exception however you like
    }
  }

Pretty simple. The logging code isn’t shown for brevity’s sake but all the class does is hook up an event handler for the Error event and within that event handler do whatever is necessary to log the error. In our case, we email the error and put it in a database table, but you can log it however you wish. (here’s some exception logging code for you)

Now all that’s required is to drop the assembly in the app’s bin directory and tell the ASP.NET application about the HTTP module. This is done via the web.config file with the following in the <system.web> section

  <httpModules>
    <add type="Utilities.ErrorLogger" name="ErrorLoggingModule"/>
  </httpModules>

type is the .NET type and name is whatever you like. And, er, that’s it.

As you’ve probably realised HTTP modules are powerful little things that can be used to do all kinds of stuff in a flexible manner.

Update – for those not reading the comments, there’s a free logging tool called ELMAH that can do all this for you without needing to write any code.

Thursday, March 19, 2009

IE8’s compatibility view is not IE7

It would appear that IE8 has been released today which coincides with me finding out something unpleasant about its compatibility view. I’d assumed switching to compatibility view would show the web page using IE7’s rendering engine. But this doesn’t seem to be the case. Go to the Process Mapping website and try out the dropdown menus. When you move outside the boundaries of a menu it should disappear, as you’d expect. Using IE6, IE7, IE8 (native mode), FireFox, Safari and Chrome that’s exactly what happens. But in IE8’s compatibility view the menu stays where it is.

So I can only assume compatibility view is not IE7 in new clothing. Given that anybody can switch to compatibility view for whatever reason and returning to that site will stick with that setting, this means that the introduction of IE8 means rather than having one other browser to test against, we have two instead! Oh great…

Tuesday, March 17, 2009

Whatever happened to NDoc?

NDoc was one of my favourite tools for .NET. Document your assemblies with the .NET XML documentation tags, point NDoc at them and out comes a help file. It was simple to use but was also powerful enough to customize the output in most ways you’d want.

Go to the linked website and you’ll see not much has happened on the NDoc project since 2005. Unfortunately support for .NET 2 was never completed before the original author decided to give up on the project and it looks like nobody has picked up the baton to develop it further. This is understandable to an extent. After all Microsoft have come out with Sandcastle which essentially does the same thing as NDoc. But Sandcastle is difficult to love. It has no user interface and builds take forever compared to NDoc.

The user interface problem can be solved by using Sandcastle Help File Builder, which does a nice job of looking like NDoc, but is still hampered by the underlying Sandcastle technology. After much frigging around I’m still unable to figure out how to add custom HTML into my documentation (for Google Analytics and the like) on a per project basis. I can do it globally by messing with the templates but this means I have to modify the templates for each build of a particular project, which is not ideal.

Perhaps I’m missing something, but it seems sad that 4 years since development on NDoc stopped, we still don’t have something as easy to use or as fast, even with the might of Microsoft behind it. I’m not surprised the original author decided to stop working on it, apparently he was getting threatened because his support for .NET 2 wasn’t coming along fast enough… And I’m sure the thought of trying to compete with Microsoft didn’t encourage his efforts either. But Microsoft have failed to deliver IMHO so I’m still missing NDoc after all this time.

Monday, March 16, 2009

US states table for SQL Server

I couldn’t find any SQL to generate a table of US states for SQL Server though I found this for MySql, so I modified it slightly and came up with this for SQL Server. Create the table with this

CREATE TABLE States(
    StateCode char(2) NOT NULL,
    StateName varchar(250) NOT NULL,
 CONSTRAINT PK_States PRIMARY KEY CLUSTERED 
 (
    StateCode ASC
 )
)

And populate it with this

insert into States values ('AL', 'Alabama');
insert into States values ('AK', 'Alaska');
insert into States values ('AZ', 'Arizona');
insert into States values ('AR', 'Arkansas');
insert into States values ('CA', 'California');
insert into States values ('CO', 'Colorado');
insert into States values ('CT', 'Connecticut');
insert into States values ('DE', 'Delaware');
insert into States values ('DC', 'District of Columbia');
insert into States values ('FL', 'Florida');
insert into States values ('GA', 'Georgia');
insert into States values ('HI', 'Hawaii');
insert into States values ('ID', 'Idaho');
insert into States values ('IL', 'Illinois');
insert into States values ('IN', 'Indiana');
insert into States values ('IA', 'Iowa');
insert into States values ('KS', 'Kansas');
insert into States values ('KY', 'Kentucky');
insert into States values ('LA', 'Louisiana');
insert into States values ('ME', 'Maine');
insert into States values ('MD', 'Maryland');
insert into States values ('MA', 'Massachusetts');
insert into States values ('MI', 'Michigan');
insert into States values ('MN', 'Minnesota');
insert into States values ('MS', 'Mississippi');
insert into States values ('MO', 'Missouri');
insert into States values ('MT', 'Montana');
insert into States values ('NE', 'Nebraska');
insert into States values ('NV', 'Nevada');
insert into States values ('NH', 'New Hampshire');
insert into States values ('NJ', 'New Jersey');
insert into States values ('NM', 'New Mexico');
insert into States values ('NY', 'New York');
insert into States values ('NC', 'North Carolina');
insert into States values ('ND', 'North Dakota');
insert into States values ('OH', 'Ohio');
insert into States values ('OK', 'Oklahoma');
insert into States values ('OR', 'Oregon');
insert into States values ('PA', 'Pennsylvania');
insert into States values ('RI', 'Rhode Island');
insert into States values ('SC', 'South Carolina');
insert into States values ('SD', 'South Dakota');
insert into States values ('TN', 'Tennessee');
insert into States values ('TX', 'Texas');
insert into States values ('UT', 'Utah');
insert into States values ('VT', 'Vermont');
insert into States values ('VA', 'Virginia');
insert into States values ('WA', 'Washington');
insert into States values ('WV', 'West Virginia');
insert into States values ('WI', 'Wisconsin');
insert into States values ('WY', 'Wyoming');

Friday, March 06, 2009

WPF ASCII grid part 3

It’s been a while since I last posted about my efforts to write a simple WPF app. At the end of my last post I had managed to create a custom control to display my ASCII grid and things were coming along nicely. The next thing I wanted to was pretty straightforward, or so I thought… I wanted to add a property to my control that decides whether the grid shows the standard ASCII values or the extended ASCII range. To do this I thought the simplest thing to do, when the property value changed, was clear out my grid and re-add the cells with the new values. Maybe not the most efficient approach but it seemed like the simplest option since there doesn’t seem a way to access the cell contents of a grid to change the text values.

But the problem with this approach is it doesn’t really work. Changing the property value calls the code but afterwards all that can be seen is the grid lines, not the new contents of the cells. Resizing the window does show the new cell contents so I assumed this was a refresh issue. Looking in to how to refresh a WPF control came up with these possible options

  • Call Dispatcher.Invoke with a priority of DispatcherPriority.Render
  • Call InvalidateVisual
  • Call InvalidateMeasure
  • Call InvalidateArrange
  • Call Measure
  • Call OnChildDesiredSizeChanged

I’ve no idea which of these I should be calling but I tried all of them and none had any effect. So I am at something of a loss. I think the next step is to rework my code so I don’t recreate the TextBlocks I’m using to display the ASCII values and just update the Text property of the existing TextBlocks instead.

Tuesday, March 03, 2009

FEEDJIT

If you look further down this page, you’ll see a map of the world covered with what looks like the pox. Click on the map and you’ll be whisked off to the Feedjit site which shows the location of recent hits to this site and which pages were visited. I guess it isn’t really providing any more information than I can get from Google Analytics but I think it is displayed in a much more intuitive way. Are you listening Google?

Google AJAX APIs Playground

Mostly as a reminder to myself, Google have added a AJAX APIs Playground page that shows off some of the many features they provide in Google Maps, Local Search etc. There’s lots of things there that I had no idea about and you can edit the code in place to play around with the features. Sweet.

Friday, February 27, 2009

Copying a stream to another stream

It’s obvious that the ability to copy the contents of one stream to another is something that you’re likely to want to do fairly regularly. I vaguely recall that this will be added to some version of the .NET Framework but until that time, here’s a simple implementation that should do the trick. It could easily be converted into an extension method so it plays better in the .NET 3 world.

    /// <summary>
    /// Copies a stream to another stream
    /// </summary>
    /// <param name="copyFrom">The stream to copy from</param>
    /// <param name="copyTo">The stream to copy to</param>
    public static void CopyTo(Stream copyFrom, Stream copyTo)
    {
      const int buffSize = 128;
      byte[] buff = new byte[buffSize];
      int count;
      do
      {
        count = copyFrom.Read(buff, 0, buffSize);
        copyTo.Write(buff, 0, count);
      }
      while (count > 0);
    }