It may be just me (and I’m not prepared to say otherwise), but I seem to get a bit tied in knots sometimes when creating navigation menus for websites in cases where the menu will change depending on whether the user is logged in or not.  The simplest solution (including the menu in-line in the page) has issues once caching is used.  Here’s one solution that has worked well for me.

First, we create a div within the page as a placeholder.

<div id=”navBar”></div>

Ensuring we’ve got a reference to jQuery in there, we’ll add the following javascript.
function createMenu(container, data) { var div_menu = $(document.createElement('div')).addClass('menu'); container.append(div_menu); for (i = 0; i < data.length; i++) { var list = $(document.createElement('ul')); div_menu.append(list); var listitem = createLink(data[i], true); list.append(listitem); if (data[i].Children != null) { var sublist = $(document.createElement('ul')); listitem.append(sublist); for (j = 0; j < data[i].Children.length; j++) { sublist.append(createLink(data[i].Children[j], false)); } } } } function createLink(data, isMain) { var link = $(document.createElement('a')); if (data.Link != null) { link.attr('href', data.Link); } else { link.attr('href', 'javascript:void(0);'); } if (data.IsExternal) { link.attr('target', '_blank'); } var txt = isMain ? data.Title.toUpperCase() : data.Title; if (data.IsExternal) { txt += ' <span style="font-size: xx-small;">(external link)</span>'; } link.html(txt); if (data.Image != null) { var img = $(document.createElement('img')); img.attr({ 'src': data.Image, 'alt': '' }); link.prepend(img); } var listitem = $(document.createElement('li')); listitem.append(link); return listitem; } function uncache(url) { var d = new Date(); var time = d.getTime(); return url + ((url.indexOf('?') == -1) ? '?' : '&') + 'time=' + time; } $(document).ready(function () { var url = '<%= AppHelper.LinkUrl("~/Home/GetMenuItems") %>'; $.post(uncache(url), function (data) { createMenu($('#navBar'), data); }); });

The code is a little tricksy, but not too bad.  There are two main functions, createMenu and createLink.  It should be fairly easy to see how these build the resultant DOM elements.  Obviously you’ll need some CSS in there to make it beautiful.  The uncache function gets around IE’s insistence on caching AJAX calls,  and the whole system is put into action in the document ready function.  In this case, it calls out to an ASP.NET MVC method, which consists of the following code.

public JsonResult GetMenuItems() { return Json(MenuItems.GetMenu(this._usersService)); }

This calls a static method on the MenuItems class, passing in a reference to the UsersService class (which is a wrapper around the Membership Provider, giving some handy utilities).

And the LINQ magic?  It happens here…

internal class MenuItem { public string Title { get; set; } public string Link { get; set; } public string Image { get; set; } public bool IsExternal { get; set; } public IList<MenuItem> Children { get; set; } public Func<bool> Included { get; set; } public MenuItem(string title, string link = null, string image = null, bool isExternal = false, IList<MenuItem> children = null, Func<bool> included = null) { this.Title = title; this.Link = link; this.Image = image; this.IsExternal = isExternal; this.Included = included; this.Children = children; } } internal static class MenuItems { private static IList<MenuItem> menuItems = null; private static IUsersService _usersService; private static object padlock = new object(); public static IList<MenuItem> GetMenu(IUsersService usersService) { if (menuItems == null) { lock (padlock) { _usersService = usersService; if (menuItems == null && _usersService != null) { BuildMenu(); } } } var thisMenu = menuItems .Where(x => x.Included == null || x.Included()) .Select(x => new MenuItem( title: x.Title, link: x.Link, image: x.Image, isExternal: x.IsExternal, children: x.Children == null ? null : x.Children .Where(y => y.Included == null || y.Included()).Select(y => new MenuItem( title: y.Title, link: y.Link, image: y.Image, isExternal: y.IsExternal )).ToList() )).ToList(); return thisMenu; } private static void BuildMenu() { menuItems = new List<MenuItem> { new MenuItem("Home", AppHelper.LinkUrl("~/")), new MenuItem("Who Are We", children: new List<MenuItem> { new MenuItem("What We Believe", AppHelper.LinkUrl("~/Page/WhatWeBelieve")), new MenuItem("What To Expect", AppHelper.LinkUrl("~/Page/WhatToExpect")), new MenuItem("Our Staff", AppHelper.LinkUrl("~/Staff")), new MenuItem("Location &amp; Map", AppHelper.LinkUrl("~/Home/ContactUs")) }), new MenuItem("Member's Lounge", children: new List<MenuItem> { new MenuItem("Connect Groups", AppHelper.LinkUrl("~/Home/ConnectGroups")), new MenuItem("Velocity Youth", AppHelper.LinkUrl("~/Page/Velocity")), new MenuItem("Children's Groups", AppHelper.LinkUrl("~/Page/ChildrensGroups")), new MenuItem("Audio Sermons", AppHelper.LinkUrl("~/Sermons")), new MenuItem("Event Calendar", AppHelper.LinkUrl("~/Calendar")), new MenuItem("Connect09", "http://connect09.com/", AppHelper.ResolveUrl("Jigsaw-16.png"), true), new MenuItem("Registration", AppHelper.LinkUrl("~/Account/Register"), included: delegate() { return !_usersService.IsLoggedIn(); }) }), new MenuItem("Baptisms", AppHelper.LinkUrl("~/Page/Baptism")), new MenuItem("Contact Us", AppHelper.LinkUrl("~/Home/ContactUs")), new MenuItem("Admin", included: delegate() { return _usersService.IsAdmin(); }, children: new List<MenuItem> { new MenuItem("List Users", AppHelper.LinkUrl("~/Account/ShowUsers"), included: delegate() { return _usersService.IsSiteAdmin(); }), new MenuItem("Front Page Manager", AppHelper.LinkUrl("~/FrontPageManager"), included: delegate() { return _usersService.IsSiteAdmin(); }), new MenuItem("List Invoices", AppHelper.LinkUrl("~/Payments/ShowInvoices"), included: delegate() { return _usersService.IsFinanceAdmin(); }) }) }; } }

Okay, there’s a fair bit there.  The MenuItem class should be fairly straightforward.  The Included property is the important one, as this will help determine if a menu item should be shown to this user.

