The blog of dlaa.me

Breaking up (lines) is (not) hard to do [Tip: Put XAML attributes and elements on multiple lines so the markup is easy to read and work with]

Tip

Put XAML attributes and elements on multiple lines so the markup is easy to read and work with

Explanation

The last few tips have dealt with DependencyProperty issues. Now it's time for something completely different: XAML. While it's nice to pretend XAML editing can all be done in a design tool like Visual Studio or Expression Blend, I've always felt that XAML should be pleasant for people to work with, too. Therefore, it's worthwhile to establish a common approach to XAML formatting. What I find works well is to put attributes and elements on separate lines with a single indent for continuations and nesting. (Aside: I make an exception for elements with a single attribute to keep simple things compact.) I also tend to order properties according to importance, starting with the most significant ones and ending with the least. I consider the x:Name of an element to be the most important, so it typically comes first. Attached properties also rank high in my book and are usually next. After that, I tend to list common properties (ex: ContentControl.Content and TextBlock.Text) first and formatting properties (FrameworkElement.HorizontalAlignment and .Margin) closer to the end. In addition to keeping lines short and easy to read, this approach is very revision control-friendly - by which I mean that edits, adds, and removes all show up unambiguously. Sometimes people forget that markup is code, too - please treat it with the same respect and discipline. :)

Good Example

<Button
    x:Name="TheButton"
    Grid.Row="0"
    Content="Click me"
    Click="TheButton_Click"
    HorizontalAlignment="Center"
    Margin="10">
    <Button.Background>
        <SolidColorBrush Color="Pink"/>
    </Button.Background>
</Button>

More information

Revisiting the Code Not Taken [Updated analysis of two ways to create a full-size Popup in Silverlight]

Earlier this year I wrote about two approaches for creating a Popup to overlay the entire Silverlight plug-in. I began by showing the technique most people start with and then demonstrated a rather surprising side-effect of that approach. Here's the relevant screen shot:

A quirk of handling the Resized event on Silverlight 3

Unless you've read the original post, the problem may not be obvious - here's what I said at the time:

But something is still wrong in the image above... If you look carefully, you can see that the browser zoom is set at 50% - yet somehow the image is sized correctly despite us not doing any work to handle the browser's zoom setting yet. How can that be? Hold on, Sherlock, there's another clue in the image: look at the size of the buttons. Yeah, those buttons are not the size they should be for the 50% zoom setting that's active (refer back to the previous image if you don't believe me). Those buttons are at the 100% size - wha??

Aside: Hey, don't feel bad, it weirded me out, too. :)

It turns out that when you attach an event handler to the Resized event, Silverlight 3 disables its support for browser zoom. The reason being that Silverlight 3 assumes the application has chosen to handle that event because it wants full control over the zoom experience (via ZoomFactor and Zoomed, perhaps). Now that's really kind of thoughtful of it and everything - but in this case it's not what we want. In fact, that behavior introduces a somewhat jarring experience because the graphics visibly snap between 50% and 100% as the Resized event handler is attached and detached.

 

Well, that was then and this is now: I'm pleased to report that Silverlight 4 does not have the troublesome "hooking Resized disables browser zoom" behavior! Consequently, projects targeting Silverlight 4 are free to hook the Resized event without worrying that their browser zoom behavior will be compromised. Yay!

Aside: Of course, projects targeting Silverlight 3 (or previously compiled for it) and running on Silverlight 4 will continue to see the same Silverlight 3 behavior. This is because Silverlight 4 maintains backward compatibility with previous versions to avoid "breaking the web" when a new version comes out. Specifically, applications written for version N of Silverlight are expected to run the same on version N+M - even when there have been changes to the relevant functionality after version N was released.

 

Now that it's possible, I've updated the sample to demonstrate the desired behavior on Silverlight 4 by making the following tweak to the Application.Current.Host.Content portions:

EventHandler rootResized = delegate
{
    child.Width = root.ActualWidth / root.ZoomFactor;
    child.Height = root.ActualHeight / root.ZoomFactor;
};

Everything else remains the same, and now the Application.Current.Host.Content approach works almost as well as the Application.Current.RootVisual approach I recommended previously (though the image still flickers when changing the browser zoom):

Correctly sizing the Popup to cover the plug-in

 

With both approaches nearly equivalent on Silverlight 4, you might wonder if I'd like to revise my earlier recommendation to prefer the RootVisual-based approach... Well, I would not! I still feel the RootVisual approach is simpler and easier to understand, so I'll continue to use it myself and recommend it to others. However, there's no longer a compelling reason not to use the Content-based approach, so I'm cool if that's your personal preference. :)

 

