The blog of dlaa.me

Posts tagged "Windows Phone"

sudo localize --crossplatform [Free PseudoLocalizer class makes it easy to identify localization issues in WPF, Silverlight, and Windows Phone 7 applications!]

Two posts ago, I explained the benefits of pseudo-localization and showed an easy way to implement it for WPF - then said I'd outline how to do the same for Silverlight and Windows Phone 7. In my previous post, I went off on the seeming diversion of implementing a PNG encoder for Silverlight. With this post, I'll fulfill my original promise and unify the previous two posts! As you'll see, the basic principles of my approach to WPF localization translate fairly directly to Silverlight - though some limitations in the latter platform make achieving the same result more difficult. Even though more code and manual intervention are required for Silverlight and Windows Phone 7, the benefits are the same and pseudo-localization remains a great way to identify potential problems early in the development cycle.

For completeness I'll show examples and techniques for all platforms below...

 

Normal WPF application PseudoLocalizer in a WPF application

Please see the original post for an explanation of the changes shown above.

 

Adding pseudo-localization of RESX resources to a WPF application

  1. Add the PseudoLocalizer.cs file from the sample download to the project.

  2. Add PSEUDOLOCALIZER_ENABLED to the (semi-colon-delimited) list of conditional compilation symbols for the project (via the Project menu, Properties item, Build tab in Visual Studio).

  3. Add the following code somewhere it will be run soon after the application starts (for example, add a constructor for the App class in App.xaml.cs):

    #if PSEUDOLOCALIZER_ENABLED
        Delay.PseudoLocalizer.Enable(typeof(ProjectName.Properties.Resources));
    #endif
  4. If necessary: Add a project reference to System.Drawing (via Project menu, Add Reference, .NET tab) if building the project now results in the error "The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?)".

  5. If necessary: Right-click Resources.resx and choose Run Custom Tool if running the application under the debugger (F5) throws the exception "No matching constructor found on type 'ProjectName.Properties.Resources'. You can use the Arguments or FactoryMethod directives to construct this type.".

 

Adding pseudo-localization of RESX resources to a Silverlight or Windows Phone 7 application

  1. Add the PseudoLocalizer.cs and PngEncoder.cs files from the sample download to the project.

  2. Add PSEUDOLOCALIZER_ENABLED to the (semi-colon-delimited) list of conditional compilation symbols for the project (via the Project menu, Properties item, Build tab in Visual Studio).

  3. Make the following update to the auto-generated resource wrapper class by editing Resources.Designer.cs directly (the highlighted portion is the primary change):

    #if PSEUDOLOCALIZER_ENABLED
        global::System.Resources.ResourceManager temp =
            new Delay.PseudoLocalizerResourceManager("PseudoLocalizerSL.Resources", typeof(Resources).Assembly);
    #else
        global::System.Resources.ResourceManager temp =
            new global::System.Resources.ResourceManager("PseudoLocalizerSL.Resources", typeof(Resources).Assembly);
    #endif

    In case it's not clear, this change simply duplicates the existing line of code that creates an instance of ResourceManager, modifies it to create an instance of Delay.PseudoLocalizerResourceManager instead, and wraps the two versions in an appropriate #if/#else/#endif so pseudo-localization can be completely controlled by whether or not PSEUDOLOCALIZER_ENABLED is #defined.

    Important: This change will be silently overwritten the next time (and every time!) you make a change to Resources.resx with the Visual Studio designer. Please see my notes below for more information on this Silverlight-/Windows Phone-specific gotcha.

 

PseudoLocalizer in a Silverlight application

 

Adding a pseudo-localizable string (all platforms)

  1. Double-click Resources.resx to open the resource editor.

  2. Add the string by name and value.

  3. Reference it from code/XAML.

  4. Silverlight/Windows Phone 7: Re-apply the Delay.PseudoLocalizerResourceManager change to Resources.Designer.cs which was silently undone when the new resource was added.

 

Adding a pseudo-localizable image (WPF only)

  1. Double-click Resources.resx to open the resource editor.

  2. Click the "expand" arrow for Add Resource and choose Add Existing File....

  3. Open the desired image file.

  4. Reference it from code/XAML (possibly via BitmapToImageSourceConverter.cs from the sample ZIP).

 

Adding a pseudo-localizable image (all platforms)

  1. Rename the image file from Picture.jpg to Picture.jpg-bin.

  2. Double-click Resources.resx to open the resource editor.

  3. Click the "expand" arrow for Add Resource and choose Add Existing File....

  4. Open the desired image file.

  5. Reference it from code/XAML (probably via ByteArrayToImageSourceConverter.cs from the sample ZIP).

  6. Silverlight/Windows Phone 7: Re-apply the Delay.PseudoLocalizerResourceManager change to Resources.Designer.cs which was silently undone when the new resource was added.

  7. Optionally: Restore the image's original file name in the Resources folder of the project and manually update its file name in Resources.resx using a text editor like Notepad. (I've done this for the sample project; it makes things a little clearer and it's easier to edit the image resource without having to rename it each time.)

 

PseudoLocalizer in a Windows Phone 7 application

 

There you have it - simple text and image pseudo-localization for WPF, Silverlight, and Windows Phone 7 applications is within your grasp! :) The basic concept is straightforward, though limitations make it a bit more challenging for Silverlight-based platforms. Nevertheless, the time you're likely to save by running PseudoLocalizer early (and often) should far outweigh any inconvenience along the way. By finding (and fixing) localization issues early, your application will be more friendly to customers - no matter what language they speak!

 

[Click here to download the complete source code for PseudoLocalizer, various helper classes, and the WPF, Silverlight, and Windows Phone 7 sample applications shown above.]

 

Notes

  • For a brief overview of using RESX resources in a WPF, Silverlight, or Windows Phone 7 application, please see the "Notes" section of my original PseudoLocalizer post. You'll want to be sure the basic stuff is all hooked up and working correctly before adding PseudoLocalizer into the mix.

  • The act of using RESX-style resources in a Silverlight application is more difficult than it is in a WPF application (independent of pseudo-localization). WPF allows you to directly reference the generated resources class directly from XAML:

    <Window.Resources>
        <properties:Resources x:Key="Resources" xmlns:properties="clr-namespace:PseudoLocalizerWPF.Properties"/>
    </Window.Resources>
    ...
    <TextBlock Text="{Binding Path=Message, Source={StaticResource Resources}}"/>

    However, that approach doesn't work on Silverlight (or Windows Phone 7) because the generated constructor is internal and Silverlight's XAML parser refuses to create instances of such classes. Therefore, most people create a wrapper class (as Tim Heuer explains here):

    /// <summary>
    /// Class that wraps the generated Resources class (for Resources.resx) in order to provide access from XAML on Silverlight.
    /// </summary>
    public class ResourcesWrapper
    {
        private Resources _resources = new Resources();
        public Resources Resources
        {
            get { return _resources; }
        }
    }

    And reference that instead:

    <UserControl.Resources>
        <local:ResourcesWrapper x:Key="Resources" xmlns:local="clr-namespace:PseudoLocalizerSL"/>
    </UserControl.Resources>
    ...
    <TextBlock Text="{Binding Path=Resources.Message, Source={StaticResource Resources}}"/>

    Obviously, the extra level of indirection adds overhead to every place resources are used in XAML - but that's a small price to pay for dodging the platform issue. :)

  • WPF supports private reflection and PseudoLocalizer takes advantage of that to enable a simple, seamless, "set it and forget it" hook-up (via the call to Enable above). Unfortunately, private reflection isn't allowed on Silverlight, so the same trick doesn't work there. I considered a variety of different ways around this, and ultimately settled on editing the generated wrapper class code because it applies exactly the same customization as on WPF. And while it's pretty annoying to have this tweak silently overwritten every time the RESX file is edited, it's simple enough to re-apply and it's easy to spot when reviewing changes before check-in.

  • I explained what's wrong with the default behavior of adding an image to a RESX file in my PngEncoder post:

    [...] the technique I used for [WPF] (reading the System.Drawing.Bitmap instance from the resources class and manipulating its pixels before handing it off to the application) won't work on Silverlight. You see, the System.Drawing namespace/assembly doesn't exist for Silverlight! So although the RESX designer in Visual Studio will happily let you add an image to a Silverlight RESX file, actually doing so results in an immediate compile error [...].

    Fortunately, the renaming trick I use above works well for Silverlight and Windows Phone - and WPF, too. So if you're looking to standardize on a single technique, this is the one. :)

    Even if you're devoted to WPF and don't care about Silverlight, you should still consider the byte[] approach: although System.Drawing.Bitmap is easier to deal with, it's not the right format. (Recall from the original PseudoLocalizer post that I wrote an IValueConverter to convert from it to System.Windows.Media.ImageSource.) Instead of loading images as System.Drawing.Bitmap and converting them with BitmapToImageSourceConverter, why not load them as byte[] and convert them with ByteArrayToImageSourceConverter.cs - and save a few CPU cycles by not bouncing through an unnecessary format?

  • In addition to the renaming technique for accessing RESX images from Silverlight, there's a similar approach (courtesy of Justin Van Patten) that renames to .wav instead and exposes the resource as a System.IO.Stream. For the purposes of pseudo-localization, the two renaming approaches should be basically equivalent - which led me to go as far as hooking everything up and writing StreamToImageSourceConverter.cs before I realized why the Stream approach isn't viable...

    What it comes down to is an unfortunate API definition - the thing that's exposed by the wrapper class isn't a Stream, it's an UnmanagedMemoryStream! And while that would be perfectly fine as an implementation detail, it's not: the type of the auto-generated property is UnmanagedMemoryStream and the type returned by ResourceManager.GetStream is also UnmanagedMemoryStream. But UnmanagedMemoryStream can't be created by user code in Silverlight (and requires unsafe code in WPF), so this breaks PseudoLocalizer's approach of decoding/pseudo-localizing/re-encoding the image because it means the altered bytes can't be wrapped back up in a UnmanagedMemoryStream to maintain the necessary pass-through behavior!

    If only the corresponding RESX interfaces had used the Stream type (a base class of UnmanagedMemoryStream), it would have been possible to wrap the altered image in a MemoryStream and return that - a technique supported by all three platforms. Without digging into this too much more, it seems to me that the Stream type could have been used with no loss of generality - though perhaps there's a subtlety I'm missing.

    Aside: As a general API design guideline, always seek to expose the most general type that makes sense for a particular scenario. That does not mean everything should expose the Object type and cast everywhere - but it does mean that (for example) APIs exposing a stream should use the Stream type and thus automatically work with MemoryStream, UnmanagedMemoryStream, NetworkStream, etc.. Only when an API needs something from a specific subclass should it use the more specific subclass.

    Be that as it may, I didn't see a nice way of wrapping images in a UnmanagedMemoryStream, and therefore recommend using the byte[] approach instead!