In MenuItems, the first time GetMenu is called, we use a bit of locking to ensure the menu list is only set up once.  We then create ‘thisMenu’, by calling a (IMHO) nice bit of LINQ which iterates over and filters the possible menu items (set up in BuildMenu) depending on the calculated value of each item’s Included delegate.

While there are no doubt many other ways to achieve dynamically built menus using ASP.NET MVC, this suits my needs, and may work for you as well.

I’ve been looking at the idea of storing database schema changes along with source code in Mercurial.  Now, database change management seems to be a non-trivial issue.  There are some good tools around, including Visual Studio (Premium or Ultimate editions), but my weapon of choice ended up being FluentMigrator.

The next issue was that the project I was working on used the ASP.NET Membership Provider model, which requires (in its default implementation) a whole bucket load of tables, stored procedures and views.  These are normally taken care of by running aspnet_regsql.exe, but I really wanted a solution where I could just create a brand new blank database, run my application, and have the entire schema ready to go.  To achieve this meant that the aforementioned bucket of tables,  stored procedures and views would need to be created from a migration script.

After several attempts, I am able to say that the idea worked!  Further, if you feel that this is something you might like to try out, I have made the source code for the migration available at the URL below.

Now, before I finish, I must put in a heavy disclaimer.  I have not done extensive testing with this migration script, and do not know enough about the ASP.NET Membership Provider model to be sure that future updates to SQL Server and /or the .NET Framework will cause issues here.  All I can say is that it works for me, and has worked using SQL Server 2008, SQL Server 2008 R2, .NET 3.5 SP1 and .NET 4.

Here’s the URL for the code: http://cid-6bd010a81465a625.skydrive.live.com/redir.aspx?page=self&type=3&client=wnf&resId=6BD010A81465A625!150

A brief update

May 17, 2010

I recently received an email from Adam Cogan.  Four simple words said, “You should blog more.”

Now, when Adam Cogan speaks, I listen.  Unless he’s talking about SharePoint or TFS, in which case I might tune out a little.  So I guess you could say I listen to a small portion of what he says.

Nevertheless, I had been thinking that my blogging was a little lax of late anyway, so thought I would take this opportunity to write a little about what I’ve been up to.

My job, which for a few years has kept me busy in the land of .NET / C# / WPF, has recently taken me in a new direction.  I’m working on developing applications for Panasonic’s Vieracast platform.  The applications are developed using object-oriented Javascript, which is something I never even knew existed until a few months ago.  Shame on me!  My first few efforts resulted in code that I hope never sees the light of day, but I’ve started leveraging tools and principles learned from my years of .NET coding and am now cranking out some pretty neat stuff.  When the code goes into production I might blog a little more about the project, but for now it’s all under pretty strict NDA.

The Glenmore Park Anglican Church website hasn’t seen too many updates recently, and the reason is that I’m spending time refactoring the underlying models and structures to make future enhancements easier.  One part of this is to put the database schema under source control by using FluentMigrator to look after versioning.  Because I’m using Microsoft’s ASP.NET Membership Provider model for authentication (with the database schema associated with this), I’ve been spending some time creating the ASP.NET Membership Provider database schema as a FluentMigrator migration.  Once this is complete, I’ll make the code available online for those who would like to add this capability to their projects.

The last exciting bit of news (for me, at least) is the purchase of brand spanking new Apple PowerBook Pro (Intel i7 CPU, 8GB RAM, 17” 1920x1200 screen).  As I write, the machine is “Prepared for Shipment”.  Moving to Mac has been something that’s taken a lot of thought, but recent experiences with running virtual machines using VirtualBox (plus the announcement that Steam is now available for Mac) have given me the confidence to move over.  At this stage I intend to run OSX as the host environment and use Win7 VMs for development, and hope to write a couple articles detailing how this pans out.

So there you go, Adam.  Until next time…

I recently upgraded my mobile phone to a Nokia N97 mini, which runs the S60 5th edition platform.  I had previously attempted developing software for the S60 platform 3rd edition – without much success – and thought I’d give the new platform a go.

However, while there are several tutorials out there for people starting to develop with the Qt toolset (particularly on the Qt site), there seemed to be little in the way of basic tutorials for native C++ development.  So I shall attempt to present a simple tutorial here.

What do I need?

You will need to download and install the following tools:

You will also need the Java Runtime Engine installed (I’ve got the latest version and it all works fine), but you’ve probably already got that.

One issue I found was that development on a Virtual PC image didn’t work, mainly due to issues with the N97 Emulator failing on startup.  I don’t know how this would work with VMWare, and your mileage may vary anyway.

What are we making?

The tutorials I have seen for Qt basically create a listbox, a textbox, a button to add the text from the textbox into the listbox, and another button to clear the listbox.  This sounds like a good start, but we’ll make it with a slight change, which you’ll see as we go on.

Let’s begin…

Okay, start by launching Carbide.c++.  Being an Eclipse-based IDE, it will ask for a workspace to use.  Just select the default for now.

Once Carbide has started, create a New Symbian OS C++ Project.  From the template options, choose the S60 GUI Application with UI Designer.  Following this, type in your project name (say, MyTextList).  Next, you will be asked to select your target SDK and Build Configurations.  If you only have one SDK installed, this should be the default, otherwise you will need to choose your target.  Next, select your target language.

Following this, you will be given the option to select a design template.  I have chosen to go with the Empty design, to give a better overview of the design process.  Next, you can change the Container Name and Type if you wish.  Just accept the defaults and all should be fine.  On the Basic Settings screen, feel free to change settings if you wish, or just accept the defaults.  The same applies to the Project Directories, and that (finally) finishes the Project Wizard.

Designing the UI

In the UI Designer (MyTextListContainer.uidesign), you can change the window title if you wish, just by clicking on the text.  Change it to My Text List.  Now, in the main area of the window, we want to drag a List Box from the Controls group.  Note, if you add anything else (such as a Button), the List Box will disappear from this list.  Because the List Box takes up the entire client area, we will deviate from the Qt design and put the Add Text / Clear Items options in the menu rather than as separate controls.

We will also need a dialog to allow the user to input text, so drag a Single-line Data Query (from the Notes and Dialogs group) to the bottom panel of the designer.  Select this in the lower panel, then click on the Text Edit control in the dialog (where it says Edit text).  Go to the Properties tab, and change the maximum length parameter to 20.