[Click here to download the complete source code for the original sample (a Visual Studio 2010 (Beta 2) project targeting Silverlight 3)]

[Click here to download the complete source code for the new sample (a Visual Studio 2010 project targeting Silverlight 4)]

SplitButtoning hairs [Two fixes for my Silverlight SplitButton/MenuButton implementation - and true WPF support]

One of my ContextMenu test cases for the April '10 release of the Silverlight Toolkit (click here for the full write-up) was to implement a quick "split button" control for Silverlight using Button and ContextMenu. In that post, I cautioned that my goal at the time was to do some scenario testing, not to build the best SplitButton control ever. However, what I came up with seemed to work pretty well in practice and I figured folks could probably use the code mostly as-is.

And it seems like they did - because I got two bug reports in that post's comments section! :)

SplitButton and MenuButton

 

Let me address them in reverse order:

The position of the ContextMenu was wrong when the browser is zoomed: True enough - though I'm going to ask for a bit of leniency here because the cause of the misalignment is actually a bug in Silverlight (which I've already reported). It seems the results of a call to element.TransformToVisual(Application.Current.RootVisual) or element.TransformToVisual(null) are not consistent for elements that are vs. are not inside a Popup control when the browser is zoomed (in or out). As a result, the SplitButton code to position the menu got inconsistent data and was unable to place the menu correctly. I've tweaked the code slightly to accommodate the underlying issue and now the menu is properly aligned at any zoom setting.

While I was at it, I figured it might be nice if the menu moved around with the SplitButton as the user resized the browser or changed the zoom while the menu was displayed. This is admittedly an edge case, but it's easy enough to handle by hooking the LayoutUpdated event while the menu is displayed (and only while it's displayed!), so I did that and now the menu sticks to the button and refuses to be shaken off. :)

The code didn't work on WPF: I had a footnote claiming my Silverlight implementation should work on WPF as well, though I hadn't tried it myself. It turns out that statement is mostly true - except for the positioning logic which bumps into an subtle API incompatibility with the TransformToVisual method. (Noticing a pattern here?) In the process of getting things working for WPF, I realized the code to position the menu could be simplified somewhat with the (WPF-only) TranslatePoint method. Therefore, the actual positioning logic is a tad different across the two platforms ("a tad" == 4 lines of code) while everything else stays the same. This time when I claim SplitButton and MenuButton work on WPF, it's because I've tried it. :)

In fact, I've added a new, WPF-specific assembly (SplitButtonWpf) and demo (SplitButtonWpfSample) to the sample code associated with this post. Which means there is a dedicated assembly containing SplitButton and MenuButton for both platforms as well as a separate sample application for each!

 

[Click here to download the complete Silverlight/WPF source code for SplitButton/MenuButton and the sample application shown above.]

 

With those changes in place (and an unrelated key handling tweak for WPF), I feel even better about the prospects of using SplitButton and MenuButton in a real application. Naturally, if something else comes up, please let me know. Otherwise, I hope you find it useful!