What it lacks in efficiency, it makes up for in efficiency! [Silverlight-ready PNG encoder implementation shows one way to use .NET IEnumerables effectively]

At the end of my previous post about easily pseudo-localizing WPF applications, I said this post would show how to apply those concepts to a Silverlight application. Unfortunately, I seem to have made an off-by-one error: while this post is related to that topic, it is not the post I advertised. But it seems interesting in its own right, so I hope you enjoy it. :)

Okay, so what does a PNG (Portable Network Graphics) image encoder have to do with pseudo-localizing on the Silverlight platform? Almost nothing - except for the fact that I went above and beyond with my last post and showed how to pseudo-localize not just text, but images as well. It turns out the technique I used for that (reading the System.Drawing.Image instance from the resources class and manipulating its pixels before handing it off to the application) won't work on Silverlight. You see, the System.Drawing namespace/assembly doesn't exist for Silverlight! So although the RESX designer in Visual Studio will happily let you add an image to a Silverlight RESX file, actually doing so results in an immediate compile error: The type or namespace name 'Drawing' does not exist in the namespace 'System' (are you missing an assembly reference?)...

 

But all is not lost - there are other ways of adding an image to a RESX file! True, the process is a little wonky and cumbersome, but at least it works. However, the resulting resource is exposed as either a byte[] or a Stream instance - both of which are basically just a sequence of bytes. And because there's no SetPixel method for byte arrays, this is a classic "Houston, we have a problem" [sic; deliberately misquoting] moment for my original approach of pseudo-localizing the image by manipulating its pixels... Hum, what's a girl to do?

Well, those bytes do correspond to an encoded image, so it ought to be possible to decode the image - at which point we could wrap it in a WriteableBitmap and do the pixel manipulation via its Pixels property. After that, we could re-encode the altered pixels back to an image file (remember that the resource data is expected to be the encoded bytes of an image) and things should work as seamlessly as they did for the original scenario. And they actually do! Well, on WPF, at least...

 

PngEncoder sample image

Sample PNG file encoded by PngEncoder

 

Not on Silverlight, though. Silverlight will get you all the way to the last step, but that's the end of the line: the platform doesn't expose a way to do the re-encoding. :( As you might guess, this is hardly the first time this issue has come up - a quick web search turns up plenty of examples of people wanting to encode images under Silverlight. The typical recommendation is to find (or write) your own image encoder, and so there are a number of examples of Silverlight-compatible image encoders to be found!

So why did I write my own??

Well, because none of the examples I found was quite what I wanted. The most obvious candidate just flat out didn't work; it crashed on every input I gave it. The runner-up (deliberately) took shortcuts for speed and didn't produce valid output. The third option was released under a license I'm not able to work with. And the fourth was part of a much larger image encoding library I didn't feel like pulling apart. Besides, I release full source code on my blog, and I don't want to be in the business of distributing other peoples' code with my samples. A lot of times it's just easier and safer to code something myself - and I've had a lot of great learning experiences as a result! :)

 

When choosing an image format for re-encoding on the fly, Silverlight makes the decision fairly easy because it supports only two image formats: JPEG and PNG. Because JPEG is a lossy format, it's pretty much a non-starter (we don't want to degrade image quality) and therefore lossless PNG is the obvious choice. Conveniently, the PNG image format is fairly simple - especially if you're willing to punt on (losslessly) compressing the image! All you need to encode a PNG file is a format prefix (8 bytes), a header chunk (5 bytes), an image chunk with the properly encoded pixels, and an end chunk (0 bytes). There's a decent amount of bookkeeping to be done along the way (scanline filtering, compression block creation, two different hash algorithms, etc.), but it's all fairly straightforward. And the PNG specification is well-written and fairly easy to follow - what more could you ask for?

Aside: Giving up on compression may seem like a big deal, but I don't think it is for the scenario at hand. While small file size is important for making the best use of long-term storage, the pseudo-localization scenario creates its PNG file on the fly, loads it, and immediately discards it. The encoded image simply isn't around for very long and so its large size shouldn't matter.

Okay, so PNG encoding isn't rocket science - but I still feel that if I'm going to reinvent the wheel, then at least I should try to contribute something new or interesting to the mix! :)

 

What I've done here is to use IEnumerable everywhere:

namespace Delay
{
    /// <summary>
    /// Class that encodes a sequence of pixels to a sequence of bytes representing a PNG file (uncompressed).
    /// </summary>
    /// <remarks>
    /// Reference: http://www.w3.org/TR/PNG/.
    /// </remarks>
    static class PngEncoder
    {
        /// <summary>
        /// Encodes the specified pixels to a sequence of bytes representing a PNG file.
        /// </summary>
        /// <param name="width">Width of the image.</param>
        /// <param name="height">Height of the image.</param>
        /// <param name="pixels">Pixels of the image in ARGB format.</param>
        /// <returns>Sequence of bytes representing a PNG file.</returns>
        public static IEnumerable<byte> Encode(int width, int height, IEnumerable<int> pixels) { /* ... */ }
    }
}

And IEnumerable isn't just for the public API, it's used throughout the code, too! This is the meaning behind the title of this post: giving up on compression is inefficient from a storage space perspective, but operating exclusively on enumerations is very efficient from a memory consumption and a computational efficiency perspective! Because of that, this class can encode a 20-megabyte PNG file without allocating any more memory than it takes to encode a 1-byte PNG file. What's more, no work is done before it needs to be: if you're streaming a PNG file across a slow transport, the file will be read and encoded only as quickly as the receiver consumes the bytes - and if encoding is aborted mid-way for some reason, no unnecessary effort has been wasted!

This efficiency is possible thanks to the way IEnumerable works and the convenience of C#'s yield return which makes it easy to write code that returns an IEnumerable without explicitly implementing IEnumerator. As a result, the code for PngEncoder is clear and linear with no hint (or visible complexity) of the allocation savings or deferred processing that occur under the covers. Instead, everything communicates in terms of byte sequences - translating one sequence to another inline as necessary. For example, every horizontal line of an encoded PNG image is prefixed with a byte indicating which filtering algorithm was used. These filter bytes aren't part of the original pixels that are passed into the Encode call, so they need to be added. What's cool is that it's easy to write a method to do so - and because it takes an IEnumerable input parameter and returns an IEnumerable result, such a method can be trivially "injected" into any data flow!

 

One particularly handy realization of this technique is demonstrated by a class I called WrappedEnumerable - here's what it looks like to the developer:

/// <summary>
/// Class that wraps an IEnumerable(T) or IEnumerator(T) in order to do something with each byte as it is enumerated.
/// </summary>
/// <typeparam name="T">Type of element being enumerated.</typeparam>
abstract class WrappedEnumerable<T> : IEnumerable<T>, IEnumerator<T>
{
    /// <summary>
    /// Method called to initialize for a new (or reset) enumeration.
    /// </summary>
    protected virtual void Initialize() { }

    /// <summary>
    /// Method called for each byte output by the underlying enumerator.
    /// </summary>
    /// <param name="value">Next value.</param>
    protected abstract void OnValueEnumerated(T value);
}

WrappedEnumerable is useful for PngEncoder because the encoding process makes use of two different hash algorithms: CRC-32 and Adler-32. Long-time readers of this blog know I'm no stranger to hash functions - in fact, I've previously shared code to implement CRC-32 based on the very same PNG specification! But as much as I love .NET's HashAlgorithm, using it here seemed like it might be overkill. HashAlgorithm is ideal for processing large chunks of data at a time, but the sequence-oriented nature of PngEncoder deals with a single byte at a time. So what I did was to create WrappedEnumerable subclasses Crc32WrappedEnumerable and Adler32WrappedEnumerable which override the two methods above to calculate their hash values as the bytes flow through the system! These are both simple classes and work quite well for "injecting" hash math into a data flow. The PNG format puts stores its hash values after the corresponding data, so by the time that value is needed, the relevant data has already flowed through the WrappedEnumerable and the final hash is ready to be retrieved.

 

However, the same cannot be said of the length fields in the PNG specification... Lengths are stored before the corresponding data, and that poses a distinct problem when you're trying to avoid looking ahead: without knowing what data is coming, it's hard to know how much there is! But I'm able to cheat here: because PngEncoder doesn't compress, it turns out that all the internal lengths can be calculated from the original width and height values passed to the call to Encode! So while this is a bit algorithmically impure, it completely sidesteps the length issue and avoids the need to buffer up arbitrarily long sequences of data just to know how long they are.

Aside: After spending so much time celebrating the IEnumerable way of life, this is probably a good time to highlight one of its subtle risks: multiple enumeration due to deferred execution. Multiple enumeration is something that comes up a lot in the context of LINQ - an IEnumerable<byte> is not the same thing as a byte[]. Whereas it's perfectly reasonable to pass a byte[] off to two different functions to deal with, doing the same thing with an IEnumerable<byte> usually results in that sequence being created and enumerated twice! Depending on where the sequence comes from, this can range from inefficient (a duplication of effort) to catastrophic (it may not be possible to generate the sequence a second time). The topic of multiple enumeration is rich enough to merit its own blog post (here's one and here's another and here's another), and I won't go into it further here. But be on the look-out, because it can be tricky!
Further aside: To help avoid this problem in PngEncoder, I created the TrackingEnumerable and TrackingEnumerator classes in the sample project. These are simple IEnumerable/IEnumerator implementations except that they output a string with Debug.WriteLine whenever a new enumeration is begun. For the purposes of PngEncoder, seeing any more than one of these outputs represents a bug!

 

In an attempt to ensure that my PngEncoder implementation behaves well and produces valid PNG files, I've written a small collection of automated tests (included with the download). Most of them are concerned with parameter validation and correct behavior with regard to the IEnumerable idiosyncrasies I mentioned earlier, but the one called "RandomImages" is all about output verification. That test creates 25 PNG files of random size and contents, encodes them with PngEncoder, then decodes them with two different decoder implementations (System.Drawing.Bitmap and System.Windows.Media.Imaging.PngBitmapDecoder) and verifies the output is identical to the input. I've also verified that PngEncoder's PNG files can be opened in a variety of different image viewing/editing applications. (Interesting tidbit: Internet Explorer seems to be the strictest about requiring valid PNG files!) While none of this is a guarantee that all images will encode successfully, I'm optimistic typical scenarios will work well for people. :)

 

[Click here to download the source code for the complete PngEncoder.cs implementation, a simple test application, and the automated test suite.]

 

At the end of the day, the big question is whether my focus (obsession?) with an IEnumerable-centric implementation was justified. From a practical point of view, you could argue this either way - I happen to think things worked out pretty elegantly, but others might argue the code would be clearer with explicit allocations. I'll claim the memory/computational benefits I describe here are pretty compelling (especially in resource-constrained scenarios like Windows Phone), but others could reasonably argue that naïve consumers of PngEncoder are likely to write their code in a way that negates those benefits (ex: by calling ToArray).

However, I'm not going to spend a lot of time second-guessing myself; I think it was a great experience. :) The following sums up my thoughts on the matter pretty nicely:

The road of life twists and turns and no two directions are ever the same. Yet our lessons come from the journey, not the destination. - Don Williams, Jr.

sudo localize & make me-a-sandwich [Free PseudoLocalizer class makes it easy for anyone to identify potential localization issues in .NET applications]

I've previously written about the benefits of localization and shown how to localize Windows Phone 7 applications. The techniques I describe in that post constitute a good workflow that's just as suitable for WPF and Silverlight desktop applications! But even with good processes in place, the way localization is done in the real world has some challenges...

You see, localization can be expensive: hiring people to translate an entire application, re-testing it in the newly supported locale, fixing resulting bugs, etc.. So teams often wait till near the end of the release cycle - after the UI has stabilized - to start localizing the application. This is a perfectly reasonable approach, but there are invariably a few surprises - usually some variation of "What do you mean that string is hard-coded in English and can't be translated??". It sure would be nice if teams could do some kind of low-cost localization in order to identify - and fix - oversights like this long before they turn into problems...