Select the optionsMenu control from the lower panel in the designer, which will give you the ability to type menu items.  Add a couple items for Add Text and Clear Items.

Right-click on the Add Text menu item and select the Handle ‘selected’ Event option.  Say Yes to the dialog box and we can start throwing in some code.

Handling the Add Text event

Inside the HandleAdd_TextMenuItemSelectedL method, we want to open an edit dialog, allow the user to enter text,and if this returns okay, insert the text into the list.  This bit took the longest to work out, but here’s the code:

TBool CMyTextListContainerView::HandleAdd_TextMenuItemSelectedL( TInt aCommand )
    {
    _LIT(STRFMT, "\t%S\t\t");
    
    TBuf<20> text;
    TBuf<30> itemText;
    
    TInt retval = this->RunDataQuery1L(text, true, NULL);
    if (retval == EEikBidOk)
        {
            CAknSingleStyleListBox *listBox = this->iMyTextListContainer->ListBox();
            
            MDesC16Array *textArray = listBox->Model()->ItemTextArray();
            CDesCArray *itemList = static_cast<CDesCArray*>(textArray);
            
            itemText.Format(STRFMT, &text);
            itemList->AppendL(itemText);
            
            listBox->HandleItemAdditionL();
        }
    return ETrue;
    }

This starts by setting up a format string for the list item and a few local variables.  We then open the text input dialog.  The return value for this depends on whether the OK or Cancel button was pressed, so we check for the OK value.  Note that we don’t need to check for a null value in the text field, as the OK button is not displayed when the text box is empty.

We then get a reference to the List Box control, then to it’s Item Text Array (note that S60 development generally follows an MVC (Model-View-Controller) framework, so we add the item to the Text Box’s model, not the Text Box itself).  This reference is then cast to a string array.

The input text is then written out to the correct format (see this page for a description of the various formats) and appended to the item list.  We then let the List Box know that it needs to update itself with a call to HandleItemAdditionL().

Check that the build is targeted on the Emulator build (right-click the project, and check the Build Configurations > Set Active), and build and run the app.  Accept any defaults in any dialogs that pop up, and you should see the emulator pop up running your app!  At this point you should be able to add items to the text box, but the Clear Items menu item won’t work.  Exit the emulator and we’ll finish things off.

Clearing the List Box

Jump back to the MyTextListContainer.uidesign window.  Click on the optionsMenu in the lower pane, then right-click the Clear Items menu item and Handle the ‘selected’ Event.  Again, go to the code, and add the following to the method:

TBool CMyTextListContainerView::HandleClear_ItemsMenuItemSelectedL( TInt aCommand )
    {
    CAknSingleStyleListBox *listBox = this->iMyTextListContainer->ListBox();
    
    MDesC16Array *textArray = listBox->Model()->ItemTextArray();
    CDesCArray *itemList = static_cast<CDesCArray*>(textArray);
    
    itemList->Reset();
    listBox->Reset();
    
    listBox->HandleItemRemovalL();
    listBox->DrawNow();
    
    return ETrue;
    }

Again, we grab a reference to the List Box and its Item Text Array.  We reset the Item List, reset the List Box, and then call HandleItemRemovalL().  One additional step I needed (although I’m not sure why it is needed) was to call DrawNow() on the List Box.

Once again, run this up in the emulator and make sure everything works as expected.

Deploying to your phone

To create a file suitable for deployment to your phone, right-click the project in the Project Explorer, go to Build Configurations > Set Active, and select Phone Release (GCCE) for your SDK.  To see the details of the build configuration, right-click the project, select Properties, and go to Carbide.c++ > Build Configurations.  The defaults should be fine.

Build the project, and then browse (in the Project Explorer) to the sis folder.  Here, you should find the MyTextList.sis and MyTextList.sisx files.  Assuming you have previously connected you phone to Ovi Suite and have the Suite running on your PC, double-click the .sisx file.  This should start the Ovi Suite program installer automatically and allow you to install the application on your phone.

For some reason on the N97 mini the phone complained that the app may not be compatible with my phone.  This may mean that I should be running the normal S60 5th edition SDK rather than the N97 SDK, but it all works anyway.

I hope this helps with your S60 development.  Please let me know what you think.

Binding in WPF is a massive improvement on previous efforts, and the power offered by the INotifyPropertyChanged interface in its ability to transform a POCO (Plain Old CLR Object) into a binding partner is pretty magical.

So what does INotifyPropertyChanged require of us?  Not much, really.  The basic implementation of the interface is as simple as the following line of code:

public event PropertyChangedEventHandler PropertyChanged;

We can now add the following to our property setters to cause WPF to notice any value changes:

private string foo;

public string Foo
{
    get { return this.foo; }
    set
    {
        if (this.foo != value)
        {
            this.foo = value;
            this.PropertyChanged(this, new PropertyChangedEventArgs("Foo"));
        }
    }
}

But what happens if the PropertyChanged event hasn’t had a handler wired in?  PropertyChanged is null, so the setter falls over.  No problem, here’s attempt #2:

set
{
    if (this.foo != value)
    {
        this.foo = value;
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs("Foo"));
        }
    }
}

OK, so since we’ll probably have more than one property in a class, we can wrap this up in a helper method.  Enter attempt #3:

private string foo;

public string Foo
{
    get { return this.foo; }
    set
    {
        if (this.foo != value)
        {
            this.foo = value;
            this.OnPropertyChanged("Foo");
        }
    }
}

private void OnPropertyChanged(string label)
{
    if (this.PropertyChanged != null)
    {
        this.PropertyChanged(this, new PropertyChangedEventArgs(label));
    }
}

We can do better, though.  Notice that every time this method is called, a new PropertyChangedEventArgs instance is being created.  This instance is dependent only on the label of the property, so maybe we can bring an EventArg cache into play.  Attempt #4 coming up:

private Dictionary<string, PropertyChangedEventArgs> eventArgs = new Dictionary<string, PropertyChangedEventArgs>();

private void OnPropertyChanged(string label)
{
    if (this.PropertyChanged != null)
    {
        if (!this.eventArgs.ContainsKey(label))
        {
            this.eventArgs.Add(label, new PropertyChangedEventArgs(label));
        }

        this.PropertyChanged(this, this.eventArgs[label]);
    }
}

In fact, since the PropertyChangedEventArgs depends ONLY on the label, there’s no reason why we can’t make this a static (shared) collection.  Attempt #5:

private static Dictionary<string, PropertyChangedEventArgs> eventArgs = new Dictionary<string, PropertyChangedEventArgs>();

private void OnPropertyChanged(string label)
{
    if (this.PropertyChanged != null)
    {
        if (!MyClass.eventArgs.ContainsKey(label))
        {
            MyClass.eventArgs.Add(label, new PropertyChangedEventArgs(label));
        }

        this.PropertyChanged(this, MyClass.eventArgs[label]);
    }
}

We’re getting close now, I can feel it!  But what happens if a couple threads try to update a property at the same time?  We don’t want to fail because two identical keys are added, so let’s add some locking.  Attempt #6:

private static Dictionary<string, PropertyChangedEventArgs> eventArgs = new Dictionary<string, PropertyChangedEventArgs>();

private void OnPropertyChanged(string label)
{
    if (this.PropertyChanged != null)
    {
        if (!MyClass.eventArgs.ContainsKey(label))
        {
            lock (MyClass.eventArgs)
            {
                MyClass.eventArgs.Add(label, new PropertyChangedEventArgs(label));
            }
        }

        this.PropertyChanged(this, MyClass.eventArgs[label]);
    }
}

Of course, there is one problem here too.  What if two threads arrive at the lock at the same time?  One will get in and add the new item, while the other will wait.  When the lock is released, the other thread will get the lock and … add the same item!  So, one more check is needed inside the lock.  Attempt #7:

private static Dictionary<string, PropertyChangedEventArgs> eventArgs = new Dictionary<string, PropertyChangedEventArgs>();

private void OnPropertyChanged(string label)
{
    if (this.PropertyChanged != null)
    {
        if (!MyClass.eventArgs.ContainsKey(label))
        {
            lock (MyClass.eventArgs)
            {
                if (!MyClass.eventArgs.ContainsKey(label))
                {
                    MyClass.eventArgs.Add(label, new PropertyChangedEventArgs(label));
                }
            }
        }

        this.PropertyChanged(this, MyClass.eventArgs[label]);
    }
}

And that should do it!  Have fun.

This is the second in a series of articles in which I walk through the process of creating a WCF service and client application for streaming large (several gigabytes) files across a network.  The articles are presented as below:

  • Creating the Server Application - 1
  • Creating the Server Application - 2 (this article)
  • Creating the Client Application
  • Creating an Installer for the Server
  • Adding Extra Functionality

Please note the disclaimer in the first article before implementing this code.

Finishing the Server Application

With all of the classes for WCF defined, it is time to implement the guts of the service.  Early in part 1 of this series, we created a WCF Service Library called WCFFileStreamerService.  In here, delete IService1.cs and Service1.cs, and add a reference to the WCFFileStreamerTypes project.  Add a new class to this service called FileTransferService which implements the IFileTransferService interface.

The License Key

You may remember that each Request message in our service had a LicenseKey property.  While this can be implemented in a variety of ways, we'll just use a static string for now.  Open the Properties window for the WCFFileStreamerService project, and in Settings, add a default settings file (if required), and then add an Application-scoped string named LicenseKey.  Set this value to a secret key, such as 'p@ssw0rd'.  Sneaky, huh?  While you're at it, add another string named StreamStoragePath and set it to the path where files will be streamed from / to.  This will eventually be set specifically for the server, but for testing on your development box it will need to be a local path.  We're now ready to implement our service methods.

The Service Methods

Go back to the FileTransferService class.  Each method should test the LicenseKey property of the request, so add the following code to each method (of course, you could add a private method and call that from each method - the choice is up to you):

if (request.LicenseKey != Settings.Default.LicenseKey) 
{ 
    const string reason = "Incorrect License Key provided."; 
    throw new FaultException<string>(reason, reason); 
} 

GetFreeSpace

The GetFreeSpace method is a handy method for getting the free space on the target drive on the server.  The code is as follows:

long retval = 0;

var storagePath = Settings.Default.StreamStoragePath;
var storageDrive = Path.GetPathRoot(storagePath).Substring(0, 2);

try
{
    SelectQuery query = new SelectQuery("SELECT * FROM Win32_LogicalDisk WHERE Name = '"
+ storageDrive + "'"); ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); foreach (ManagementObject mo in searcher.Get()) { retval = Convert.ToInt64(mo["FreeSpace"].ToString()); } } catch { } GetFreeSpaceResponse response = new GetFreeSpaceResponse(); response.FreeSpace = retval; return response;

You'll need a reference to System.Management for the SelectQuery & ManagementObject stuff.

Upload Service Methods

The upload portion of the service has three methods: an InitializeUpload method to prepare the server for the stream to come, a GetUploadStatus method so that the client can check on the upload progress, and a CancelUploadAync method so that the client can cancel the upload.  These last two methods are not required for downloading for reasons that will hopefully become apparent.  Here are the methods:

public InitUploadResponse InitializeUpload(InitUploadRequest request)
{
    ... LicenseKey test goes here ...

    var status = new InitializeStatus();
    status.UploadID = Guid.NewGuid();
    status.Length = request.Length;
    status.ServerLength = 0L;

    UploadStatusManager.Instance.AddNewItem(status.UploadID);

    FileInfo fi = new FileInfo(Path.Combine(
Settings.Default.StreamStoragePath, request.FileName)); if (fi.Exists) { if (fi.Length < request.Length) { status.Status = InitStatus.ExistsAndResumable; status.ServerLength = fi.Length; } else if (fi.Length > request.Length) { status.Status = InitStatus.ExistsNonMatchingHash; } else { // Lengths match, so check hash... byte[] hash; using (Stream s = fi.OpenRead()) { hash = SHA1Helper.SHA1Helper.HashFile(s); } if (SHA1Helper.SHA1Helper.CompareHashCodes(hash, request.FileHash)) { status.Status = InitStatus.ExistsMatchingHash; } else { status.Status = InitStatus.ExistsNonMatchingHash; } } } else { status.Status = InitStatus.Ready; } InitUploadResponse response = new InitUploadResponse(); response.Status = status; return response; } public GetUploadStatusResponse GetUploadStatus(GetUploadStatusRequest request) { ... LicenseKey test goes here ... UploadStatusManager manager = UploadStatusManager.Instance; UploadStatusMessage message = new UploadStatusMessage(); try { message.Status = manager[request.UploadID].Status; message.PercentComplete = manager[request.UploadID].PercentComplete; message.Message = manager[request.UploadID].Message; } catch (InvalidOperationException ex) { throw new FaultException<string>(ex.Message, ex.Message); } GetUploadStatusResponse response = new GetUploadStatusResponse(); response.StatusMessage = message; return response; } public void CancelUploadAsync(CancelUploadAsyncRequest request) { ... LicenseKey test goes here ... try { UploadStatusManager.Instance[request.UploadID].CancelRequested = true; } catch (InvalidOperationException ex) { throw new FaultException<string>(ex.Message, ex.Message); } }

Support Classes

OK, so you may have noticed there are a couple of missing classes used on the above code.  The UploadStatusManager is used by the service to keep track of uploads.  Here's the code:

internal class UploadStatusManagerItem
{
    public UploadStatus Status { get; set; }
    public int PercentComplete { get; set; }
    public string Message { get; set; }
    public bool CancelRequested { get; set; }

    public UploadStatusManagerItem()
    {
        this.Status = UploadStatus.Idle;
        this.CancelRequested = false;
    }
}

internal class UploadStatusManager
{
    private static UploadStatusManager _manager = null;
    private Dictionary<Guid, UploadStatusManagerItem> _statusItems;

    public static UploadStatusManager Instance
    {
        get
        {
            if (_manager == null)
            {
                _manager = new UploadStatusManager();
            }

            return _manager;
        }
    }

    private UploadStatusManager()
    {
        _statusItems = new Dictionary<Guid, UploadStatusManagerItem>();
    }

    public UploadStatusManagerItem AddNewItem(Guid id)
    {
        var item = new UploadStatusManagerItem();
        _statusItems.Add(id, item);
        return item;
    }

    public UploadStatusManagerItem this[Guid id]
    {
        get
        {
            if (!_status.Items.ContainsKey(id))
            {
                throw new InvalidOperationException(
"Item must be added before it is accessed."); } return _statusItems[id]; } } }

You will also need to add a new project (so it can be shared with the client) called SHA1Helper, containing the following class:

public static class SHA1Helper
{
    public static byte[] HashFile(Stream file)
    {
        var sha1 = new SHA1CryptoServiceProvider();
        byte[] retval = sha1.ComputeHash(file);
        sha1.Clear();

        return retval;
    }

    public static bool CompareHashCodes(byte[] hash1, byte[] hash2)
    {
        if (hash1.Length != hash2.Length)
        {
            return false;
        }

        for (int i = 0; i < hash1.Length; i++)
        {
            if (hash1[i] != hash2[i])
            {
                return false;
            }
        }

        return true;
    }
}

Download Service Methods

The InitializeDownload method ensures that the requested file does exist, and returns the size and hash to the client so that it can prepare for the download.

InitDownloadMessage message = new InitDownloadMessage();

string fileName = Path.Combine(Settings.Default.StreamStoragePath, request.FileName);
FileInfo fi = new FileInfo(fileName);

if (fi.Exists)
{
    message.FileName = Path.GetFileName(fi.Name);
    message.Length = fi.Length;

    if (request.CalculateHash)
    {
        using (var fs = fi.OpenRead())
        {
            message.FileHash = SHA1Helper.SHA1Helper.HashFile(fs);
        }
    }

    message.IsValidFile = true;
}
else
{
    message.IsValidFile = false;
}

InitDownloadResponse response = new InitDownloadResponse();
response.DownloadMessage = message;

return response;

And finally, the Streaming Classes

One more class, called StreamedFileTransferService, implementing the IStreamedFileTransferService interface. The two methods here (UploadStream and DownloadStream) include the same License Key checks as above.  The UploadStream method receives a Stream object (possibly with its Location already set to the length of an existing server-side file, to allow resume), and simply reads and appends data between the incoming (client, streamed) file and the local (server) file.  The DownloadStream method provides a Stream of the requested file (possibly with its Location set to a requested offset) to the client.

Here’s the code:

public UploadStreamResponse UploadStream(UploadStreamRequest request)
{
    … LicenseKey test goes here …

    UploadStatusManagerItem statusItem;
    try
    {
        statusItem = UploadStatusManager.Instance[request.UploadID];
    }
    catch (InvalidOperationException ex)
    {
        throw new FaultException<string>(ex.Message, ex.Message);
    }

    statusItem.Message = “Uploading”;
    statusItem.Status = UploadStatus.Uploading;
    statusItem.PercentComplete = 0;

    bool result = false;

    string fileName = Path.Combine(Settings.Default.StreamStoragePath, request.FileName);
    byte[] buffer = new byte[65536];
    long totalread = 0L;
    int read;

    FileMode createMode = FileMode.CreateNew;
    switch (request.Mode)
    {
        case UploadMode.New:
            createMode = FileMode.CreateNew;
            break;

        case UploadMode.Overwrite:
            createMode = FileMode.Create;
            break;

        case UploadMode.Resume:
            createMode = FileMode.Append;
            break;
    }

    using (FileStream fs = new FileStream(fileName, createMode, FileAccess.Write, FileShare.None))
    {
        if (request.Mode == UploadMode.Resume)
        {
            fs.Seek(request.StartOffset, SeekOrigin.Begin);
        }

        while (((read = request.DataStream.Read(buffer, 0, buffer.Length)) > 0) && !statusItem.CancelRequested)
        {
            fs.Write(buffer, 0, read);
            totalread += read;

            statusItem.PercentComplete = (int)((totalread * 100) / request.Length);
            statusItem.Message = “Uploading (“ + statusItem.PercentComplete + “% complete)”;
        }

        fs.Flush();

        if (statusItem.CancelRequested)
        {
            statusItem.Message = “Upload Cancelled”;
            statusItem.Status = UploadStatus.Complete;
        }
        else
        {
            statusItem.PercentComplete = 100;
            statusItem.Status = UploadStatus.Complete;
            result = true;
        }
    }

    request.DataStream.Close();

    UploadResultMessage message = new UploadResultMessage();

    if (!statusItem.CancelRequested)
    {
        statusItem.PercentComplete = 100;
        statusItem.Status = UploadStatus.CalculatingHash;
        statusItem.Message = “Calculating hash for Server file”;
        byte[] hash;
        using (Stream s = File.OpenRead(fileName))
        {
            hash = SHA1Helper.SHA1Helper.HashFile(s);
        }

        result = SHA1Helper.SHA1Helper.CompareHashCodes(hash, request.FileHash);

        message.Result = result ? UploadResult.Success : UploadResult.Failure;
        message.Message = result ? “Upload Complete” : “Upload Failed – Hash values do not match”;
    }
    else
    {
        message.Result = UploadResult.Failure;
        message.Message = “Upload Cancelled”;
    }

    UploadStreamResponse response = new UploadStreamResponse();
    response.ResultMessage = message;

    return response;
}

public DownloadStreamResponse DownloadStream(DownloadStreamRequest request)
{
    … LicenseKey test goes here …

    DownloadStreamResponse response = new DownloadStreamResponse();

    string fileName = Path.Combine(Settings.Default.StreamStoragePath, request.FileName);
    FileInfo fi = new FileInfo(fileName);

    if (fi.Exists)
    {
        response.FileName = request.FileName;
        response.Length = request.Length;
        response.FileHash = request.FileHash;
        response.DataStream = (Stream)fi.OpenRead();

        response.StartOffset = response.DataStream.Seek(request.StartOffset, SeekOrigin.Begin);

        return response;
    }

    const string fileReason = “Invalid file requested”;
    throw new FaultException<string>(fileReason, fileReason);
}

Service Configuration

The final task to set up the service is to edit the App.config.  I won’t go into full details, but here’s the basic setup:

  • FileTransferService: NetTcp, Port 8000, Security Mode = None
  • StreamedFileTransferService: NetTcp, Port 8000, Transfer Mode = Streamed, Security Mode = None

It’s probably a dangerous thing to set the Security Mode to None, but this was chosen in my case so that the client could access the server over the Internet without worrying about user credentials or certificates.  I do not recommend doing this in most situations, and it is up to the developer to choose a relevant security model for their specific application.

After all that, you should be able to get your services up and running.  If you get a message about an AddressAccessDeniedException, check out http://go.microsoft.com/fwlink/?LinkId=70353 and open up the relevant ports.

In the next article, I’ll move a bit quicker – I’ve noticed the last couple of posts have been a bit on the long side.  To this end, I have created a repository over at http://bitbucket.org/daggmano/wcffilestreamer/ so you can grab the source code as required.

In this series of articles, I will walk through the process of creating a WCF service and client application which can be used to transfer large files (several gigabytes or more) between the client and the server.  The process will be broken down into the following stages:

  • Creating the Server Application - 1 (this article)
  • Creating the Server Application - 2
  • Creating the Client Application
  • Creating an Installer for the Server
  • Adding Extra Functionality

Disclaimer

Before we begin, it’s probably a good idea to say that I am not an expert on WCF.  There are probably many other ways to implement this solution, and I do not claim that what is presented here is the best, or even one of the better, implementations.  That being said, it works, and that’s a good start. Please feel free to write a comment if you feel there are areas where the code needs improvement.

Creating the Server Application

I will be using Visual Studio 2008 and .NET 3.5 for this tutorial.  As is my preference, I’ll start by creating a new Blank Visual Studio Solution, called WCFFileStreamer.  In here, add a new WCF Service Library, named WCFFileStreamerService.

Shared Types

By default, the WCF Service Library includes an Interface called IService1 to define the Service and Data Contracts.  The ‘correct’ method of sharing these types between the client and server is to import a service reference to the client, but I have run into problems with this on previous projects so will manually configure this interaction.  A couple advantages of this are that it enables more control over the interactions, and that it gives us a better feel for what happens ‘under the covers’ in WCF.

Add a new project to the solution, in this case just a Class Library named WCFFileStreamerTypes.  There will be a number of enums, classes and interfaces to create, but they’re all quite small and have no logic in them, so feel free to either create one file per enum / class / interface, or just throw everything in one file.  You’ll also need to add references to System.ServiceModel and System.Runtime.Serialization.  Here are the enums you’ll need:

[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
public enum UploadStatus : int
{
    [EnumMember] Complete = 0,
    [EnumMember] Uploading = 1,
    [EnumMember] CalculatingHash = 2,
    [EnumMember] Idle = 3
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
public enum InitStatus : int
{
    [EnumMember] Ready = 0,
    [EnumMember] ExistsAndResumable = 1,
    [EnumMember] ExistsMatchingHash = 2,
    [EnumMember] ExistsNonMatchingHash = 3
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
public enum UploadMode : int
{
    [EnumMember] New = 0,
    [EnumMember] Overwrite = 1,
    [EnumMember] Resume = 2
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
public enum UploadResult : int
{
    [EnumMember] Success = 0,
    [EnumMember] Failure = 1
}

The following classes will be required as Data Contracts:

[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
[KnownType(typeof(InitStatus))]
public class InitializeStatus
{
    [DataMember(IsRequired = true)] public InitStatus Status { get; set; }
    [DataMember(IsRequired = true)] public Guid UploadID { get; set; }
    [DataMember(IsRequired = true)] public long Length { get; set; }
    [DataMember(IsRequired = true)] public long ServerLength { get; set; }
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
[KnownType(typeof(UploadResult))]
public class UploadResultMessage
{
    [DataMember(IsRequired = true)] public UploadResult Result { get; set; }
    [DataMember(IsRequired = true)] public string Message { get; set; }
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
[KnownType(typeof(UploadStatus))]
public class UploadStatusMessage
{
    [DataMember(IsRequired = true)] public UploadStatus Status { get; set; }
    [DataMember(IsRequired = true)] public int PercentComplete { get; set; }
    [DataMember(IsRequired = true)] public string Message { get; set; }
}
[DataContract(Namespace = "http://schemas.my.service.com/2009/07/filestreamer/")]
public class InitDownloadMessage
{
    [DataMember(IsRequired = true)] public bool IsValidFile { get; set; }
    [DataMember(IsRequired = true)] public string FileName { get; set; }
    [DataMember(IsRequired = true)] public long Length { get; set; }
    [DataMember(IsRequired = true)] public byte[] FileHash { get; set; }
}

Add the following Message Contracts:

[MessageContract(IsWrapped = false)]
public class InitUploadRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageBodyMember] public string FileName { get; set; }
    [MessageBodyMember] public long Length { get; set; }
    [MessageBodyMember] public byte[] FileHash { get; set; }
}
[MessageContract(IsWrapped = false)]
[KnownType(typeof(InitializeStatus))]
public class InitUploadResponse
{
    [MessageBodyMember] public InitializeStatus Status { get; set; }
}
[MessageContract(IsWrapped = false)]
[KnownType(typeof(UploadMode))]
public class UploadStreamRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageHeader] public Guid UploadID { get; set; }
    [MessageHeader] public UploadMode Mode { get; set; }
    [MessageHeader] public string FileName { get; set; }
    [MessageHeader] public long Length { get; set; }
    [MessageHeader] public long StartOffset { get; set; }
    [MessageHeader] public byte[] FileHash { get; set; }
    [MessageBodyMember] public Stream DataStream { get; set; }
}
[MessageContract(IsWrapped = false)]
[KnownType(typeof(UploadResultMessage))]
public class UploadStreamResponse
{
    [MessageHeader] public UploadResultMessage ResultMessage { get; set; }
}
[MessageContract(IsWrapped = false)]
public class GetUploadStatusRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageBodyMember] public Guid UploadID { get; set; }
}
[MessageContract(IsWrapped = false)]
[KnownType(typeof(UploadStatusMessage))]
public class GetUploadStatusResponse
{
    [MessageBodyMember] public UploadStatusMessage StatusMessage { get; set; }
}
[MessageContract(IsWrapped = false)]
public class CancelUploadAsyncRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageBodyMember] public Guid UploadID { get; set; }
}
[MessageContract(IsWrapped = false)]
public class InitDownloadRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageBodyMember] public string FileName { get; set; }
    [MessageBodyMember] public bool CalculateHash { get; set; }
}
[MessageContract(IsWrapped = false)]
[KnownType(typeof(InitDownloadMessage))]
public class InitDownloadResponse
{
    [MessageBodyMember] public InitDownloadMessage DownloadMessage { get; set; }
}