The source code IS the executable, RTM edition [Updated CSI, a C# interpreter (with source and tests) for .NET 4 RTM]

CSI is a simple C# interpreter and has been available for .NET 1.1, 2.0, 3.0, and 3.5 for a while now. Earlier this year, I updated CSI for .NET 4 Beta 2, and now I've (somewhat belatedly) updated it for the final, public .NET 4 Framework. Today's post is mainly about getting an official .NET 4 RTM-compiled build of CSI released, so there aren't any functional changes to the tool itself.

FYI: I have a TODO list and there are some interesting things on it - it's just that none of them seemed particularly urgent.

The links above explain what CSI is and how it works; the executive summary is that CSI offers an alternative to typical CMD-based batch files by enabling the use of the full .NET framework and stand-alone C# source code files for automating simple, repetitive tasks. It accomplishes that by compiling source code "on the fly" and executing the resulting assembly behind the scenes. The benefit is that it's easy to represent tasks with a simple, self-documenting code file that leaves no need to worry about compiling a binary, trying to keep it in sync with changes to the code, or tracking project files and remembering how to build everything.

 

[Click here to download CSI for .NET 4.0, 3.5, 3.0, 2.0, and 1.1 - along with the complete source code and test suite.]

 

Notes:

  • The copy of CSI.exe in the root of the download ZIP is now the .NET 4 version because that's the latest public .NET Framework. Previous versions of CSI can be found in the Previous Versions folder: CSI11.exe, CSI20.exe, CSI30.exe, and CSI35.exe.
  • As with the previous release, I have not re-compiled the .NET 1.1 version, CSI11.exe - largely because I don't have .NET 1.1 installed anywhere. :)

 

Here's the "read me" file for a slightly better idea of how CSI works:

====================================================
==  CSI: C# Interpreter                           ==
==  David Anson (http://blogs.msdn.com/b/delay/)  ==
====================================================


Summary
=======
CSI: C# Interpreter
     Version 2010-06-07 for .NET 4.0
     http://blogs.msdn.com/b/delay/

Enables the use of C# as a scripting language by executing source code files
directly. The source code IS the executable, so it is easy to make changes and
there is no need to maintain a separate EXE file.

CSI (CodeFile)+ (-d DEFINE)* (-r Reference)* (-R)? (-q)? (-c)? (-a Arguments)?
   (CodeFile)+      One or more C# source code files to execute (*.cs)
   (-d DEFINE)*     Zero or more symbols to #define
   (-r Reference)*  Zero or more assembly files to reference (*.dll)
   (-R)?            Optional 'references' switch to include common references
   (-q)?            Optional 'quiet' switch to suppress unnecessary output
   (-c)?            Optional 'colorless' switch to suppress output coloring
   (-a Arguments)?  Zero or more optional arguments for the executing program

The list of common references included by the -R switch is:
   System.dll
   System.Data.dll
   System.Drawing.dll
   System.Windows.Forms.dll
   System.Xml.dll
   PresentationCore.dll
   PresentationFramework.dll
   WindowsBase.dll
   System.Core.dll
   System.Xml.Linq.dll
   Microsoft.CSharp.dll
   System.Xaml.dll

CSI's return code is 2147483647 if it failed to execute the program or 0 (or
whatever value the executed program returned) if it executed successfully.

Examples:
   CSI Example.cs
   CSI Example.cs -r System.Xml.dll -a ArgA ArgB -Switch
   CSI ExampleA.cs ExampleB.cs -d DEBUG -d TESTING -R


Notes
=====
CSI was inspired by net2bat, an internal .NET 1.1 tool whose author had left
Microsoft. CSI initially added support for .NET 2.0 and has now been extended
to support .NET 3.0, 3.5, and 4.0. Separate executables are provided to
accommodate environments where the latest version of .NET is not available.


Version History
===============

Version 2010-06-07
Update .NET 4 (RTM) version
Make .NET 4 version primary

Version 2010-01-04
Add .NET 4 (Beta 2) version
Minor updates

Version 2009-01-06
Initial public release

Version 2005-12-15
Initial internal release

Please rate your dining experience [How to: Show text labels on a numeric axis with Silverlight/WPF Toolkit Charting]

A customer contacted me a few days ago asking how to display text labels on a NumericAxis. (Whereas CategoryAxis makes it easy to use text labels for the independent axis, this request was about text labels on the dependent axis.) It's a bit of an unusual request (I spent a minute just now and don't see how to accomplish this in Excel), but I knew it would be easy to do with the Charting controls in the Data Visualization assembly that's part of the Silverlight Toolkit and WPF Toolkit.

The underlying scenario is to provide labels for the results of one of those "How are we doing?" surveys restaurants and hotels like to give out. The chart should look like this:

Text labels on the dependent axis

Though I originally suggested a different approach, the act of coding it up myself suggested a trusty old IValueConverter would be most appropriate. (Aside: See more IValueConverter tricks here and here.) The basic approach is to explicitly specify the dependent axis and then use it to configure exactly the set of tick marks we want. Once that's done, a simple bit of IValueConverter magic converts the numeric values into their corresponding text labels - and the problem is solved! :)

 

I've added the sample shown here to my DataVisualizationDemos application which is collection of all the Data Visualization samples I've blogged. Like the core Data Visualization code itself, the demo app compiles for and runs on multiple platforms with the same code and XAML - it's an easy way to publish a sample and show it running on Silverlight 3, Silverlight 4, WPF 3.5, and WPF 4. Just for kicks, I've used the "Compatible" ColumnSeries for this example - but it works just as well with traditional ColumnSeries. Better yet, the basic idea can be generalized to solve a variety of similar problems as well!

 

[Click here to download the complete source code for the cross-platform DataVisualizationDemos sample application. ]

 

Here's the relevant XAML:

<!-- Chart of customer feedback -->
<charting:Chart Title="Customer Feedback">
    <compatible:ColumnSeries
        ItemsSource="{Binding}"
        IndependentValuePath="Topic"
        DependentValuePath="Rating">

        <!-- Custom Y axis for text labels -->
        <compatible:ColumnSeries.DependentRangeAxis>
            <charting:LinearAxis
                Orientation="Y"
                Minimum="0"
                Maximum="4"
                Interval="1"
                ShowGridLines="True">

                <!-- Custom style/template for text labels -->
                <charting:LinearAxis.AxisLabelStyle>
                    <Style TargetType="charting:AxisLabel">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="charting:AxisLabel">
                                    <TextBlock Text="{Binding Converter={StaticResource RatingToStringConverter}}"/>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </charting:LinearAxis.AxisLabelStyle>
            </charting:LinearAxis>
        </compatible:ColumnSeries.DependentRangeAxis>

        <!-- Custom style for different background -->
        <compatible:ColumnSeries.DataPointStyle>
            <Style TargetType="charting:DataPoint">
                <Setter Property="Background" Value="#ff00a0e0"/>
            </Style>
        </compatible:ColumnSeries.DataPointStyle>
    </compatible:ColumnSeries>
</charting:Chart>

And code:

/// <summary>
/// Implements IValueConverter to convert from double rating values to friendly string names.
/// </summary>
public class RatingToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // Validate parameters
        if (!(value is double))
        {
            throw new NotSupportedException("Unsupported value type in RatingToStringConverter.");
        }
        // Convert number to string
        double doubleValue = Math.Floor((double)value);
        if (0.0 == doubleValue)
        {
            return "Awful";
        }
        else if (1.0 == doubleValue)
        {
            return "Poor";
        }
        else if (2.0 == doubleValue)
        {
            return "Fair";
        }
        else if (3.0 == doubleValue)
        {
            return "Good";
        }
        else if (4.0 == doubleValue)
        {
            return "Great";
        }
        else
        {
            throw new ArgumentException("Unsupported value in RatingToStringConverter.");
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

You Spin Me Round (Like a Record) [Easily animate orientation changes for any Windows Phone application with this handy source code]

As computing devices have become more powerful, the trend has been toward more "fluid" user interfaces - interaction models that flow smoothly from one state to another. This differs from earlier approaches where resources were more limited and interactions tended to be Spartan and isolated from each another. Some of the motivation behind an increased focus on fluid UI is almost certainly the "wow" factor; modern interfaces can be quite attractive and fun to use! But there's also scientific research to back this up: people tend to find smooth transitions less disruptive to their workflow than abrupt ones - and the animation itself can help tie the "before" and "after" states together by showing how one becomes the other.

With that in mind, one of the things I've wanted to do is apply this principle to enhance the user experience for Silverlight applications on the Windows Phone platform. Specifically, I wanted to make it easy for developers to animate the orientation change of an application that occurs when the phone is rotated from its default portrait orientation to landscape. This is one of those cases where a picture is worth a thousand words, so I've created a short video showing the default behavior of a Silverlight-based Windows Phone application when the device (in this case the emulator) is rotated.

 

Click the image below to view a brief H.264-encoded MP4 video of the default rotation behavior:

Video of default orientation change behavior

For people who have never used the emulator: those two buttons I click rotate the device one quarter turn clockwise or counter-clockwise. I begin by rotating counter-clockwise once, then clockwise once back to the starting orientation, then clockwise three more times to loop all the way around.

 

What I show above is what you get for free when you create a new application - and it's great the device and platform support dynamic orientation changes without any special effort! But I thought it would be cool if I could extend that just a bit in order to animate those orientation changes - again, without requiring the developer to change anything (beyond a couple of superficial name changes).

Click the image below to view a short video of the animated behavior I've created:

Video of animated orientation change behavior

As in the first video, I show the device rotating to landscape, then back, then all the way around. But this time I've included a little bit of fun at the end. :)

 

The custom rotation behavior is made possible by the AnimateOrientationChangesPage class I created which works by lying to the layout system (Now, where have I heard that before?). It's a small, self-contained class you insert into the default hierarchy and then never need to worry about again. AnimateOrientationChangesPage automatically gets involved with the necessary steps for rotation and even handles page-to-page navigation changes seamlessly.

My goal was to make it easy for Windows Phone applications to offer the kind of fluid rotation experience that's becoming common these days - and it seems like AnimateOrientationChangesPage does that. I hope you find it useful!

 

[Click here to download the AnimateOrientationChanges sample for the Windows Phone 7 platform.]

 

Notes:

  • If you download the sample, please be sure to open MainPage.xaml in the development environment before hitting F5 to run it - otherwise deployment to the emulator might fail with the message "Object reference not set to an instance of an object." because of a bug in the tools.
  • I created this sample using the public Windows Phone Developer Tools CTP - April Refresh bits, so anyone else with that toolset installed should be able to run it as-is. I also happened to have a chance to try things out on a more recent (internal) build of the tools - fortunately, things worked just as well there! However, due to a minor change to one of the platform APIs, please remove the following from the top of AnimateOrientationChangesPage.cs if you're trying to run on a more recent build:
    // Remove this #define when using more recent phone/tools builds
    #define APRIL_TOOLS
  • Unfortunately, I don't have actual phone hardware to run this code on, so I can't be sure it runs as smoothly there. While the performance under the Windows Phone emulator is great on my laptop, even a laptop has beefier hardware than something pocket-sized like a phone. That said, the implementation is relatively straightforward: the orientation change notification starts a Storyboard with a single DoubleAnimation of a custom DependencyProperty. The change handler for this property updates the values of a RotateTransform and TranslateTransform and triggers an arrange pass (via InvalidateArrange). Everything so far is part of the Silverlight Performance team's recommendations for good performance, so the bottleneck (if there is one) might turn out to be the arrange pass (note: not measure; just arrange) which is always a recursive operation. However, arrange is a fundamental operation for the Silverlight platform, so I expect it to be optimized and pretty efficient in practice. That said, if I manage to get my hands on a device for long enough to play around, I'll see how things really perform...
  • I wanted AnimateOrientationChangesPage to be as easy to use as possible. And while I couldn't go as far as enabling it with an attached property, I was able to do the next best thing: derive from PhoneApplicationPage. This means any existing Windows Phone application can be converted to use AnimateOrientationChangesPage simply by changing the base class of its pages to AnimateOrientationChangesPage. Specifically, here are the complete steps for converting the MainPage class of a newly created application:
    1. Verify the page supports multiple orientations by checking for the following code in the constructor (it's added by default, so it ought to be there):
      SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
    2. Add the AnimateOrientationChangesPage.cs code file to the project/solution.
    3. Change the base class of MainPage to AnimateOrientationChangesPage in MainPage.cs:
      using System;
      // ...
      using Delay;
      
      namespace WindowsPhoneApplication1
      {
          public partial class MainPage : AnimateOrientationChangesPage
          {
              // ...
          }
      }
    4. Make the corresponding change to MainPage.cs.xaml:
      <delay:AnimateOrientationChangesPage
          x:Class="WindowsPhoneApplication1.MainPage"
          xmlns:delay="clr-namespace:Delay"
          ...>
      
          <!-- ... -->
      
      </delay:AnimateOrientationChangesPage>
    5. Run the application and enjoy the animations!
  • These directions enable the default animation behavior which I've configured to use a 0.4 second duration and a reasonable easing function. However, if you'd like to customize things further, you can set the Duration property to a different TimeSpan and/or change the EasingFunction to the IEasingFunction implementation of your choice. Just in case, there's also an IsAnimationEnabled property that can be set to false to disable orientation change animations entirely. These properties can be set at any time; the new values take effect at the next orientation change.
  • I reserve the right to change my mind and move some of this code up into a PhoneApplicationFrame subclass if it turns out that would be helpful. :) But for right now, the current implementation seems pretty effective - I've specifically tested it with navigation scenarios and it works well there, too.
  • If you watch the video of the default behavior closely, you'll see a brief period of time right after the rotation occurs where the emulator is rotated but the screen isn't. I think this is an artifact of how the emulator works and doesn't represent something users would see on a real device. As it happens, I don't see that same glitch in the video with AnimateOrientationChangesPage enabled - but I'd apply the same caveat there, too.
  • I've deliberately not supported the PageOrientation.PortraitDown enumeration value. This is not because it would be hard to do (it's quite trivial, actually), but rather because no device I've seen (including the Windows Phone Emulator) actually supports flipping completely upside down in portrait orientation. If this design decision is a problem for your scenario, full PortraitDown support requires only three trivial case statements to implement that will be obvious from looking at the code.