Yep - that process is known as pseudo-localization. What pseudo-localization does is create alternate versions of resource strings by using different characters that look similar enough to the original English characters that text is still readable - but obviously "localized". (This is one of those "a picture is worth a thousand words" moments, so please check out the Wikipedia article or bear with me for just a moment...) Additionally, because some languages tend to have longer phrases than English (German, I'm looking at you!), there's often an additional aspect of string lengthening to simulate that and help detect wrapping, clipping, and the like.

Aside: It's important to remember that the character "translations" are chosen exclusively on the basis of how similar they look to the original character and not on the basis of linguistic correctness, cultural influence, or anything like that!

 

Here's the sample application I created for this post running in its normal English mode. (It's not very sophisticated, but it's good enough for our purposes.) Can you tell if all the strings are properly localizable? Are there any other localization concerns here?

Normal application

 

Pseudo-localization isn't a new concept and there are a variety of tools out there that do a fine job of it. However, none of the ones I found during a quick search appeared to be free, simple to use, and unencumbered by restrictions (i.e., licensing, distribution, etc.). So I thought it would be fun to write my own and share it with the community as open source under the very permissive Ms-PL license. :)

The class I've created is called PseudoLocalizer and it automatically pseudo-localizes standard .NET RESX-based resources. Using RESX-based resources is the recommend approach for building localizable Silverlight and Windows Phone 7 applications, the recommended approach for building localizable Windows Forms applications, and also a perfectly fine approach for building localizable WPF applications.

Aside: The recommended technique for localizing WPF applications is actually something else, but I have some issues with that approach and won't be discussing it here.

 

I've said it's easy to use PseudoLocalizer: all it takes are three special steps:

  1. Add the (self-contained) PseudoLocalizer.cs file from the sample download to your project.

  2. Add the following code somewhere it will be run once and run early (ex: the application's constructor):

    #if PSEUDOLOCALIZER_ENABLED
        Delay.PseudoLocalizer.Enable(typeof(ProjectName.Properties.Resources));
    #endif
  3. Add PSEUDOLOCALIZER_ENABLED to the list of conditional compilation symbols for your project (via the Project menu, Properties item, Build tab in Visual Studio).

Done! Not only will all localizable strings be automatically pseudo-localized, but images will, too! How does one pseudo-localize an image?? Well, I considered a variety of techniques, but settled on simply inverting the colors to create a negative image. This has the nice benefit of keeping the image dimensions the same (which is sometimes important) as well as preserving any directional aspects it might have (i.e., an image of an arrow pointing left has the same meaning after being "localized").

Aside: I've never seen image pseudo-localization before, but it seems like the obvious next step. Although many images don't need to be localized (ex: a gradient background), some images have text in them or make assumptions that aren't true in all locales (ex: thumbs up means "good"). So it seems pretty handy to know which images aren't localizable! :)

 

Okay, let's see how well you did on the localization quiz! Here's the sample application with PseudoLocalizer enabled:

Pseudo-localized application

Hum... While it's clear most of the text was correctly localizable (and therefore automatically pseudo-localized), it appears the lazy developer (uh, wait, that's me...) forgot to put one of the strings in the RESX file: "Goodbye" is hardcoded to English in the XAML. :( Fortunately, the image is localizable, so it can be changed if necessary. Unfortunately, the fixed-width application window seems to be a little too narrow for longer translations (like German) and is clipping two of the pseudo-localized strings! Nuts, I'm afraid this application may need a bit more work before we can feel good about our ability to ship versions for other languages...

 

[Click here to download the complete source code the PseudoLocalizer and the WPF sample application shown above.]

 

Notes:

  • What I've described in this post works only on WPF; Silverlight has a couple of limitations that prevent what I've done from working exactly as-is. My next blog post will discuss how to overcome those limitations and use PseudoLocalizer on Silverlight, too!

  • The way PseudoLocalizer works so seamlessly is by examining the type of the generated resource class and using (private) reflection to swap in a custom ResourceManager class for the default one. This PseudoLocalizerResourceManager works the same as the default one for loading resources - then it post-processes strings and bitmaps to provide its pseudo-localization effects. Private reflection (i.e., examining and/or modifying the internal data of an object) is generally a bad practice - but I tend to believe it's acceptable here because the target class is simple, its (generated) code is part of the project, and PseudoLocalizer is only ever going to be used as a development tool (i.e., never in a released application).

  • For readers who want a brief overview of how to use RESX-resources in WPF, here you go:

    1. Set the Access Modifier to "Public" in the RESX designer so the auto-generated resource property accessors will be accessible to the WPF data-binding system.

    2. Create an instance of the auto-generated RESX class (typically named "Resources") as a WPF-accessible resource:

      <Window.Resources>
          <properties:Resources x:Key="Resources" xmlns:properties="clr-namespace:PseudoLocalizerWPF.Properties"/>
      </Window.Resources>
    3. Create a Binding that references the relevant properties of that resource wherever you want to use them:

      <TextBlock Text="{Binding Path=AlphabetLower, Source={StaticResource Resources}}"/>
  • Because RESX files expose images as instances of System.Drawing.Image and WPF deals with System.Windows.Media.ImageSource, I wrote a simple IValueConverter to convert from the former to the latter. (And it's part of the source code download.) Using BitmapToImageSourceConverter follows the usual pattern for value converters:

    1. Create an instance as a resource:

      <delay:BitmapToImageSourceConverter x:Key="BitmapToImageSourceConverter" xmlns:delay="clr-namespace:Delay"/>
    2. Use that resource in the relevant Binding:

      <Image Source="{Binding Path=Image, Source={StaticResource Resources}, Converter={StaticResource BitmapToImageSourceConverter}}"/>

 

Because the cost of fixing defects increases (dramatically) as a product gets closer to shipping, it's important to find and fix issues as early as possible. Localization can be a tricky thing to get all the kinks worked out of, so it's helpful to have good tools around to ensure a team is following good practices. With its low cost, simple implementation and friction-free usage model, I'm hopeful PseudoLocalizer will become another good tool to help people localize successfully!

"Might as well face it, you're addicted to blob..." [BlobStoreApi update adds container management, fragmented response handling, other improvements, and enhanced Amazon S3 support by Delay.Web.Helpers!]

As part of my previous post announcing the Delay.Web.Helpers assembly for ASP.NET and introducing the AmazonS3Storage class to enable easy Amazon Simple Storage Service (S3) blob access from MVC/Razor/WebMatrix web pages, I made some changes to the BlobStoreApi.cs file it built on top of. BlobStoreApi.cs was originally released as part of my BlobStore sample for Silverlight and BlobStoreOnWindowsPhone sample for Windows Phone 7; I'd made a note to update those two samples with the latest version of the code, but intended to blog about a few other things first...

 

Fragmented response handling

BlobStoreOnWindowsPhone sample

That's when I was contacted by Steve Marx of the Azure team to see if I could help out Erik Petersen of the Windows Phone team who was suddenly seeing a problem when using BlobStoreApi: calls to GetBlobInfos were returning only some of the blobs instead of the complete list. Fortunately, Steve knew what was wrong without even looking - he'd blogged about the underlying cause over a year ago! Although Windows Azure can return up to 5,000 blobs in response to a query, it may decide (at any time) to return fewer (possibly as few as 0!) and instead include a token for getting the rest of the results from a subsequent call. This behavior is deterministic according to where the data is stored in the Azure cloud, but from the point of view of a developer it's probably safest to assume it's random. :)

So something had changed for Erik's container and now he was getting partial results because my BlobStoreApi code didn't know what to do with the "fragmented response" it had started getting. Although I'd like to blame the documentation for not making it clear that even very small responses could be broken up, I really should have supported fragmented responses anyway so users would be able to work with more than 5,000 blobs. Mea culpa...

I definitely wanted to add BlobStoreApi support for fragmented Azure responses, but what about S3? Did my code have the same problem there? According to the documentation: yes! Although I don't see mention of the same "random" fragmentation behavior Azure has, S3 enumerations are limited to 1,000 blobs at a time, so that code needs the same update in order to support customer scenarios with lots of blobs. Fortunately, the mechanism both services use to implement fragmentation is quite similar and I was able to add most of the new code to the common RestBlobStoreClient base class and then refine it with service-specific tweaks in the sub-classes AzureBlobStoreClient and S3BlobStoreClient.

Fortunately, the extra work to support fragmented responses is all handled internally and is therefore invisible to the developer. As such, it requires no changes to existing applications beyond updating to the new BlobStoreApi.cs file and recompiling!

 

Container support

While dealing with fragmented responses was fun and a definite improvement, it's not the kind of thing most people appreciate. People don't usually get excited over fixes, but they will get weak-kneed for new features - so I decided to add container/bucket management, too! Specifically, it's now possible to use BlobStoreApi to list, create, and delete Azure containers and S3 buckets using the following asynchronous methods:

public abstract void GetContainers(Action<IEnumerable<string>> getContainersCompleted, Action<Exception> getContainersFailed);
public abstract void CreateContainer(string containerName, Action createContainerCompleted, Action<Exception> createContainerFailed);
public abstract void DeleteContainer(string containerName, Action deleteContainerCompleted, Action<Exception> deleteContainerFailed);

To be clear, each instance of AzureBlobStoreClient and S3BlobStoreClient is still associated with a specific container (the one that was passed to its constructor) and its blob manipulation methods act only on that container - but now it's able to manipulate other containers, too.

Aside: In an alternate universe, I might have made the new container methods static because they don't have a notion of a "current" container the same way the blob methods do. However, what I want more than that is to be able to leverage the existing infrastructure I already have in place for creating requests, authorizing them, handling responses, etc. - all of which take advantage of instance methods on RestBlobStoreClient that AzureBlobStoreClient and S3BlobStoreClient override as necessary. In this world, it's not possible to have static methods that are also virtual, so I sacrificed the desire for the former in order to achieve the efficiencies of the latter. Maybe one day I'll refactor things so it's possible to have the best of both worlds - but for now I recommend specifying a null container name when creating instances of AzureBlobStoreClient and S3BlobStoreClient when you want to ensure they're used only for container-specific operations (and not for blobs, too).

 

Other improvements

Delay.Web.Helpers AmazonS3Storage sample

With containers getting a fair amount of love this release, it seemed appropriate to add a containerName parameter to the AzureBlobStoreClient constructor so it would look the same as S3BlobStoreClient's constructor. This minor breaking change means it's now necessary to specify a container name when creating instances of AzureBlobStoreClient. If you want to preserve the behavior of existing code (and don't want to have to remember that "$root" is the magic root container name for Azure (and previous default)), you can pass AzureBlobStoreClient.RootContainerName for the container name.

In the process of doing some testing of the BlobStoreApi code, I realized I hadn't previously exposed a way for an application to find out about asynchronous method failures. While it's probably true that part of the reason someone uses Azure or S3 is so they don't need to worry about failures, things can always go wrong and sometimes you really need to know when they do. So in addition to each asynchronous method taking a parameter for the Action to run upon successful completion, they now also take an Action<Exception> parameter which they'll call (instead) when a failure occurs. This new parameter is necessary, so please provide a meaningful handler instead of passing null and assuming things will always succeed. :)

I've also added a set of new #defines to allow BlobStoreApi users to easily remove unwanted functionality. Because they're either self-explanatory or simple, I'm not going to go into more detail here (please have a look at the code if you're interested): BLOBSTOREAPI_PUBLIC, BLOBSTOREAPI_NO_URIS, BLOBSTOREAPI_NO_AZUREBLOBSTORECLIENT, BLOBSTOREAPI_NO_S3BLOBSTORECLIENT, and BLOBSTOREAPI_NO_ISOLATEDSTORAGEBLOBSTORECLIENT.