[MessageContract(IsWrapped = false)]
public class DownloadStreamRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
    [MessageHeader] public string FileName { get; set; }
    [MessageHeader] public long Length { get; set; }
    [MessageHeader] public long StartOffset { get; set; }
    [MessageHeader] public byte[] FileHash { get; set; }
}
[MessageContract(IsWrapped = false)]
public class DownloadStreamResponse
{
    [MessageHeader] public string FileName { get; set; }
    [MessageHeader] public long Length { get; set; }
    [MessageHeader] public long StartOffset { get; set; }
    [MessageHeader] public byte[] FileHash { get; set; }
    [MessageBodyMember] public Stream DataStream { get; set; }
}
[MessageContract(IsWrapped = false)]
public class GetFreeSpaceRequest
{
    [MessageHeader(MustUnderstand = true)] public string LicenseKey { get; set; }
}
[MessageContract(IsWrapped = false)]
public class GetFreeSpaceResponse
{
    [MessageBodyMember] public long FreeSpace { get; set; }
}

A couple of things are worth noticing in the above Message Contracts.  The first thing is that every request includes a LicenseKey string in the Header.  This is a fairly easy way to provide some security to our service.  You can either hard code a word or phrase into your service and client (as we will do here), or you could use some other method.

The second thing to notice is the liberal use of MessageHeader parameters in the UploadStreamRequest / UploadStreamResponse contracts.  This reason for this will become clear later.