Back online! [MSDN blogging platform upgraded, comments re-enabled, new content on the way...]

My last post talked about the upcoming MSDN blogging platform upgrade. That upgrade took place during last week and new posts/comments for this blog were consequently disabled.

Today, I'm happy to report the upgrade was successfully completed and this blog is back online! I have some new articles in the queue and look forward to posting them soon...

Thank you for your patience - I hope you enjoy the new blogging platform!

Going dark [MSDN blogging platform being upgraded - NO new posts or comments next week]

The administrators of the MSDN blogging platform are performing a software upgrade and will be putting all blogs into read-only mode on Sunday, May 16th. New posts and new comments will not be possible for this blog during the transition. The upgrade is expected to finish by Monday, May 24th - but difficulties during the migration could push that date back. I'll post a quick note once the dust has settled and things are back to normal.

In the meantime, you should be able to contact me using the old platform's email form. Alternatively, I can be reached on Twitter as @DavidAns.

Thank you for your patience - see you on the other side! :)

Gettin' blobby with it [Sharing the code for a simple Silverlight 4 REST-based cloud-oriented file management app for Azure and S3]

I've described the "app building" process before in this post about my HeadTraxExtreme sample for the Silverlight 3 Beta and a later in this update of HeadTraxExtreme for the Silverlight 3 RTW. The gist is that:

... everyone comes up with an idea for a medium-sized application they think could be built with the bits at hand - then goes off and tries to build as much of that application as they can before time runs out. The emphasis is on testing new scenarios, coming up with creative ways of integrating components, and basically just getting the same kind of experience with the framework that customers have every day. Coming up with a beautifully architected solution is nice if it happens, but not specifically a goal. Rather, the point is to help people take a holistic look at how everything works together...

App building is a great way to get exposure to parts of the product you don't normally deal with and can help give team members a more complete view of the platform they work on. I had another app building opportunity recently and my goal was to create a utility application I've been wanting for a while.

Here's where I got in the bit of time I had:

 

BlobStore with silly sample data

Disclaimer: In case it's not completely obvious, none of the file names above is accurate. :)

 

The Sales Pitch

Did you ever want an easy way to transfer files between two machines on semi-isolated networks (ex: home and work)? Looking for an easier way to publish content to the web? Tired of sending yourself files as email attachments all the time? Sneaker-net got you down? Well, your dreams have just come true!

BlobStore is the hot new craze that's taking the world by storm. It's a small, lightweight Silverlight 4 application that acts as a basic front-end for the Windows Azure Simple Data Storage and the Amazon Simple Storage Service (S3)! Just run the BlobStore app, point it at a properly provisioned Azure or S3 account (see below for details on provisioning), and all of a sudden sharing files with yourself and others becomes as simple as dragging and dropping them! BlobStore makes it easy to manage your files by providing a feature-rich (okay, that's an exaggeration) interface that allows you to view, download, or delete files and copy their URL to the clipboard easy-peasy. Logon credentials are stored on your machine so there's no need to remember long account passwords - once you've connected once, future connections are simple and painless.

But wait, there's more!

If you download right now, you'll also get the complete source code from which you can explore all the inner workings of this life-changing application. Included with every code download is a free REST wrapper for basic Azure and S3 blob access that handles all the tricky Authorization header details for you. It's almost guaranteed to make your next Azure/S3 application a snap to develop and a wild success in the marketplace.

So now how much would you pay? :)

 

BlobStore login screen

 

Featured Silverlight 4 Features

  • Asynchronous networking
    • Windows Azure REST API
    • Amazon S3 REST API
    • Authorization HTTP header
    • Custom HTTP verbs
    • Client HTTP stack
    • Real-time progress indication
  • View/view model separation
    • INotifyPropertyChanged interface
    • ICommand interface
    • Simple DelegateCommand implementation
    • Extensive data binding
  • Design-time experience
    • Design-time data
    • Design tool-friendly experience
  • Isolated Storage
    • For user preferences
    • For files
  • Controls
    • Custom DependencyPropertys
    • Visual State Manager
    • Custom control Templates
    • Custom UserControls
  • Clipboard access
  • Right click support
  • Custom unhandled exception handler

Potential Enhancements (TODOs)

  • Smooth various rough edges
  • Support running out-of-browser
  • Ability to automatically provision new Azure/S3 accounts
  • Better handling of web service failures
  • Encrypt logon information in isolated storage
  • Additional metadata for each blob (file size, MD5 hash, etc.)
  • Support Silverlight-based file downloads (i.e., SaveFileDialog)
  • Support marking blobs private (i.e., not world-readable)

Potential v.Next Enhancements (features not supported by Silverlight 4)

  • Set/get clipboard access for data (ex: images, files)
  • Support for downloading by dragging files out of the plug-in

