08 November 2011

Wellywood Controversy

For those who haven't been in touch with local Wellington news, part of the 'talk of the town' is the controversial 'Wellywood' sign, inspired by the well-known 'Hollywood' sign in L.A. (just switch 'Ho' with 'We' and bob's-your-uncle). Wellington International Airport Limited (WIAL) owns the land on the Miramar cutting, and decided it would be nice to put such a sign there.

Several months later, they have resource consent and have twice announced they were putting up 'that' sign - both times to severe backlash from the general public. Fortunately the powers-that-be have taken this opposition on board and created a competition to come up with an alternative design, that more of us could be proud of.

Naturally, I decided to take them up on the opportunity ;-)

Firstly, the issues with the original proposal that I wanted to address were:

  • A sense of 'tackiness' - taking a colloquial term coined through the media, to describe the local film industry catalysed through the work of Peter Jackson, Weta Workshop & Weta Digital, and giving that some sort of official status. While I have great respect for what these people have achieved, there is more to Wellington than the film industry to be proud of.
  • Lack of originality - the font is exactly the same as the well-known Hollywood sign. 7/9 of the sign reads exactly the same.
  • A sense of 'me-too' desperation - Hollywood is an actual name of a suburb, they found their identity. Wellington has its own identity that we should nurture and develop, and it won't come from aping others and shouting for attention. That seems too 'try-hard', and gives me an impression that Wellington isn't confident in or proud of itself to stand on its own feet.
There were several main criteria for the submitted concepts, including: fit within a 27 x 3.5m area; not have protrusions more than 0.5m; no lighting or illumination; and realistically be constructed within an $80,000 budget. Concepts don't necessarily have to be a sign, but could also be an image or artwork.

With that in mind, here's what I came up with:

No 'Wellywood' - Wellington does just fine. Original, angular font, which complements the 'rock-face' texture. This texture also complements and draws inspiration from the interior of the 'rocks' international airport terminal, which has recently won the Transport category at the Inside Festival.

For those who don't see the link with the film industry, bear in mind that I created this using a 3D modeller called Blender - which is similar to software that modellers use at Weta Digital. The black lines evoke a sense of a triangulated 3D mesh. I don't believe we need imagery of Gollum, King Kong, etc. to highlight this; also considering that such imagery would likely be under strict copyright from the respective studios.

Here's another variation, with a slightly more futuristic font:
In the end, over 350 submissions were received. Five were put forward as the finalists for public vote. Yes, I can live with the fact that neither of mine were selected, but those finalists had better be good. Sadly, I was a little underwhelmed, and I wasn't alone. Someone commented, "Are our designers that inept?" Another commented, "I can't believe these are the five best from more than 300 entries! Let's see all the options listed."

Well I hope I've proved the first commenter wrong, and whetted the appetite of the second.

09 March 2010

Regular Expressions: From Zero to Regex Hero

Last week I presented this session on Regular Expressions at the Wellington .NET User Group. It was nice to get such a great reception from the audience, and I hope they were able to follow along with the content.

Part of what made this a success was the integration of the technical demos in the presentation. From previous experience presenting at the Silverlight User Group, it can feel awkward having notes in the slides to jump to the demo, and conversely having to remember when to jump back into the slides without taking the demo too far. The task-switching also introduces the potential to inadvertently end the slide show and require paging through many slides to return to the proper point in the presentation.

In the end, I drew inspiration from Bea Stollnitz's WPF presentations, where her slides are developed using WPF instead of PowerPoint. At it's heart, the application uses the Page-based navigation feature of WPF to display the slides.

What I have done is integrate live controls that are able to test/demonstrate regular expressions into the particular slides, making it unnecessary to switch to the IDE. And if you do, the application can be restarted from the slide that was previously shown. A key difference from Bea's implementation is that I use the Model-View-ViewModel design pattern for a greater degree of separation between UI and content. This also allows more slides to be defined than there are distinct WPF Pages; binding to different viewmodels allows each template page to display different content. The real content of the slides (except 'fixed content' slides) is then defined in a single XML file.

I have placed the source for this application, dubbed RegExpress, on CodePlex at http://regexpress.codeplex.com/. This requires Visual C# 2008 or later to build and run. A good spin-off is that aside from providing guidance through the features of regular expressions, RegExpress can also be used to prototype or test regular expressions for particular software tasks, e.g. input string validation, data extraction, and search & replace.

Finally, for more detailed info on the inner workings of regular expressions, I highly recommend visiting http://www.regular-expressions.info/. This is essentially the 'Bible' of Regular Expressions, and is great for walking through how regular expressions work behind-the-scenes. It is worth knowing about these details, as the understanding makes it easier to compose expressions on-the-fly to solve custom problems, or to determine why a particular expression might not be working properly.

After all, there is often a case where regular expressions will help you save the day.

04 February 2010

Visual Art Meets Maths / Computer Science

Recently our Auckland office had a new art installation on the wall of one of its meeting rooms. Being a technology company, it was quite apt to make it an array of dots that predictably spelt 'Intergen' in binary (albeit all-caps). Here's what it looks like below:

What I noticed, being uppercase, was that the first three bits were always the same - which is in stark contrast to the pattern formed by the remaining bits. Also noticing that the art was eight bytes (forming a square grid) gave me an idea: Hilbert curves.

Rather than serialising the bits across each row from top to bottom, these bits could be serialised along the path of a Hilbert curve. So how do we construct this Hilbert curve?

A Hilbert curve is a fractal in similar vein to a Koch curve, but constructed in a way that 'fills space' instead of substituting a segment with a copy of the whole structure. What this means is that we define basic components, so for the Hilbert curve we'll have a Clockwise Spin and an Anticlockwise Spin component:

We also have logical rules for how each component is recursively composed of sub-components, so we have two different 1st-order Hilbert curves (ignoring effects of rotation):
As you can see, a component would occupy one cell, i.e. one dot in the artwork representing one bit. Going to the next level (2nd-order, etc.) means that this cell is divided into four. Following the path according to the direction rotation means that the sub-components are defined in the order 1-0-0-1 (where 1 indicates the sub-component is different to the parent component type). Once a sub-component has been 'rendered', the next component to render is determined by the type of parent component.

For an 8x8 grid, we only need to do this to the 3rd-order, yielding the curve below. This starts with an Anticlockwise spin as the 1st-order curve, starting in the top-left corner. The three most-significant bits for each character form a different pattern, though it isn't so bold as the straight grid before.
So all that's left is to follow the path and fill in the remaining bits, to achieve the final result:

03 February 2010

Now Certified in WPF (plus a binding tip)

As the title says, I'm now certified with an MCTS in Windows Presentation Foundation applications, having passed the 70-502 exam. (I've heard it's a tough one.) There were 50 multiple-choice questions to answer in something like 2:30 hours. As would be expected, the toughest questions were the ones I didn't really have experience in, like aspects of ClickOnce deployment, though these were balanced out by the XAML-related questions.

So anyway, after my moment of euphoria I was working on performing a custom data migration into Dynamics CRM 4, since the Bulk Data Import was inadequate for our mapping needs. I had previously bult a small WPF utility to start a bulk delete of records (for remigrating data), so was in the process of extending it to perform this custom migration.

In this mode, the user selects a CSV file from the file system prior to performing the migration. This is done by clicking a 'browse' button, which displays an OpenFileDialog for the user to select the file. Once the file is 'opened', the adjacent TextBox is updated with the selected path and file name. What makes this interesting is that the application uses the MVVM (Model-View-ViewModel) pattern.

Since it uses the MVVM pattern, the TextBox is bound to the ViewModel in a two-way mode. This means that when the TextBox's Text property is being set programmatically, it uses the code:

csvPath.SetValue(TextBox.TextProperty, selectedFilePath);
rather than:
csvPath.Text = selectedFilePath;
At first, this seemed to work - the TextBox was being updated to show the new value. However when the migration was being performed, stepping through the code revealed that the ViewModel contained the old (default) value.

The solution?

All bindings have an UpdateSourceTrigger property, which determines when the binding source gets updated. Nearly all controls have a default value of PropertyChanged, so that the source is updated as soon as the property value changes. TextBoxes tend to buck the trend and by default, update the source when the TextBox loses focus. Since the property is being set in code, the TextBox never gets focus in order to lose it so that the ViewModel gets updated.

Therefore, the TextBox and its binding should be defined in XAML as follows:
<TextBox x:Name="csvPath"
    Text="{Binding Path=CsvPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

23 January 2010

A New Website - Featuring EPiServer Composer

This week had quite a noteworthy event - the Go-Live of Intergen's new public website, which I was significantly involved with in terms of development. What made this particularly unique was that it was our first project using EPiServer Composer - a fairly new extension to the core content management system (CMS). This allows reusable components to be included in pages at the whim of content editors, which allows fewer page templates to be defined. So how does this compare with previous sites build using just the standard EPiServer CMS?

In the past, when developing such websites, our development team would determine the page templates that had to be built in order to deliver the functionality of each page in the site, as well as support different visuals for different sections of the site. This means that pages with similar layout (but different content) could use the same template (referred to as Page Types in EPiServer terminology). If the client required one of these pages to have an additional column or section, this would usually require a separate template to be built. As a result, it would be possible that many templates would be required, some with close similarities to each other.

With the reusable components created for a site using EPiServer Composer, the page templates can be simplified and made more generic. As an example, many sites have a media gallery feature. Traditionally, the gallery would be one template, each image item would be another. If the gallery also supported videos, each video item would need to use a third template.

In Composer, the gallery itself could be a component - which allows it to be placed inside a content block on any compatible page, whether a standard content page or section summary/landing page. This is exactly what has happened with the Intergen site, where an image gallery can be found on a standard content page.

Obviously, there are a few new things to learn when developing with EPiServer Composer. A reasonably good crash-course can be found here (if you're already used to the main aspects of working with standard EPiServer CMS). Here are a few things I also found out through experience on this project:

  • Composer elements (content functions and layout functions) are technically defined as EPiServer pages themselves, which are stored in a page hierarchy below a page called something like '[Extension page container]'. All Composer elements in the entire site are stored under that single hierarchy.
  • This single page tree is separate from the content pages that use the Composer elements. For global elements, this makes sense; for page-specific elements, they would be better suited to below the page itself. This is something to watch out for when migrating pages via export and import - the Composer structure needs to be migrated with the content.
  • Because Composer elements could be potentially used on any page (despite setting composition rules), it is a good idea to keep the CSS classes defined according to the specific component, rather than rely on classes associated with page type. If components are styled differently for different sections of the site, such styles can override the default, but a style should always be defined so that it doesn't appear broken on particular pages.
  • For different components that are effectively complex web controls, ensure the CSS class names do not collide, e.g. having general ".item" classes without a parent to distinguish between control, e.g. ".tabbedView .mapView .item".
  • Since multiple components can potentially be used on the same page, avoid using (and relying on) HTML element IDs. There should be no CSS style with a '#' (unless it is specifically part of the page's chrome and can only appear once on any page).
  • Because of the use of CSS classes for dynamic components, the jQuery JavaScript library becomes very useful - especially from its ability to select elements in the same manner as CSS. No need to rely on HTML element IDs.
  • If multiple dynamic components of the same type exist on a page, writing custom jQuery plugins is very useful in separating each component's behaviour so that it doesn't affect the other components.
Meanwhile, feel free to explore the Intergen Blog for other perspectives on this project. Enjoy the new site!

08 August 2009

Multilingual WPF Applications - on CodeProject

Well it seems that multilingual WPF applications is a hot topic, at least according to my most visited posts and a few questions posted on CodeProject. That has kick-started me into writing an article based on my implementation described here (in what became parts 1, 2 and 3).

The article may be viewed at:
http://www.codeproject.com/KB/WPF/Multilingual-WPF-Apps.aspx


Being my first CodeProject article, it was a bit of a learning experience. Fortunately there's some good information on getting started with writing & submitting articles and perspectives on how to make it a good article.

The good thing that an article template [ZIP] is provided with HTML file for writing the article, which is a good way to work on it offline and preview how it will look when it's published.

When it comes to submission and publishing, I chose to use the submission wizard rather than email it by ZIP file. While this has its convenient advantages, there are a few points to note:

  • You can't simply upload the ZIP file containing the article (in HTML), code sample (in nested ZIP) and images in one step. Instead, you need to upload each image and ZIP file (containing code) separately on one page, then input the article on the next page of the wizard.
  • The article itself is input using the WYSIWYG editor that supports rich formatting. Fortunately, this contains an HTML mode, so you can simply copy the article body from your HTML file and paste in the HTML view. You don't need to copy the <pre> tag containing the information you fill out online earlier in the submission wizard.
  • If you have images, you'll need to change the URL to be relative to your article (as the instructions indicate on the wizard page). This is largely dependent on what you choose as your base name as displayed in the browser's address bar.
  • Code fragments in <pre> tags need to have the 'lang' attribute assigned if using any language apart from C++. You won't see the difference when editing the article offline, but when it comes to previewing it online, this will make the difference for what syntax colouring you get. Doing this earlier saves time when you go through the submission process.
Other things I learned in the process:
  • When quoting internationalised text, watch the encoding of the HTML file. I changed this to UTF-8 so that foreign scripts like Japanese ひらがな (Hiragana) didn't show up as blocks or question marks.
  • Take care of how the HTML comments are handled. In the text editor (Notepad++), everything was fine. When it came to viewing it in Firefox, most of the article ended up being commented out. Fortunately I discovered a hack - simply add "<!--><-->" (without quotes) before your article content. I've no idea what was causing this!
  • While it takes a bit of time writing the article, don't underestimate how long it takes to provide a code sample, even if it can be extracted from a different project. It takes a long time to tidy things up so that redundant parts are removed, and to ensure that the sample is of good quality (as much as the article). There is a tricky balance - to ensure the code is easy to follow and credible, while getting it done as quickly as possible.
And lastly, it's a good thing to have fun in the process - it probably shows, given that I styled the sample.

19 July 2009

Building Multilingual WPF applications - Part 3

Since Part 2 on building Multilingual WPF applications, one unresolved issue has grabbed my attention as the proverbial stone in one's shoe - that of not getting the custom markup extension working correctly. As a refresher, I had implemented a strategy to change the interface language at run-time without needing to compile satellite assemblies (and have multiple versions of the same application, one for each language). I also noticed how the bindings from the UI to (what is now) the ViewModel were quite long and ugly when reproducing the same lines of XAML for each element that needed localised text, and sought to replace these expressions with something simpler.

So at the end of Part 2, I had managed to simplify ridiculously long binding expressions such as this:

<TextBlock Text="{Binding Path=currentLanguage,
Converter={StaticResource uiText}, ConverterParameter=ApplyAllLabel,
Source={x:Static vm:CommonViewModel.current}}" />
to this, via attached properties:
<TextBlock vm:Extensions.TextKey="ApplyAllLabel" />
However, there was a major flaw - it created a dependency on there being a Text property on the control, which basically had to be a TextBlock. For custom label controls, this logic (and the attached dependency properties) had to be replicated. An ugly hack that shouldn't need to be done.

So I had another crack at implementing this via a custom markup extension, remembering that a Binding is a MarkupExtension, like the custom extension I was creating. That means that the ProvideValue method simply has to invoke the ProvideValue method on the underlying Binding, and return the result. Therefore, my custom markup extension needed the following:
  • A private Binding instance, which is initialised in the constructor,
  • A public property, which gets and sets the CommandParameter of the Binding (i.e. the text key)
  • The ProvideValue implementation, as mentioned above:
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _lookupBinding.ProvideValue(serviceProvider);
}
To use it in the UI, the markup extension is used in the following way:
<TextBlock Text="{ext:LocalisedText Key=ApplyAllLabel}" />
Even better, the same markup extension can be used for custom controls, such as my labelled form controls:
<uiCt:TextLabel labelText="{ext:LocalisedText Key=DescriptionLabel}" />
The good news is the output is the same:

Localising Right-to-Left Languages

One other thing that was left unresolved from Part 1 was the notion of localising right-to-left languages. The problem is best described by the following screenshot:
For readers of right-to-left languages such as Hebrew and Arabic, the UI would be somewhat confusing when its layout doesn't flow logically in a manner consistent with reading order.

So I embarked on an adventure to perform this extra localisation, armed with the buzz from getting the custom markup extension working. This saw me creating converters for Thicknesses (for Margin and Padding), CornerRadiuses (for Borders), Dock property enumerations, and HorizontalAlignment. Piece by piece, I was getting the interface flipped in the right order. I was just creating the conversions for the Grid's ColumnDefinitions property and Column attached property, when I discovered something that made all this unnecessary.

The FrameworkElement.FlowDirection (attached) property.

The beauty of this is that I only need one binding - at the root level control inside the main window - since this value is inherited by every FrameworkElement below it in the visual hierarchy. This binding only needs to look at the 'isRightToLeft' property of the UILanguageDefinition instance and convert that (via a ValueConverter) to a FlowDirection enumeration, and a custom markup extension is created to simplify the 'binding' expression in XAML.

Naturally, this attached property is quite powerful. Here are some points / gotchas to consider:
  • Custom panels are automatically laid out in reverse, so you do not need to create an 'isReversed' property (or similar) and adjust your ArrangeOverride calculations accordingly.
  • Bitmap images and shapes (e.g. Paths) are reversed. If you want to preserve the rendering of these, independent of flow direction (e.g. for corporate logos / branding), then you need to override the FlowDirection by setting it to LeftToRight.
  • If the interface has a FlowDirection of RightToLeft and an element (e.g. Image) has a FlowDirection of LeftToRight, then the Margin on the element will act in a RightToLeft manner. Since a Padding acts on the internal visual hierarchy of the element, a padding will behave in a LeftToRight manner.
  • TextBoxes containing language-invariant data should have the FlowDirection set to LeftToRight. Ideally, this should be set in a Style to minimise repetition and guarantee consistency.
So in practice, the main window's immediate child element, a DockPanel, is declared as:
<DockPanel FlowDirection="{ext:LocalisedFlowDirection}">
where the LocalisedFlowDirectionExtension is defined as follows:
public class LocalisedFlowDirectionExtension : MarkupExtension
{
private Binding _flowDirectionBinding;

public LocalisedFlowDirectionExtension()
{
_flowDirectionBinding = FlowDirectionConverter.createBinding();
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
return _flowDirectionBinding.ProvideValue(serviceProvider);
}
}
The static createBinding method creates a binding that uses a shared converter with this logic at the core of its Convert method:
bool isRightToLeft = (bool)value;
return (isRightToLeft)? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
With that done, the UI is rendered as expected:
Onwards and upwards.

14 July 2009

Backstage Behind the Scenes

Today marks the launch of Office 2010's technical preview, accompanied by a new site showcasing the work that Microsoft has been putting in to deliver Office 2010 - http://www.office2010themovie.com/. As one of the developers working on the site, I am able to provide a brief behind-the-scenes of the Behind-the-Scenes, so to speak. Since I mostly worked on the Silverlight-based front-end, this will mainly focus on the Silverlight aspect of the site - although a pure HTML version exists for visitors using mobile phones.

As you can probably guess, the entire page (i.e. not just the video player) is pure Silverlight 2.0. This has a number of benefits over using a mixture of HTML and Silverlight objects:

  • Navigation between various 'pages' (I prefer to refer to these as 'Views') does not affect the inter-page navigation as is stored in the browser's history. This makes it very useful for online applications where entities are added, viewed, edited and removed without the 'back' button history growing ever larger. No need to do asynchronous JavaScript either.
  • The layout possibilities are more flexible without resorting to table-based layouts in HTML. Even if layout is div-based and using CSS, there are a number of common technical limitations that make some aspects of design impractical for web pages, for example equal column heights with variable content in each column.
  • Every control can be styled in a much more flexible manner by overriding control templates. That's how we were able to add shadow effects and gradients to the text boxes, for example.
While Silverlight 2 has its limitations in comparison with WPF (as previously mentioned), it still allows the creation of custom panels so that the desired layout mechanism can be achieved. In the absence of a suitable WrapPanel for preserving right-justification of the 'Play Video' button in the video catalogue list, I was able to create one from scratch. (See the below WPF screenshot for why the standard WrapPanel is inadequate.)

There are some cases where particular layouts may be possible in HTML+CSS with floating divs, etc., however there are many cases where ugly hacks and/or verbosity would be needed to come close to the intended design. (Stay tuned for another custom panel coming to the home view on July 20...)

The project itself took our team (4 developers, 1 designer, a project manager, deployment engineer and client liaison representative/tester) a little over two weeks sprinting from start to finish, with the application being functionally-complete (or as close as possible to it) at the end of the first week. That left this past week for styling the interface from generic black rectangles to the polished interface you see on the live site - including stabilisation and design convergence (making the actual interface match the design mockups down to the nearest pixel, where possible).

Naturally, there were challenges along the way. Challenges such as getting the player working correctly with streaming video, supporting mouse-wheel scrolling on as many browsers as possible, implementing the Model-View-ViewModel (MVVM) pattern without native Silverlight commands (Thanks, Chris, for introducing me to your implementation). Yes, there was the figurative blood, sweat and tears as we battled seemingly endless design tweaks, compile times and bugs. But it was worth it, and I, for one, enjoyed it.

Now, time to celebrate...

10 July 2009

Another Step for Silverlight...

Hooray - Silverlight 3.0 has just been released today, bringing it another step forward (and a little bit closer to WPF - at least in .NET 3.5). I have had the fortune of working on a few Silverlight-based projects at work, mostly using Silverlight 2. While that has come a long way from Silverlight 1.0 (and 1.1 alpha), it had its quirks that had me yearning for WPF.

Quirks like no DockPanel, WrapPanel or ViewBox. No triggers either - a VisualStateManager instead. No element-to-element data binding. A RenderTransform on UIElements, but no LayoutTransform as well (see the difference in the WPF screenshot below).


On my current project, I have been involved with skinning the application to match the visual design produced by one of our internal designers. This included restyling the scrollbars in the ScrollViewer for a cleaner look, which made me somewhat uneasy as I had done this as part of implementing ButtercupReader with mixed success. The good news was that the scrollbars matched the design and worked, though there was a small issue that had me stumped, with no hint at the time on the wider Internet. This issue reveals itself when scrolling to the very bottom (or right), and results in clipping off the end of the scrollbar as shown:


Sure enough, this issue came up again on my current project - though a second pair of eyes had a crack at getting past the issue, and luckily was able to solve it.

So what was the problem?

The track was implemented as a Border spanning the appropriate rows (or columns) of the Grid that is at the heart of the scrollbar. For some reason, this caused some sort of internal mayhem that resulted in the clipping of the end of the scrollbars.

The solution? Don't use a Border - use a Rectangle instead.

Will this also be the case in Silverlight 3? Probably, given that this behaves the same with the latest Silverlight 3 runtime installed. However, things may be different if the application is built natively with Silverlight 3. Something to try out...

05 July 2009

Multilingual WPF Applications: Part 2

In a previous post, I set up a way to bind a WPF interface to a class containing localised strings, so that the language of the entire application can be dynamically switched at runtime. This is unlike the standard .NET way of creating satellite assemblies, which gives you a separate version of your application for each different language / locale supported.

So far, this has been working nicely as I've been building up my WPF application, but one thing is becoming clear - it is tedious work repeating the same binding for every new TextBlock containing UI text (e.g. for labels and messages):


<TextBlock Text="{Binding Path=currentLanguage,
Converter={StaticResource uiText}, ConverterParameter=ApplyAllLabel,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type uiCt:MainWindow}}}" />
There are two problems I see with this:
  • It's long. Too long - especially how often these bindings are used throughout the entire application.
  • The RelativeSource property setting is inefficient and fragile - traversing the hierarchy of visuals to find the parent window to retrieve the 'currentLanguage' property.
Firstly, avoiding the FindAncestor traversal. XAML has a nifty markup extension to reference a shared object, aptly named x:Static. This is perfect for the currentLanguage property as it is shared throughout the entire user interface. Using this also means that MainWindow is not the appropriate place to store such shared data.

Hence, the introduction of a ViewModel - in simple terms a 'Model' specific to the View.

The ViewModel I constructed for storing the language data that every part of the UI binds to uses the Singleton design pattern and implements the INotifyPropertyChanged interface. This part is crucial for enabling the UI to automatically update the bindings when the source is changed. Given that the shared instance of this ViewModel is called 'current', the binding becomes:
<TextBlock Text="{Binding Path=currentLanguage,
Converter={StaticResource uiText}, ConverterParameter=ApplyAllLabel,
Source={x:Static vm:CommonViewModel.current}}" />
where the 'vm' XML namespace is mapped to the namespace of the ViewModel in the solution.

Once the insides have been wired up to update the ViewModel, the application reassuringly runs the same as before, at least from a visual point of view.

However, the binding expression is still too long. I ended up making two attempts at making the XAML more succinct. As a learning exercise, the first attempt involved creating a custom markup extension (with inspiration from Philipp Sumi). To cut a long story short, attempting to return Binding objects to the Text property caused an invalid cast exception, since the Text property expects only a string. I learned that the Binding markup extension does not return a Binding object, but an object of the type expected by the DependencyProperty. (Philipp also posted an example of creating a custom Binding markup extension, alas I wasn't quite able to adapt that to my needs with success.)

In the end, what did work was using a custom attached property. This went into a static class called Extensions, and provided a callback handler for the PropertyChanged event. This handler method creates a Binding in code and assigns it to the Text property of the (cast) TextBlock.

So the DependencyProperty is defined as:
public static readonly DependencyProperty TextKeyProperty =
DependencyProperty.RegisterAttached("TextKey",
typeof(string), typeof(Extensions),
new FrameworkPropertyMetadata("",
FrameworkPropertyMetadataOptions.AffectsMeasure,
onTextKeyChanged));
and the callback effectively becomes:
private static void onTextKeyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
TextBlock castControl = obj as TextBlock;
string key = e.NewValue as string;
Binding lookupBinding = UITextLookupConverter.createBinding(key);
castControl.SetBinding(TextBlock.TextProperty, lookupBinding);
}
This is using the same value converter as before, except the static instance is defined in the UITextLookupConverter instead of XAML. The 'createBinding' method creates the Binding like so:
Binding languageBinding = new Binding("languageDefn")
{
Source = CommonViewState.current,
Converter = _sharedConverter,
ConverterParameter = key,
};
Finally, with the appropriate XML namespaces mapped, the XAML becomes:
<TextBlock vm:Extensions.TextKey="ApplyAllLabel" />
Nice.

However, there is one drawback where creating a custom Binding markup extension would be a better strategy. This method is only useful for the Text property of a TextBlock. For other controls (e.g. custom controls) that display text in different properties, additional extended attributes would be needed for each unique DependencyProperty (even if similar custom controls can inherit from a common abstract base class, or implement a common interface to retrieve the appropriate DependencyProperty). A single custom (binding) markup extension would be able to be used for any property of any control that expects a string.

For now, this is progress.