Here's a hint: the operating system's culture settings for France (and many other countries) use a ',' for the decimal separator instead of '.' (as is used in the United States). For example, while Pi would be written as "3.14" with the US culture settings, it would be written as "3,14" with French culture settings. And while Html5Canvas doesn't accept any user input, it does parse some of the JavaScript input that makes up the script... Specifically, my sample application includes the following line:

context.fillStyle = "rgba(255, 127, 39, 0.8)";

The <canvas> specification allows for strings to be assigned to the fillStyle property, so it's necessary for Html5Canvas to parse that string. And it was the "0.8" alpha value that was the problem here. Attempting to parse that value with the user's culture settings will fail for a culture that uses ',' as the decimal separator. This is actually a common problem for text formats that are meant to be used in multiple cultures - and the typical solution is to require that the text always be written for some form of invariant culture (which typically adheres to the US's conventions). Therefore, the trivial fix was to parse that value according to the invariant culture (i.e., always use '.' as the decimal separator) instead of using the default parsing behavior which honors the user's settings.

Aside: The default behavior is nearly always what you want... except for very rarely when it's not... like this time! :)

Of course, if I'd reviewed the project's code analysis warnings a few days ago, I would have found this issue earlier. And while I usually do that for all my sample code, I skipped it this time because it was proof-of-concept code and because I knew the "Naming" analysis rules threw lots of warnings for the official <canvas> API (mainly because JavaScript tends to follow different conventions than .NET). So as punishment for my misdeed, I went ahead and fixed (or suppressed) all of the non-"Naming" code analysis warnings in the project!

To be clear, there is no functional change because of this - but now the code is just a little bit cleaner. :)

And while I was at it, I decided to post the sample application in runnable form just in case there were some people who wanted to watch the Hypotrochoid draw itself, but didn't want to compile the sample themselves.

There's been some buzz about the upcoming HTML 5 standard over the past few months. In particular, there are a couple of new features that people are looking forward to. One of them is the new <canvas> element which introduces a 2D drawing API offering a pretty rich set of functionality. If you've worked with HTML much, you can probably imagine some of the things that become possible with this. In fact, those are probably some of the same things that Flash and Silverlight are being used for today! So some people have gone as far as to suggest HTML 5 could eliminate the need for Flash and Silverlight...

I didn't know much about the <canvas> element and I wanted to understand the situation better, so I did some research. The specification for <canvas> is available from two sources: the W3C and the WHATWG. (The two groups are working together, so the spec is the same on both sites.) The API is comprised of something close to 100 interfaces, properties, and methods and offers an immediate mode programming model that's superficially similar to the Windows GDI API. At a very high level, the following concepts are defined (though Philip Taylor did some investigation a while back which suggested that no browser offered complete support):

Paths and shapes (move/line/curve/arc/clipping/etc.)

Strokes and fills (using solid colors/gradients/images/etc.)

Images

Context save/restore

Transformations (scale/rotate/translate/etc.)

Compositing (alpha/blending/etc.)

Text (font/alignment/measure/etc.)

Pixel-level manipulation

Shadows

There's a variety of stuff there - and as I read through the list, I was struck by how much of it is natively supported by Silverlight. Pretty much all of it, in fact! :) So I thought it might be a fun and interesting learning experience to try implementing the HTML 5 <canvas> specification in Silverlight! What better way to understand the capabilities of <canvas> than to implement them, right?