Notes

  • In addition to supporting Azure and S3, BlobStore allows you to use Silverlight's isolated storage for the blob service. This works fine for playing around and general experimentation, but it's important to note that the web browser isn't able to download from isolated storage, so files "uploaded" to isolated storage can't be "downloaded" with the browser.
  • Access to an isolated storage "account" requires a non-empty account name and key - any name and key will work.
  • Isolated storage support is present only for testing purposes (and so people without a provisioned account can play around); I haven't verified that BlobStore works well if access to isolated storage has been disabled or runs out of space.
  • Developer documentation for the two online services can be found at the following locations: Windows Azure, Amazon S3
  • Despite my Silverlight Toolkit pedigree, BlobStore uses no Toolkit or SDK controls - it's 100% core framework-y goodness!

 

[Click here to run the BlobStore application in your browser.]

[Click here to download the complete source code for the Silverlight 4 BlobStore sample.]

 

BlobStore upload progress screen

 

Provisioning

Before BlobStore can access an Azure/S3 account, that account needs to be provisioned to allow access by a Silverlight browser-based application and permit unauthenticated third parties to download content. Steve Marx has a great overview of provisioning an Azure account - the basic steps are to create the special $root container, make it world-readable, and upload a clientaccesspolicy.xml file that lets Silverlight know the cross-domain access is okay. For an S3 account, the concept is the same, but the implementation is a bit simpler - just upload a clientaccesspolicy.xml file and make it world-readable.

These one-time provisioning steps can be done manually, in code, or with one of the public tools for Azure or S3. The clientaccesspolicy.xml file I use is taken from Steve's example with a tweak to make support for http and https more explicit:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-methods="*" http-request-headers="*">
        <domain uri="http://*" />
        <domain uri="https://*" />
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 

Summary

BlobStore was a fun project to work on and it gave me some nice exposure to Silverlight's networking stack (which I don't normally get a chance to use). There are some rough edges I didn't get around to smoothing over, but on the whole I think BlobStore turned out pretty well: it's small and quick to download, easy to use, it simplifies something I do every day, and it was a great learning experience!

Aside: You can even use BlobStore to publish Silverlight applications (just upload the XAP and HTML file), so it's got that recursive thing going for it, too...

I'd never claim BlobStore is the best example of application design, but I did try to follow "best practices" in most cases; there are some interesting techniques in there that some of you may not have seen in action. And if writing and sharing BlobStore helps others learn how to develop better Silverlight applications, we all win. :)

 

Enjoy!

We've secretly changed this control's DataContext - let's see if it notices! [Workaround for a Silverlight data binding bug affecting various scenarios - including DataGrid+ContextMenu]

I was contacted by Simon Weaver via Twitter about a problem where the Bindings for MenuItems of a ContextMenu on a DataGrid were acting as though they were associated with a different row than they really were. This seemed pretty weird until Yifung Lin, one of the original DataGrid developers, suggested this might be due to the row recycling behavior DataGrid uses to improve performance. It turns out he was right - but the rabbit hole goes much deeper than that...

Sample application showing bug

Working from an internal reproduction of the problem Brian Braeckel created, I found that I was able to simplify the scenario significantly - to the point of removing ContextMenu and DataGrid entirely! Signs were pointing strongly to this being a bug in the Silverlight framework, but before I started crying wolf, I wanted someone to review my work to be sure I hadn't over-simplified things. Fortunately, RJ Boeke was around and not only confirmed the validity of my repro, but also pointed out further simplifications!

 

When all is said and done, the bug is pretty easy to describe:

If a Binding points to an element via its ElementName or Source parameters and the DataContext of a parent of that element changes, that change will NOT be propagated through the Binding. However, if the DataContext of the element itself changes, the change will be propagated correctly.

RJ and I were both kind of surprised to find this bug in Silverlight 4, but when Sage LaTorra looked into this from the QA side, he reported the same problem with Silverlight 3. Which - in a weird kind of way - is actually good news because it means things haven't gotten worse with Silverlight 4 and existing applications won't break unexpectedly!

Unfortunately, the DataGrid+ContextMenu scenario relies on this exact behavior to work correctly... When a ContextMenu is about to be displayed, a Popup is created, the menu UI is added to it, and it's shown. Because the Popup isn't in the visual tree, the ContextMenu won't see the right DataContext by default - so it sets up a Binding with its Source set to the ContextMenu's owner element. This works well and the DataContext then "flows" into the Popup where MenuItems can see it and bind to it successfully. The problem in the DataGrid scenario arises when DataGrid decides to recycle one of its rows by swapping one DataContext for another. Though this is a perfectly legitimate thing to do, it runs afoul of this bug and breaks the scenario.

 

Fortunately, there's an easy workaround. Even better: it works for all known instances of the problem, not just the DataGrid+ContextMenu kind! Here's an example of code that demonstrates the bug when the DataContext of a parent element is changed (which you can do in the sample application by clicking on the red or green background):