Finally we can create the Service Contracts.  Note that there are two interfaces specified for the server.  I will talk about the reasons for this more in the next article.

[ServiceContract(Name = "StreamedFileTransferService",
Namespace = "http://www.my.service.com/2009/07/filestreamer/")] public interface IStreamedFileTransferService { [OperationContract] [FaultContract(typeof(string))] UploadStreamResponse UploadStream(UploadStreamRequest request); [OperationContract] [FaultContract(typeof(string))] DownloadStreamResponse DownloadStream(DownloadStreamRequest request); }
[ServiceContract(Name = "FileTransferService",
Namespace = "http://www.my.service.com/2009/07/filestreamer/")] public interface IFileTransferService { [OperationContract] [FaultContract(typeof(string))] InitUploadResponse InitializeUpload(InitUploadRequest request); [OperationContract] [FaultContract(typeof(string))] GetUploadStatusResponse GetUploadStatus(GetUploadStatusRequest request); [OperationContract] [FaultContract(typeof(string))] void CancelUploadAsync(CancelUploadAsyncRequest request); [OperationContract] [FaultContract(typeof(string))] InitDownloadResponse InitializeDownload(InitDownloadRequest request); [OperationContract] [FaultContract(typeof(string))] GetFreeSpaceResponse GetFreeSpace(GetFreeSpaceRequest request); }

We will also provide a couple of interfaces for the client side, including asynchronous pattern calls (I told you we'd be getting dirty with manual configuration).  Because the code for this gets pretty long and repetitive, I’ll just show the first few definitions here.

[ServiceContract(Name = "StreamedFileTransferClient",
Namespace = "http://www.my.service.com/2009/07/filestreamer/")] public interface IStreamedFileTransferClient { [OperationContract( Action = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/UploadStream"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/UploadStreamResponse"
)] [FaultContract(typeof(string))] UploadStreamResponse UploadStream(UploadStreamRequest request); [OperationContract(AsyncPattern = true, Action = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/UploadStream"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/UploadStreamResponse"
)] IAsyncResult BeginUploadStream(UploadStreamRequest request, AsyncCallback callback,
object asyncState); UploadStreamResponse EndUploadStream(IAsyncResult result); [OperationContract( Action = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/DownloadStream"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/DownloadStreamResponse"
)] [FaultContract(typeof(string))] DownloadStreamResponse DownloadStream(DownloadStreamRequest request); [OperationContract(AsyncPattern = true, Action = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/DownloadStream"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
StreamedFileTransferService/DownloadStreamResponse"
)] IAsyncResult BeginDownloadStream(DownloadStreamRequest request, AsyncCallback callback,
object asyncState); DownloadStreamResponse EndDownloadStream(IAsyncResult result); }
[ServiceContract(Name = "FileTransferClient",
Namespace = "http://www.my.service.com/2009/07/filestreamer/")] public interface IFileTransferClient { [OperationContract( Action = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/InitializeUpload"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/InitializeUploadResponse"
)] [FaultContract(typeof(string))] InitUploadResponse InitializeUpload(InitUploadRequest request); [OperationContract(AsyncPattern = true, Action = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/InitializeUpload"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/InitializeUploadResponse"
)] IAsyncResult BeginInitializeUpload(InitUploadRequest request, AsyncCallback callback,
object asyncState); InitUploadResponse EndInitializeUpload(IAsyncResult result); // ... Same pattern for all contracts except the following: [OperationContract( Action = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/CancelUpload"
, ReplyAction = "http://www.my.service.com/2009/07/filestreamer/_
FileTransferService/CancelUploadResponse"
)] [FaultContract(typeof(string))] void CancelUploadAsync(CancelUploadAsyncRequest request); }

Note that the ‘_’ line continuation characters are only to allow the code to fit into my blog – these lines should be on a single line.

That should do for now.  Make sure the solution builds (it won't do anything yet), and check out the next article to get the service up and running.

It seems that every time I listen to a software development podcast (which is often), somebody is talking up the benefits of Test Driven Development (TDD).  TDD does indeed have many advantages.  It focuses our efforts by ensuring that the code we write is written for a reason, i.e. to make a test, which has been derived from the requirements specification, pass.  This is in keeping with the YAGNI (You Ain’t Gonna Need It) principle.

TDD also allows a safety net for refactoring, to ensure that, after a refactoring, the code is no more broken than it was before the refactoring.  TDD is also likely to lead to ‘better’ code - more modular, cleaner, with less unwanted dependencies between classes.  And all of these are worthy goals, and perhaps are reasons in themselves for adopting (at least partially) the TDD process.

Where I take exception, however, is that it is somehow implied (and even stated) that TDD cannot help but ensure that the code we write will be ‘correct’.  Wikipedia states that, “Test-driven development offers more that just simple validation of correctness”, which would indicate that TDD does indeed offer validation of correctness.  But it doesn’t.  Let me illustrate the point.

I need a method that will return the square of a given integer.  That seems simple, so I write a unit test.

[Test]
public void TestThatTwoSquaredIsFour()
{
    Assert.IsTrue(Square(2) == 4);
}

Note that this is psuedo-code, but you get the idea.  Okay, let’s have a crack at writing the method to satisfy this test.  Again, Wikipedia gives us some guidance.

The next step is to write some code that will cause the test to pass... It is important that the code written is only designed to pass the test...

So, with this in mind, here is my code.

public int Square(int n)
{
    return 4;
}

This is the simplest code that I can write that will pass the test.  And the test will pass.  Since this is the case, we obviously have not specified the requirements clearly enough (through tests), so we'll add another test.

[Test]
public void TestThatThreeSquaredIsNine()
{
    Assert.IsTrue(Square(3) == 9);
}

Our code now fails the second test, so let’s ‘fix’ the code in the simplest way possible.

public int Square(int n)
{
    if (n == 2)
        return 4;
    else
        return 9;
}

Run the tests again, and we have a nice green lawn.  But incorrect code.  We can go on adding more and more specific test cases, modify our method under test to pass those tests by using if statements or switch statements, and still have incorrect code.

How about adding a randomization aspect to the test, such as the following?

[Test]
public void TestThatSquareOfRandomNumberIsCorrect()
{
    int n = Random(1000);
    int result = n * n;
    Assert.IsTrue(Square(n) == result);
}

We now have a test that will (probably) force us to write a method which calculates a result rather than selecting from a limited range of options, but there are two problems: first, the test is overly complicated, and second, we have already written the required method inside the test!

So here’s the takeaway: Test Driven Development is a useful methodology which encourages the developer to develop only what is needed, in a testable and maintainable way.  However, it is important to realise that TDD does NOT mean that the produced code is correct in all cases - it is only correct in those cases for which specific tests exist.  As far as TDD is concerned, the code can fail in every other imaginable case.  So keep using your brain as you develop.  Allow TDD to do those things at which it excels, but don't expect it to have the Midas Touch - or you may very well fail.

A friend of mine who is learning WPF recently asked me how to load forms dynamically into the panes of a tab control. Being the helpful sort I am, I wrote up a quick app that demonstrates this.

Essentially, the idea is to create the forms as UserControls, and then load these at runtime into the host control (either a TabItem or some other Content or Item control such as a Grid). Remember that for a Content Control, you would set mycontrol.Content to the new form, while an Items control can contain multiple children, so to replace the content with a form you would call mycontrol.Children.Clear(), followed by mycontrol.Children.Add(newform).

The demo app is available here.

Japan - The Last Day

September 12, 2009

I managed to find some free internet access in the departure lounge, so decided to make use of it.  I left Panasonic today at 3pm local time, and caught the train back to the hotel to pick up my baggage.  My timing was impeccable, as the airport bus was only a few short minutes from leaving as I got to the stop.  The 70-odd minute ride to the airport was quiet, and gave me a chance to try to find a word to sum up my impressions of Japan.  I think I found it - “drab”.

Now, this may be peculiar to this part of Japan (Osaka - Kyoto), but everything feels just a little gray, perhaps grimy, and yet remarkably sterile.  There is very little litter, and I never saw any graffiti at all.  Perhaps the youth of Japan are instilled with a little more respect for others and for authority here, and certainly crime is lower in Japan than back at home.  For all that, it still feels a little lifeless.

Approaching Kansai Airport near Osaka, I realised something I hadn't picked up when I arrived - Kansai Aiport is on a man-made island (check it out on Google Maps), and is reached by a bridge about 3km long!

I arrived at the airport about 90 minutes before check in opened, and so sat down for a while before taking a walk to find some sustenance.   Much to my delight, I found ... a Starbucks!!  Real coffee!!  I was so thrilled at the prospect that it was some minutes before I realised that I had lost my mobile phone.  It must have slipped out of my pocket while I was sitting down, two floors above.  Retracing my steps brought no joy - it was gone.  So, off to the info counter.  Had a phone been handed in?  “Oh, please wait while I get paperwork”.  It wasn't until about a minute later that she pulls out my phone and says, “Is this yours?”  That phone may not be much more than a hill of beans, but it was my hill, and those were my beans!!

Anyway, I am looking forward to getting back to Oz and seeing my (in some cases sick) kids again.  It’s been a good trip, and one that has given me some good memories, but for now, I’m glad it’s over.