So I went off and started coding... I didn't set out to support everything and I didn't set out to write the most efficient code (in fact, some of what's there is decidedly inefficient!) - I just set out to implement enough of the specification to run some sample apps and see how hard it was.

And it turns out to have been pretty easy! Thanks to Silverlight's HTML Bridge, I had no trouble creating a Silverlight object that looks just like a <canvas> to JavaScript code running on a web page. So similar, in fact, that I'm able to run some pretty cool sample applications on my own <canvas> simply by tweaking the HTML to instantiate a Silverlight <canvas> instead of the browser's <canvas>. And as a nice side effect, Internet Explorer "magically" gains support for the <canvas> tag!

Aside: Yes, I know I'm not the first person to add <canvas> support to IE. :)

Samples

I started off easy by working my way through the Mozilla Developer Center's Canvas tutorial and implementing missing features until each of the samples loaded and rendered successfully. Here are three of my favorite samples as rendered by Html5Canvas, my custom <canvas> implementation:

Aside: It's important to note that I did NOT change the JavaScript of these (or any other) samples - I just tweaked the HTML host page to load my <canvas> implementation. My goal was API- and feature-level parity; a Silverlight implementation of <canvas> that was "plug-compatible" with the existing offerings.

But I still wanted a sample I could include with the source code download, so I also wrote a little app to exercise most of the APIs supported by Html5Canvas (thanks to Mozilla for the Hypotrochoid animation inspiration and Wikipedia for the equation). Here it is on IE:

And wouldn't it be nice to see how Html5Canvas compares to a "real" <canvas> implementation? Sure, here's my sample running on Firefox:

Details

So how does it actually work? Well, I'm obviously not modifying the browser itself, so redefining the actual <canvas> tag isn't an option. Instead, I've written a simple Html5Canvas.js file which gets referenced at the top of the HTML page:

<scripttype="text/javascript"src="Html5Canvas.js"></script>

Among other things, it defines the handy function InsertCanvasObject(id, width, height, action) function which can be used to insert a Silverlight <canvas> thusly:

That code inserts a Silverlight object running Html5Canvas.xap that looks just like the <canvas> tag would have. Yep, it's that easy!

And it brings up an important difference between Html5Canvas and a real <canvas>: Html5Canvas can be used only after the Silverlight object has loaded - whereas <canvas> is usable as soon as the body/window has loaded (which happens sooner). This distinction is important to keep in mind if you're converting an existing page, because it requires moving the initialization call from onload to the action parameter of InsertCanvasObject. Believe it or not, that's really the only big "gotcha"!

Aside: While I could think of a few ways to avoid exposing this difference to the developer, none of them were general enough to apply universally.

Other minor differences between Html5Canvas and <canvas> are that Silverlight doesn't natively support the relevant repeat modes used by createPattern to tile images (though I could implement them without much difficulty), Silverlight doesn't support the GIF image format for use with drawImage (also easily worked around), and the conventional technique of JavaScript feature-detection by writing if (canvas.toDataURL) { ... } doesn't work because Silverlight's HTML Bridge doesn't allow a method to be treated like a property (I could work around this, too, but the extra level of indirection was unnecessarily confusing for a sample app).

Finally, let me reiterate that I did not attempt to implement the complete <canvas> specification. Instead, I implemented just enough to support the first 5 (of 6 total) Mozilla sample pages as well as the handful of applications shown above. Specifically, I've implemented everything that's not in italics in the feature list at the beginning of this post. Thinking about what it would take to add the stuff that's not implemented: text and pixel-level manipulation are both directly supported by Silverlight and should be pretty easy. Shadows seem like a natural fit for Silverlight's pixel shader support (though I haven't played around with it yet). All that's left is layer compositing, which does worry me just a little... I haven't thought about it much, but this seems like another job for WriteableBitmap, perhaps.

Be sure to set the Html5Canvas.Web project as the active project and run the TestPage.html within it to see the sample application.

Summary

Html5Canvas was a fun project that definitely accomplished its goal of bringing me up to speed on HTML 5's <canvas> element! The implementation proved to be fairly straightforward, though there were a couple of challenging bits that ended up being good learning opportunities. :) The code I'm sharing here is intended to be a proof-of-concept and is not optimized for performance. However, if there's interest in making broader use of Html5Canvas, I have some ideas that should really improve things...

I hope folks find this all as interesting as I did - and maybe next time you want to add browser features, you'll use Silverlight, too! ;)

I've updated sample application I wrote for the previous post to include a new "Add Detail" button that dynamically adds an item to the current collection. As you can see from the image below, the second ListView (which doesn't take advantage of the new code) hasn't expanded the widths of its two columns in response to the new item. However, the bottom ListView which uses the IsAutoUpdatingColumnWidths attached DependencyProperty has automatically detected the new item and updated itself accordingly:

The new code seemed like it should be easy enough, then turned out to be a bit more involved than I expected. But the important point is that if you're already using IsAutoUpdatingColumnWidths, then you don't need to change your code at all! Just drop in the new implementation of my ListViewBehaviors class, and you're set!

Notes:

At the end of the day, this update is really just about detecting that the ItemsSource collection implements INotifyCollectionChanged and listening to its CollectionChanged event. The complications arise because IsAutoUpdatingColumnWidths is an attached DependencyProperty and therefore static - so there's no convenient place to associate instance-specific state. In particular, the necessary state is a list of collection-owning ListViews for reverse-mapping and a reference to the previous collection instance because the handler that DependencyPropertyDescriptor.AddValueChanged calls when a value changes only gets the new value (unlike the PropertyChangedCallback that's typically associated with DependencyPropertys).