<!-- Simple example of the broken scenario -->
<Grid x:Name="SimpleBroken" Grid.Column="0" Grid.RowSpan="2" Background="Red">
    <TextBlock Text="{Binding DataContext, ElementName=SimpleBroken}"/>
</Grid>

And here's what it looks like with the workaround applied:

<!-- Simple example of the workaround -->
<delay:DataContextPropagationGrid x:Name="SimpleWorking" Grid.Column="1" Grid.RowSpan="2" Background="Green">
    <TextBlock Text="{Binding DataContext, ElementName=SimpleWorking}"/>
</delay:DataContextPropagationGrid>

Pretty similar, huh? The way this works is that the DataContextPropagationGrid class derives from Grid, listens for DataContext changes in the usual manner, then uses the fact that changes to the local value of DataContext work to "re-broadcast" the change to any Bindings targeting it via ElementName or Source. The important thing to note is that any Binding broken because of the underlying platform bug needs to be pointed at the DataContextPropagationGrid wrapper instead - and should then behave correctly.

Aside: I haven't historically considered Grid for workaround scenarios like this. However, it seems like a good fit: Grid can be wrapped around nearly anything without side-effects, it allows multiple children for scenarios where that might be necessary, it's familiar to developers and designers, and it keeps the workaround code simple!

 

Just to "close the loop", here's an example of a DataGrid+ContextMenu demonstrating the problem:

DataGrid+ContextMenu showing bug

To reproduce it yourself, run the sample application, right-click on every row of the red DataGrid (everything will be correct), then scroll to the end of the list and do the same - you'll quickly find a mis-match like I show above. The fix is as easy as introducing the DataContextPropagationGrid class (as I did for the green DataGrid) - the important thing is to be sure to attach the ContextMenu to the DataContextPropagationGrid so the workaround has a chance to do its thing:

<!-- DataGrid+ContextMenu example of the workaround -->
<sdk:DataGrid Grid.Column="1" Grid.Row="1" AutoGenerateColumns="False" Margin="10">
    <sdk:DataGrid.Columns>
        <sdk:DataGridTemplateColumn Header="Values">
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <delay:DataContextPropagationGrid>
                        <toolkit:ContextMenuService.ContextMenu>
                            <toolkit:ContextMenu>
                                <toolkit:MenuItem Header="{Binding}"/>
                            </toolkit:ContextMenu>
                        </toolkit:ContextMenuService.ContextMenu>
                        <TextBlock Text="{Binding}"/>
                    </delay:DataContextPropagationGrid>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

 

If you run into this Silverlight bug in the DataGrid+ContextMenu scenario, please apply the DataContextPropagationGrid workaround and things should work properly. And if you happen run into the bug in some other scenario, the good news is that the DataContextPropagationGrid workaround should work there, too! Just be mindful to point the Binding's ElementName or Source at the DataContextPropagationGrid element, and you're good to go.

 

[Click here to download the complete source code for the SilverlightDataContextBugWorkaround sample.]

 

PS - Here's the code for the workaround:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Delay
{
    /// <summary>
    /// Class to help work around a Silverlight bug where DataContext changes to
    /// an element aren't propagated through Bindings on child elements that use
    /// ElementName or Source.
    /// </summary>
    public class DataContextPropagationGrid : Grid
    {
        /// <summary>
        /// Initializes a new instance of the DataContextPropagationGrid class.
        /// </summary>
        public DataContextPropagationGrid()
        {
            // Create a Binding to keep InheritedDataContextProperty correct
            SetBinding(InheritedDataContextProperty, new Binding());
        }

        /// <summary>
        /// Identifies the InheritedDataContext DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty InheritedDataContextProperty =
            DependencyProperty.Register(
                "InheritedDataContext",
                typeof(object),
                typeof(DataContextPropagationGrid),
                new PropertyMetadata(null, OnInheritedDataContextChanged));

        /// <summary>
        /// Handles changes to the InheritedDataContext DependencyProperty.
        /// </summary>
        /// <param name="d">Instance with property change.</param>
        /// <param name="e">Property change details.</param>
        private static void OnInheritedDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DataContextPropagationGrid workaround = (DataContextPropagationGrid)d;
            // Update local value of DataContext to prompt Silverlight to update problematic Bindings
            workaround.DataContext = e.NewValue;
            // Unset local value of DataContext so it will continue to inherit from the parent
            workaround.ClearValue(FrameworkElement.DataContextProperty);
        }
    }
}