Aside: Actually, BLOBSTOREAPI_PUBLIC deserves a brief note: with this release of the BlobStoreApi, the classes it implements are no longer public by default (they're expected to be consumed, not exposed). This represents a minor breaking change, but the trivial fix is to #define BLOBSTOREAPI_PUBLIC which restores things to being public as they were before. That said, it might be worth taking this opportunity to make the corresponding changes (if any) to your application - but that's entirely up to you and your schedule.

The last thing worth mentioning is that I've tweaked BlobStoreApi to handle mixed-case blob/container names properly. Formerly, passing in upper-case characters for a blob name could result in failures for both Azure and S3; with this change in place, that scenario should work correctly.

 

BlobStore with silly sample data

 

New BlobStore and BlobStoreOnWindowsPhone samples

I've updated the combined BlobStore/BlobStoreOnWindowsPhone download to include the latest version of BlobStoreApi with all the changes outlined above. Although there are no new features in either sample, they both benefit from fragmented container support and demonstrate how to use the updated API methods properly.

[Click here to download the complete BlobStore source code and sample applications for both Silverlight 4 and Windows Phone 7.]

 

New Delay.Web.Helpers release and updated sample

The Delay.Web.Helpers download includes the latest version of BlobStoreApi (so it handles fragmented containers) and includes new support for container management in the form of the following methods on the AmazonS3Storage class:

/// <summary>
/// Lists the blob containers for an account.
/// </summary>
/// <returns>List of container names.</returns>
public static IList<string> ListBlobContainers() { ... }

/// <summary>
/// Creates a blob container.
/// </summary>
/// <param name="containerName">Container name.</param>
public static void CreateBlobContainer(string containerName) { ... }

/// <summary>
/// Deletes an empty blob container.
/// </summary>
/// <param name="containerName">Container name.</param>
public static void DeleteBlobContainer(string containerName) { ... }

As I discuss in the introductory post for Delay.Web.Helpers, I'm deliberately matching the existing WindowsAzureStorage API with my implementation of AmazonS3Storage, so these new methods look and function exactly the same for both web services. As part of this release, I've also updated the Delay.Web.Helpers sample page to show off the new container support as well as added some more automated tests to verify it.

[Click here to download the Delay.Web.Helpers assembly, its complete source code, all automated tests, and the sample web site.]

 

Summary

While I hadn't thought to get back to blobs quite so soon, finding out about the fragmented response issue kind of forced my hand. But all's well that ends well - I'm glad to have added container supported to Delay.Web.Helpers because that means it now has complete blob-related feature parity with the WindowsAzureStorage web helper and that seems like a Good Thing. What's more, I got confirmation that the Windows Azure Managed Library does not support Silverlight, so my BlobStoreApi is serving duty as an unofficial substitute for now. [Which is cool! And a little scary... :) ]

Whether you're using Silverlight on the desktop, writing a Windows Phone 7 application, or developing a web site with ASP.NET using MVC, Razor, or WebMatrix, I hope today's update helps your application offer just a little more goodness to your users!

"And she'd say, 'Can you see ... what I'm saying?'" [How to: Localize a Windows Phone 7 application that uses the Windows Phone Toolkit into different languages]

While it might be convenient if everybody spoke the same language (or communicated via telepathy), that's not the world we live in. :) Therefore, building applications that can be easily translated to other languages is an important consideration. Fortunately, it's easy - and it's covered in the MSDN article How to: Build a Localized Application for Windows Phone. But what's not covered is how to localize the controls in the Silverlight for Windows Phone Toolkit. As you might expect, it's fairly similar, but I've had a few people ask about this explicitly and I decided to do a quick post on the topic. As long as I was at it, I figured I'd show the entire process from start to finish just to make things a little easier...

 

Prepare the emulator/phone

In order to monitor our progress as the sample application gets localized, it's helpful to work in a non-default language which makes it easy to identify the un-localized parts. I run an English operating system, so I'll use Spanish for this example:

  1. Open the Settings application

  2. Choose region & language

  3. Open the Display language picker

  4. Change to "Español"

  5. Tap the link to restart the emulator/phone:

    Settings

 

Create a new application

We'll start with a brand new application using the default template provided by the Visual Studio 2010 development tools:

  1. From the File menu, choose New, then Project...

  2. Go to the "Silverlight for Windows Phone" section and create a "Windows Phone Application" with the name of your choice

  3. From the Project menu, choose Add New Item...

  4. Select "Resources File" and name it AppResources.resx

  5. Add an entry for Name="Title" and Value="welcome"

  6. Open MainPage.xaml.cs and add the following to the end of the constructor to set the text of the existing PageTitle element:

    PageTitle.Text = AppResources.Title;
  7. Run the application to see the custom title in English:

    New application

 

Localize the application

Now let's localize the sample application so it uses the proper language for the user's settings. In the steps below, we'll add support for Spanish (via the "es" language code), but adding other languages is a simple matter of repeating these steps using the other language's code and translations. It's a little bit of effort, but it's all quite simple:

  1. In the Solution Explorer window, select AppResources.resx by clicking on it

  2. Press Ctrl+C, then Ctrl+V to create a copy of AppResources.resx

  3. Select Copy of AppResources.resx and press F2 to rename it to AppResources.es.resx

  4. Staying in the Solution Explorer window, right-click the project root node and choose Unload Project

  5. Right-click the project node again and choose Edit [ProjectName].csproj

  6. Change the existing SupportedCultures element to be:

    <SupportedCultures>es</SupportedCultures>
  7. Back in the Solution Explorer window, right-click the project node and choose Reload Project

  8. Open AppResources.es.resx and change "welcome" to "bienvenido"

  9. Run the application and verify the custom title now shows up in Spanish (no code changes necessary!):

    Localized application

 

Add the Windows Phone Toolkit project

Now we'll modify the sample application to reference the Windows Phone Toolkit. (You can read more about the Windows Phone Toolkit in my introductory post.) Rather than adding a binary reference to the Toolkit, we'll add a project reference and build the Toolkit code as part of the sample application (for reasons that will become clear soon):

  1. Go to http://silverlight.codeplex.com/ and download the "Silverlight for Windows Phone Toolkit Source & Sample" ZIP file

  2. Unblock the ZIP file (see the notes at the end of this post for instructions)

  3. Right-click the ZIP file and choose Extract All... to extract all files to the directory of your choice

  4. From the File menu in Visual Studio, choose Add, then Existing Project...

  5. Choose the Microsoft.Phone.Controls.Toolkit.csproj file found in the same-named directory of the extracted content

  6. Click on the application project node in Solution Explorer to make it active again

  7. From the Project menu, choose Add Reference...

  8. Switch to the Projects tab and pick the Microsoft.Phone.Controls.Toolkit project

  9. Press F6 and verify the solution builds both projects successfully:

    ------ Build started: Project: Microsoft.Phone.Controls.Toolkit, Configuration: Debug Any CPU ------
    ...
    ------ Build started: Project: LocalizedPhoneApplicationWithToolkit, Configuration: Debug Any CPU ------
    ...
    ========== Build: 2 succeeded or up-to-date, 0 failed, 0 skipped ==========

 

Add some Windows Phone Toolkit controls

Now it's time to add two of those handy-dandy Windows Phone Toolkit controls (both covered in the aforementioned blog post): DatePicker and ToggleSwitch. We'll follow the usual steps for referencing third-party controls which should be pretty familiar to everyone:

  1. Open MainPage.xaml and switch to XAML view

  2. Add the following to the top of the file along with the other xmlns definitions:

    xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
  3. Paste the following inside the empty "ContentPanel" Grid:

    <StackPanel>
        <toolkit:DatePicker/>
        <toolkit:ToggleSwitch/>
    </StackPanel>
  4. Optional: Follow the steps in the notes of my Windows Phone Toolkit introduction to add the DatePicker Application Bar icons to the project

  5. Run the application to see the text of the Toolkit controls in English:

    Toolkit ToggleSwitch Toolkit DatePicker

    Note: The day and month names are already in Spanish because they come from the operating system which knows to use the current language for dates. The Spanish-correct day/month/year formatting (vs. the United States default of month/day/year) is automatically provided by the DatePicker control.

 

Localize the Windows Phone Toolkit controls

Finally, we'll localize the Toolkit controls using the same process we did for the sample application itself:

  1. In Solution Explorer, expand the "Properties" folder for the Toolkit and click Resources.resx

  2. Press Ctrl+C, then Ctrl+V to create a copy of Resources.resx

  3. Select Copy of Resources.resx and press F2 to rename it to Resources.es.resx

  4. Open Resources.es.resx and change "CHOOSE DATE" to "ELIGE UNA FECHA", "cancel" to "cancelar", "done" to "listo", "Off" to "Desactivado", and "On" to "Activado"

  5. Run the application and verify the Toolkit control text is also in Spanish now:

    Localized Toolkit ToggleSwitch Localized Toolkit DatePicker

    Note: If you get the error Unable to copy file "obj\Debug\Microsoft.Phone.Controls.Toolkit.dll" to "..\Bin\Debug\Microsoft.Phone.Controls.Toolkit.dll". The process cannot access the file '..\Bin\Debug\Microsoft.Phone.Controls.Toolkit.dll' because it is being used by another process. when compiling, close all documents in Visual Studio via Window/Close All Documents, then restart Visual Studio itself.

 

At the end of the day, translating an application isn't just a nice thing to do, it's good business! We operate in a global marketplace and that means localized products can have a big advantage over their single-language-only competitors. While good localization can't save a bad application from itself, it can make a good application stand out. So please think about localization in your next marketplace application - your customers will appreciate it!

Gracias. :)

 

Notes:

  • People sometimes ask why the Windows Phone Toolkit (or the Silverlight Toolkit, for that matter) isn't already localized to the same set of languages the host platform supports. While I personally think it should be, doing so would take time and money and those two things can be in rather short supply at times. :) While I hope to see Toolkit localization become official some day, the good news for now is that things have been implemented such that they're easily localizable.

  • I've taken the quick/easy route of assigning the localized text resource directly to PageTitle.Text in the application's constructor, but that is not the recommended technique. Instead, what's generally preferred is what's described in the second half of the "Replacing hard-coded strings with strings in a resource file" section of the MSDN documentation: use a property Binding in XAML to reference the localized resources via a custom object as a StaticResource. It's a little unintuitive at first, but a very reasonable solution in practice. :)

  • After downloading the ZIP file for the Windows Phone Toolkit source code, you should "unblock" it before extracting its contents to avoid warnings from Visual Studio like Security Warning, You should only open projects from a trustworthy source. and The "ValidateXaml" task failed unexpectedly. System.IO.FileLoadException: Could not load file or assembly .... Unblocking is a simple matter of right-clicking on the ZIP file, choosing Properties from the context menu, clicking the Unblock button in the lower, right-hand corner of the resulting dialog, and hitting OK or Apply:

    Unblock button in Properties dialog

Hash for the holidays [Managed implementation of CRC32 and MD5 algorithms updated; new release of ComputeFileHashes for Silverlight, WPF, and the command-line!]

It feels like a long time since I last wrote about hash functions (though certain curmudgeonly coworkers would say not long enough!), and there were a few loose ends I've been meaning to deal with...

Aside: If my hashing efforts are new to you, more information can be found in my introduction to the ComputeFileHashes command-line tool and the subsequent release of ComputeFileHashes versions for the WPF and Silverlight platforms.

 

When I first needed a managed implementation of the CRC-32 algorithm a while back, I ended up creating one from the reference implementation. Thanks to the strong similarities between C and C#, the algorithm itself required only minimal tweaks and the majority of my effort was packaging it up as a .NET HashAlgorithm. Because HashAlgorithm is the base class of all .NET hash functions, the CRC32 class ends up being trivial to drop into any .NET application that already deals with hashing.

ComputeFileHashesWPF

The Silverlight platform doesn't include an implementation of the MD5 algorithm like "desktop" .NET does, and I soon ended up creating an MD5 implementation from the reference code so I could support that algorithm on Silverlight (and now Windows Phone, too). Again, the C algorithm translated to C# fairly easily - though there's quite a lot more code for MD5 than CRC32 - and the HashAlgorithm base class makes it easy to reuse. Over the next few days, I made a couple of minor revisions to the CRC32 and MD5Managed classes, but have otherwise left things alone. I've used ComputeFileHashes successfully ever since, and things seemed to be in a pretty good state.

 

Then one day kind reader Maurizio contacted me (from Italy!) to report a bug in my CRC32 wrapper: there was a missing variable in a loop that could lead to problems if someone passed a non-0 value as the inputOffset parameter of TransformBlock. Fortunately, this isn't a particularly common scenario - the "primary" overload of ComputeHash doesn't do it, none of my ComputeFileHashes code does it, and most typical scenarios probably won't do it, either. That said, a bug is a bug (is a bug), and I made a note to fix it when I got a chance... And I finally had that chance last week! :)

C:\T>ComputeFileHashesCL.exe ZeroByteFile.txt

C:\T\ZeroByteFile.txt
100.0%
CRC32: 00000000
MD5: D41D8CD98F00B204E9800998ECF8427E
SHA1: DA39A3EE5E6B4B0D3255BFEF95601890AFD80709
SHA256: E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855
SHA384: 38B060A751AC96384CD9327EB1B1E36A21FDB71114BE07434C0CC7BF63F6E1DA274EDEBFE76F65FBD51AD2F14898B95B
SHA512: CF83E1357EEFB8BDF1542850D66D8007D620E4050B5715DC83F4A921D36CE9CE47D0D13C5D85F2B0FF8318D2877EEC2F63B931BD47417A81A538327AF927DA3E
RIPEMD160: 9C1185A5C5E9FC54612808977EE8F548B2258D31

 

And as long as I was already messing with the ComputeFileHashes code (which meant recompiling for each platform, re-packaging, uploading, etc.), there were a few other things I decided to take care of at the same time. And it's just as well - in the process of doing so, I discovered (and fixed) a seemingly obscure bug in MD5Managed (which I suspect has never been hit in real life). Along the way, I added the complete suite of .NET HashAlgorithms to each tool so you'll automatically get the results of every supported algorithm when you hash a file!

 

ComputeFileHashes has been a fun project and a nice demonstration of how .NET lets you run the same code across a wide variety of environments and platforms. And the comprehensive automated test framework I added this time around makes me feel better about the correctness of these two HashAlgorithms. I deal with hashes regularly and have found all flavors of ComputeFileHashes to be handy tools to have around - especially the Silverlight version which brings simple, lightweight, install-free hashing to nearly every machine in the world. :)

 

[Click here to download the complete source code for the command-line, Silverlight, and WPF implementations of ComputeFileHashes along with the new test project.]

Click here or on the image below to run ComputeFileHashes in your browser with Silverlight 4:

ComputeFileHashesSL

Note: Bookmark the link above for easy access to hashing anytime, anywhere, on any machine!

 

Here are the major changes since last time:

  • CRC32 bug fix: HashCore was not adding the ibStart offset in its for loop. This is the issue Maurizio reported and would affect all scenarios where a non-0 value was passed for ibStart.

  • MD5Managed bug fix: MD5Update was not adding the inputIndex offset in its call to MD5Transform. This is the obscure issue found by the new test framework - in certain fairly specific circumstances (mostly around odd offsets and buffer sizes), the incorrect offset could result in an invalid hash result.

  • All supported algorithms are run on each file. Initially, only CRC32, MD5, and SHA1 were supported because they were the most common at the time and because I didn't want to waste CPU cycles on obscure algorithms that were hardly ever used. But since then, some of the "obscure" algorithms have become more common (ex: SHA256) and multi-core CPUs have become much more widespread. Because the ComputeFileHashes tools are already multi-processor friendly and because quad-processor CPUs are now commonplace, I've decided not to limit them due to CPU cycles. Most files hash instantaneously anyway, so the additional algorithms won't slow things down there; for longer files where CPU might start to dominate over disk access, the additional overhead shouldn't be that big of a deal. With this release, the bias is for convenience, and I'm optimistic that's the right tradeoff most of the time. :)

    Here's what each tool supports:

    • ComputeFileHashesCL, ComputeFileHashesWPF: CRC32, MD5, SHA1, SHA256, SHA384, SHA512, RIPEMD160
    • ComputeFileHashesSL: CRC32, MD5, SHA1, SHA256

    (Note: The Silverlight platform doesn't provide SHA384/SHA512/RIPEMD160. (And I haven't done my own implementation. (Yet...)))

  • There's a comprehensive set of automated tests for CRC32 and MD5Managed. I'd done some basic testing of this code in the past, but hadn't covered the edge cases - and a few bugs slipped by because of that. So I wanted to create a thorough automated test suite this time around and do what I could to cover all the bases.

    • The automated tests have a small library of different inputs and known-good hashes and process it in lots of different ways: all at once, in all different chunk sizes, with and without Initialize, etc.. If any of these techniques generates the wrong hash, the test suite reports the failure.
    • The new tests are thorough enough to yield 100% code coverage on both the CRC32 and MD5Managed implementations.
    • In addition to testing my CRC32 and MD5Managed classes, the automated tests also test the .NET MD5CryptoServiceProvider class - not because I expect to find errors in it, but so I can ensure both MD5 implementations behave the same in each scenario.
    • Consequently (and as a result of the more thorough coverage) CRC32 and MD5Managed now behave the same as the .NET implementations for invalid scenarios, too - all the way to exception-level compatibility for misuse of the API!
    • The only difference is for CryptographicUnexpectedOperationException which can't be constructed on Silverlight - its base class CryptographicException is thrown instead in cases where the hash value is retrieved before the process has been finalized.
  • The Visual Studio solution and project files have been upgraded to the Visual Studio 2010 format. This makes developing ComputeFileHashes in Visual Studio 2010 easy and enables the use of its new and improved feature set.

  • The CRC32/MD5Managed classes and the three ComputeFileHashes programs are now code analysis-clean for the complete Visual Studio 2010 rule set. Additional code analysis rules were introduced with VS 2010 and they reported some new violations in the code. These have all been fixed (or suppressed where appropriate).

  • The namespace of the CRC32 and MD5Managed classes has been changed to "Delay". This change brings these classes inline with the rest of the sample code I publish and makes their generality a bit clearer.

  • ComputeFileHashesCL remains a .NET 2.0 application for maximum versatility. By targeting .NET 2.0, ComputeFileHashesCL runs nearly everywhere .NET does.

  • ComputeFileHashesWPF is now a .NET 4 application for compactness and ease of distribution. ComputeFileHashesWPF used to have a dependency on the WPFToolkit for its DataGrid control. Because that control is part of the .NET 4 Framework, the new ComputeFileHashesWPF no longer depends on any non-Framework assemblies and can be distributed as a single file.

  • ComputeFileHashesSL is now a Silverlight 4 application to make use of new features in that platform. Most notably, ComputeFileHashesSL uses Silverlight's drag+drop support to enable the handy scenario of dragging a file directly from Windows Explorer and dropping it onto the ComputeFileHashesSL window to hash it (just like ComputeFileHashesWPF already supported). Additionally, I'm making use of my SetterValueBindingHelper class to use Bindings in a Setter and create the same ToolTip experience that ComputeFileHashesWPF already had: hovering over a hash failure shows the reason for the failure (typically because the file was locked by another process). Consequently, the "Details" column is no longer necessary in ComputeFileHashesSL and has been removed.

  • The ClickOnce flavor of ComputeFileHashesWPF is no longer supported. With ComputeFileHashesSL's functionality getting closer to that of ComputeFileHashesWPF and the elimination of the WPFToolkit.dll dependency from ComputeFileHashesWPF, the need for a ClickOnce install seems minimal and has been removed.

  • The version number has been updated to 2010-11-30.

Better together [DynamicOrientationChanges and TransitionFrame create a comprehensive transition experience for Windows Phone 7]

I've previously blogged about my implementation of AnimateOrientationChangesFrame, FadeOrientationChangesFrame, and HybridOrientationChangesFrame. As part of my DynamicOrientationChanges sample, these classes smoothly animate an application's layout transition as the phone orientation changes from portrait to landscape (and vice-versa).

HybridOrientationChanges sample

 

More recently, I blogged about the Windows Phone Toolkit's support for animated page transitions. The TransitionFrame class (and its helpers) work with the platform's navigation framework to animate the transitions among different pages within an application.

TransitionFrame sample

 

Though they address different scenarios, both transition helpers make it easy for developers' Silverlight applications to match the behavior of the core Windows Phone 7 applications. The obvious question is whether it's possible to use them together...

At first glance, the fact that both approaches work by directly subclassing PhoneApplicationFrame means they can't be combined as-is.

Aside: This is one of the drawbacks of subclassing as a method of adding or extending functionality in a library: you only get one chance to subclass the base class. (But because you can create as many subclasses as you want, subclassing a subclass can be an excellent alternative!)

So it has always been my hope that these two approaches could be easily combined to create an application that animates both kinds of transitions. Because the Windows Phone Toolkit's TransitionFrame class is more official, it seems appropriate to leave that one alone and try subclassing the DynamicOrientationChanges classes from TransitionFrame. Based on what I knew about both implementations, I assumed this would work, but I didn't get around to trying it before Andy Wigley contacted me with the same question! Well, Andy was brave enough to give this a go and reports that it works well - he wrote a nice summary here:

Best of breed Page rotation animations

If you're interested in combining these two scenarios, I highly recommend checking out Andy's post - he's made the change easy to understand and the steps are easy to follow!

Thanks, Andy! :)

 

PS - If others have success merging the two transition animation implementations, I'll go ahead and make this an official part of the DynamicOrientationChanges project in a future release. Please don't be shy - give it a try and let me know how it goes!

You've got questions; I've got dumb looks [PDC10's Channel 9 Live Windows Phone 7 Q&A session available online and offline]

A few weeks ago, Microsoft held its annual Professional Developer's Conference (PDC10) and I had the privilege of being a (small!) part of it. This year's content spanned a variety of topics, including Windows Azure, HTML 5, Windows Phone 7, and more! The Channel 9 folks were there in force with two full days of live interviews - including a session on Windows Phone 7 Questions & Answers. Under the expert direction of Dan Fernandez, team members Jeff Wilcox, Peter Torr, and myself spent about 30 minutes talking about building Windows Phone 7 applications and answering questions from the (virtual) audience. It was a lot of fun, and I'd like to think it might even be educational! :)

 

Channel 9 Windows Phone 7 Question and Answer session

 

Here are the viewing options:

 

While you're at it, please check out some of the other PDC10 content online - there's a ton of good stuff and it's all free to enjoy!

The taming of the phone [New SetterValueBindingHelper sample demonstrates its usefulness on Windows Phone 7 (and Silverlight 4)]

If you've done much work with Bindings in Silverlight, you're probably aware that Silverlight doesn't support applying Bindings using a Style's Setter the same way its big brother WPF does. This limitation isn't a big deal at first because people don't tend to need that until they're more familiar with the platform and have started using MVVM and taking advantage of the ItemContainerStyle property. But once you're working with scenarios where it's relevant, being able to specify Bindings in a Setter can be extremely useful because it replaces a bunch of code/subclassing with a single line of XAML!

SetterValueBindingHelperDemo sample on Silverlight

 

Fortunately, it's possible to implement this feature outside the Silverlight framework! (Or at least to implement enough of it to cover nearly all the relevant scenarios.) I originally wrote SetterValueBindingHelper for Silverlight 3 as part of an application building exercise. Later on, I updated SetterValueBindingHelper to accommodate implementation changes in the Silverlight 4 platform - and was able to do so in a way that continued to work on Silverlight 3. So because Windows Phone 7 is based on Silverlight 3, I had a strong suspicion SetterValueBindingHelper would work there, too. But it wasn't until a couple of days ago that I had a chance to validate my theory - and now that I have, here's an updated version of the Silverlight sample for Windows Phone:

SetterValueBindingHelperDemo sample on Windows Phone 7

 

Other than converting the Silverlight sample's TreeView to a ListBox (because the former doesn't exist for Windows Phone), the sample works just the same on Windows Phone as on Silverlight. The code for SetterValueBindingHelper is nearly identical as well - the only difference being that the code to walk an application's assemblies for resolving attached property types can't be used because Windows Phone doesn't support the AssemblyPart.Load method. (But because this particular feature isn't used very often, its absence probably won't even be noticed.)

As a quick reminder, here's what a typical use looks like:

<ListBox.ItemContainerStyle>
    <Style TargetType="ListBoxItem">
        <!-- WPF syntax:
            <Setter Property="Foreground" Value="{Binding Color}"/> -->
        <Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
            <Setter.Value>
                <delay:SetterValueBindingHelper
                    Property="Foreground"
                    Binding="{Binding Color}"/>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.ItemContainerStyle>
Aside: SetterValueBindingHelper supports more advanced scenarios, too - please refer to the previous post (or the original post) for information and examples.

 

[Click here to download the code for SetterValueBindingHelper along with sample applications for Silverlight 4 and Windows Phone 7.]

 

I've gotten a lot of great feedback from developers who have made use of SetterValueBindingHelper in their applications. I've found it quite helpful in my own projects, and I'm glad many of you have, too! With today's announcement, I hope another "generation" is able to use SetterValueBindingHelper to benefit from the many advantages of the Silverlight platform's rich data-binding support. :)

ListPicker? I hardly even know 'er! [A detailed overview of the Windows Phone Toolkit's ListPicker control]

In yesterday's post, I announced the second release of the Silverlight for Windows Phone Toolkit and gave an overview of the four new controls it includes. (For a discussion of the controls in the original Windows Phone Toolkit, please see my announcement for that release.) In today's post, I want to focus on one of the new controls, ListPicker, and discuss it in detail.

 

The sample application associated with the official Windows Phone Toolkit download offers a great overview of the Windows Phone Toolkit controls, but (deliberately) doesn't get into specific detail on any of them. This post is all about details, so I've written a dedicated sample application which is the source of all the XAML snippets and screenshots below:

[Click here to download the complete source code for the ListPickerSamples application.]

 

Background

From my previous post:

ListPicker is the Windows Phone 7 equivalent of the ComboBox control: give it a list of items and it will show the selected one and also allowing the user to pick from a list if they want to change it. The core applications on Windows Phone 7 implement two kinds of list selection: an in-place expander for picking among five or fewer items, and a full-screen popup for picking among more. The Toolkit's ListPicker control combines both experiences into one handy package by automatically selecting the right UX based on the number of items in its list! It is a standard ItemsControl subclass with all the common elements of a Selector, so the API will be familiar to just about everyone. In fact, you can take most existing ListBoxes and convert them to ListPickers just by changing the control name in XAML!

That's the gist: ListPicker is the control of choice for selecting values in Windows Phone 7 applications. To be more explicit, it is most appropriate in "Settings"-like scenarios where the user is offered a variety of different options and it makes sense to display only the current value (with an option to show everything once the user decides to make a change). Conversely, ListPicker is not appropriate for displaying long lists of data that the user is going to scan and scroll; scenarios like the "People" or "Marketplace" applications are better served by a ListBox or the Windows Phone Toolkit's new LongListSelector.

 

Typical Use

The most common scenario for ListPicker looks something like this:

<StackPanel>
    <toolkit:ListPicker
        Header="Rating"
        ItemsSource="{Binding Ratings}"
        SelectedIndex="1"
        SelectionChanged="RatingSelectionChanged"/>
    <TextBlock
        x:Name="RatingSelection"
        CacheMode="BitmapCache"/>
    ...
</StackPanel>

Which gets displayed like this (in normal and expanded forms):

Typical example (normal) Typical example (expanded)

As you'd expect for an ItemsControl subclass, the ItemsSource property is used to provide the list of items (see also: the Items property). And as you'd expect for a Selector-like control, the SelectionChanged event is used to signal changes and the SelectedIndex property is used to get or set the selection (see also: SelectedItem). Everything so far looks just like ListBox - the only difference is the Header property which can optionally be used to provide a simple, platform-consistent label for the ListPicker that offers additional context about the control's purpose (see also: HeaderTemplate).

Aside: The built-in ListBox control will throw an exception if you set SelectedIndex as in the example above because it tries to apply the selection before the Binding has provided the list of items. ListPicker specifically handles this common scenario so you don't have to jump through hoops to make it work. :)

 

Custom Templates

Displaying strings is all well and good, but sometimes it's nice to display richer content:

Custom template (normal) Custom template (expanded)

The first thing to do is set the ItemTemplate property as you would for ItemsControl or ListBox - that applies the specified DataTemplate to each item and formats it attractively in the usual manner. That works great, but what about ListPicker's Full mode that's used when the list has too many items? By default, the same ItemTemplate automatically applies there, too, so you may not need to do anything more! However, the Full mode UI uses the entire screen, so it's pretty common to want to specifically customize the appearance of the items for that mode. Therefore, the FullModeItemTemplate property lets you provide a different DataTemplate to be used in the Full mode scenario. Another relevant property for such cases is FullModeHeader which sets the content that's shown at the top of the full-screen "popup".

<toolkit:ListPicker
    Header="Spectrum"
    FullModeHeader="CHOOSE COLOR"
    ItemsSource="{Binding Rainbow}"
    ItemTemplate="{StaticResource RainbowTemplate}"
    FullModeItemTemplate="{StaticResource LargeRainbowTemplate}"/>

 

Threshold Overrides

By default, lists with five or fewer items expand in-place while lists with more items switch to a full-screen selection interface. This behavior matches the platform guidelines, but sometimes it might make sense to nudge the threshold one way or another (for very large or very small items, perhaps). You might even want to force a ListPicker to always use Expanded or Full mode...

Threshold (full) Threshold (expanded)

For these scenarios, there's the ItemCountThreshold property: it specifies the maximum number of items that will be displayed in Expanded mode. In addition to nudging it up or down a bit for custom scenarios, it can also be set to 0 to "always use Full mode" or a very large number to "always use Expanded mode". Granted, an application that forces Expanded mode for a list of 1000 items probably won't be easy to use - but the freedom is there to allow developers and designers to dial-in exactly the kind of experience they want.

<toolkit:ListPicker
    Header="Rating"
    FullModeHeader="CHOOSE RATING"
    ItemsSource="{Binding Ratings}"
    ItemCountThreshold="0"/>
<toolkit:ListPicker
    Header="Spectrum"
    ItemsSource="{Binding Rainbow}"
    ItemTemplate="{StaticResource RainbowTemplate}"
    ItemCountThreshold="100"/>

 

Two-Way Binding

Two-way binding

As you'd expect, ListPicker can be used with TwoWay Bindings as well. This is particularly convenient for the SelectedIndex/SelectedItem properties where it's common to want to set the initial value based on a data model (see also: MVVM) and/or when you want the model to update directly when selection changes. The corresponding XAML looks just how you'd expect:

<toolkit:ListPicker
    Header="Network"
    ItemsSource="{Binding Networks}"
    SelectedItem="{Binding CurrentNetwork, Mode=TwoWay}"/>
<StackPanel
    Orientation="Horizontal"
    Margin="{StaticResource PhoneMargin}"
    CacheMode="BitmapCache">
    <TextBlock Text="Current Network: "/>
    <TextBlock Text="{Binding CurrentNetwork}"/>
</StackPanel>

 

Tips and Tricks

At this point, I hope everyone knows how ListPicker works and has a good feel for when/where/why/how to use it. That being the case, there are a few additional things I'd like to draw attention to:

  1. The ListPicker philosophy is that "there is always an active selection", so it makes sure to select an item when it initializes or when the list changes. This automatic selection (of the first item in most cases) causes the SelectionChanged event to fire - and that causes the application's associated event handler to run (assuming one has been registered). In practice, this "initialization-time" event catches some people by surprise - but it's the intended behavior (and folks tend to agree it's correct once they understand why it happens). Now that you know about it, maybe your development experience will be a bit easier. :)

    Aside: If you want to ignore this event in code, it should be easy to detect because its SelectionChangedEventArgs.RemovedItems collection will be empty (have 0 items). And the only time that happens is when ListPicker is transitioning from an empty list to a non-empty one (e.g., on startup).
  2. ListPicker's transitions between Normal and Expanded mode are effectively animations of the control's Height. Because Height changes cause a layout pass, they don't take place on the composition thread and therefore are more susceptible to performance issues. An easy way to mitigate this in the typical "list of items in a StackPanel" scenario is to add CacheMode=BitmapCache to the elements that appear below the ListPicker (i.e., those that are pushed down by the animation). Please refer back to the first XAML snippet for an example - this tweak allows the Silverlight layout system to animate such controls as bitmaps and that helps the animation run a bit more smoothly.

    Aside: If you don't want to apply BitmapCache to every control individually, an alternate approach is to wrap the affected controls in another StackPanel and set the CacheMode property on the StackPanel instead. Please see the last XAML snippet above for an example of this.
  3. If you have a long list of controls in a StackPanel inside a ScrollViewer and there's a ListPicker near the bottom using Expanded mode, that expansion does not automatically scroll the screen to keep the ListPicker completely in view. On WPF, the fix would be a simple matter of calling FrameworkElement.BringIntoView. However, Silverlight doesn't have that API and there doesn't seem to be a good general purpose way for ListPicker to find the right parent to scroll. (Although walking up the visual tree to find the first ScrollViewer is probably right in most cases, it's not a sure thing; ListPicker errs on the side of caution and doesn't try to make guesses.) In practice, the underlying issue doesn't come up very often - when it has, my suggestion has been to use the ItemCountThreshold property to force the relevant ListPicker to use Full mode (which doesn't expand, so it doesn't alter the parent's layout, so it doesn't have this problem).

 

Summary

ListPicker is a relatively straightforward control that should be familiar to anyone who's used the standard ListBox. But while its API may be unremarkable, its user experience is all Windows Phone 7 goodness! :)

I hope you enjoy it!