So I introduced a simple ListViewState helper class and some code to maintain a list of known ListView instances an their associated INotifyCollectionChanged collections.

But we don't want to create "false" references to user objects and introduce leaks, so both of ListViewState's properties are backed by a WeakReference to allow them to be garbage collected when possible.

At this point, you might wonder if it's necessary to employ the WeakEvent pattern (which I've previously written about here)... Well, for now I've managed to convince myself that it is not - because unlike the scenario I originally wrote about, in this case the "backwards references" are all to a single static method. The way I'm thinking about it right now, that method can't really "leak" - or if it can, its size and scope are small enough that they're not worth worrying about. That said, I'm open to being proven wrong, so please don't be shy about correcting me if you know better! :)

The only other detail worth calling out is that it's necessary to hook and unhook the INotifyCollectionChanged event in two circumstances: when the ItemsSource property changes and when the IsAutoUpdatingColumnWidths property changes.

And that's all there is to it! Here's the complete, updated implementation of ListViewBehaviors:

Recently, I got email from two different people with the same question: How do I animate the HorizontalOffset/VerticalOffset properties of a Silverlight/WPF ScrollViewer control? Just in case you've never tried this yourself, I'll tell you that it's not quite as easy as you'd think; those two properties are both read-only and therefore can't be animated by a DoubleAnimation. The official way to accomplish this task is to call the ScrollToHorizontalOffset or ScrollToVerticalOffset method - but of course that can't be done by a DoubleAnimation either...

Aside: Why are HorizontalOffset and VerticalOffset read-only in the first place? Good question; I don't know! I don't see why they couldn't be writable in today's world, but I bet there was a good reason - once upon a time. :)

While I didn't have the time to implement a solution myself, I did have an idea for what to do. It was inspired by a rather quirky business plan and went like this:

Create an attached DP for ScrollViewer of type double called SettableHorizontalOffset

In its change handler, call scrollViewerInstance.ScrollToHorizontalOffset(newValue)

Animate the attached DP on the ScrollViewer of your choice

???

Profit

I cautioned at the time that my idea might not work - and sure enough, when I tried it myself, it didn't! It turns out that neither Silverlight nor WPF much like the idea of pointing Storyboard.TargetProperty at an attached DependencyProperty. So much for my clever idea; I needed a different way to solve the problem... And before long I realized that I could use a Mediator just like I did for a similar situation with LayoutTransformer (see previousposts).

So I created a simple class, ScrollViewerOffsetMediator, which exposes a ScrollViewer property that you point at a ScrollViewer instance (hint: use a Binding with ElementName to make this easy) and a VerticalOffset property which is conveniently writable. :) Any changes to the VerticalOffset property are automatically applied to the relevant ScrollViewer via a call to its ScrollToVerticalOffset method - so you can animate the mediator and it'll be just like you were animating the ScrollViewer itself!

That solves the original problem and it's all well and good - but it doesn't quite address a pretty common scenario: wanting to animate a ScrollViewer from top to bottom. You see, VerticalOffset is expressed in pixels and it's not easy to pass the (unknown) height of an arbitrary ScrollViewer to an animation in static XAML. So I added another property called ScrollableHeightMultiplier which takes a value between 0.0 and 1.0, multiplies it by the ScrollViewer's current ScrollableHeight and sets the VerticalOffset to that value. In this manner, animations can be written in general, pixel-independent terms, and it's easy to get the animated scrolling effect everybody wants!

Aside: I've implemented support for only the vertical direction - doing the same thing for the horizontal direction is left as an exercise to the reader.

The sample application uses ScrollableHeightMultiplier and an EasingFunction (just to show off!) and looks like this: