Wintellect  

My latest Wicked Code column is now online at MSDN Magazine. Titled "Silverlight Page Turning Made Simple," it presents a Silverlight 1.0 framework for building slick page-turning applications. I mentioned the framework in an earlier blog post but didn't publish the source code because it needed to appear in MSDN Magazine first. Now you can download it, use it, and modify it to your heart's content.

And yes, I'll port the framework to Silverlight 2.0 soon.

I have largely avoided problems with Vista by only running it on PCs on which it comes preinstalled. At least I avoided problems until a few weeks ago. With a total of five PCs and laptops now running Vista in my house, the constant avalanche of automatic updates seems to break something every day. The latest issue I’m fighting is one that causes ASP.NET apps launched in IE 7 from Visual Studio 2008 to sometimes start with JavaScript disabled. It’s pretty hard to teach an ASP.NET class when LinkButtons don’t work.

But worse than having ASP.NET broken on my development box is what IE 7 has done to the PC in the kitchen. For more than 20 years my wife has used my hand-me-down PCs. Last week I bought her her first-ever new PC:  a Dell quad-core with 3 GB of RAM, a 500 MB hard disk, a 20-inch LCD monitor, and Vista Home Premium. (For the first time ever, I’m envious of her PC!) At first I was a little hot when Vista didn’t support my trusty USB wireless network adapter, but $70 and a new adapter later, Vista was happy and so was I.

The smiles didn’t last long. My wife fired up IE 7 on her new computer and went to our high school’s parent portal to check my son’s math grade. IE, however, wouldn’t let her get past the login page; it kept insisting “Internet Explorer cannot display the webpage.” She asked if this was another Vista problem. I knowingly assured her it wasn’t, and informed her that she had been running IE 7 on XP for a long time. Just to make sure, I downloaded the latest version of Firefox and went to the parent portal. Much to my surprise, Firefox let me log in and view grades just fine! I spent the next couple of hours toggling settings in IE 7 trying to get past the problem, and a couple of hours after that doing Web searches and analyzing HTTP traffic to find out why IE 7 couldn’t display a page that Firefox had no problem with. (It has something to do with “302 Temporarily Moved” responses coming back over SSL/TLS, but that’s as far as I’ve gotten with it.)

My wife can still check our son’s grades with IE 7 on her old XP PC. She can check grades with Firefox on her new Vista quad-core. But she can’t use IE 7 to check grades on Vista. She’s confused, and so am I.

A number of people have asked me in recent weeks whether two Silverlight 2 controls hosted in the same page can communicate with each other, and if so, how. I tell them they need to build a JavaScript bridge between the controls and then the controls can talk to each other just fine.

Tonight I built a sample app to demonstrate. Here's what it looks like:

Control Interop Demo 

You drag the red ball in the upper rectangle and the blue rectangle in the lower rectangle mirrors the red ball's moves. The interesting aspect of this application is that each rectangle represents a different Silverlight control instance. In other words, the page hosts two Silverlight controls, and the red ball lives in one control while the blue ball lives in the other.

Here are the OBJECT tags used to instantiate two Silverlight controls:

<div id="silverlightControlHost1">

  <object data="data:application/x-silverlight,"

    type="application/x-silverlight-2-b1" width="400"

    height="300" id="SourceControl">

    <param name="source" value="ClientBin/InteropDemo.xap"/>

    <param name="onerror" value="onSilverlightError" />

    <param name="background" value="white" />

  </object>

</div>

   

<div id="SilverlightControlHost2">

  <object data="data:application/x-silverlight,"

    type="application/x-silverlight-2-b1" width="400"

    height="300" id="TargetControl">

    <param name="source" value="ClientBin/OtherControl.xap"/>

    <param name="onerror" value="onSilverlightError" />

    <param name="background" value="white" />

  </object>

</div>

Each control has its own DIV, and each loads a different XAP file. How did I get Visual Studio to build two XAP files, each containing a separate control assembly? I just added a second Silverlight Application project to the solution. If you already have a Web project in the solution, Visual Studio asks if you want the new project linked to the existing Web project:

Add Project Dialog

Visual Studio obligingly adds the Silverlight project to the solution and configures the build settings so that building the project creates a second XAP file that, like the XAP file from the first project, is copied into the Web site's ClientBin directory.

To link the two Silverlight controls together, I began by converting the code-behind class for the second control (the one containing the blue ball) into a scriptable class and adding a scriptable method named MoveBall:

[ScriptableType]

public partial class Page : UserControl

{

    public Page()

    {

        InitializeComponent();

        HtmlPage.RegisterScriptableObject("other", this);

    }

 

    [ScriptableMember]

    public void MoveBall(double x, double y)

    {

        Ball.SetValue(Canvas.LeftProperty, x);

        Ball.SetValue(Canvas.TopProperty, y);

    }

}

Then I added the following JavaScript function (the "bridge") to the HTML page:

var _target = null;

   

function moveBall(x, y)

{

    if (_target == null)

        _target = document.getElementById('TargetControl');

    _target.content.other.MoveBall(x, y);           

}

Finally, I added the following statement to the MouseMove handler in the first control (the one with the red ball):

HtmlPage.Window.Invoke("moveBall", x, y);

The X and Y coordinates passed in represent the current position of the red ball--the position I just moved it to in response to the latest mouse move.

See how it works? You move the red ball. The MouseMove handler that moves the ball also calls the JavaScript moveBall function, which in turn calls the second control's MoveBall method. The red ball moves and the blue ball moves, too. This sample takes advantage of Silverlight 2's rich DOM integration features which allow C# methods to call JavaScript functions, JavaScript functions to call C# methods, and a whole lot more.

I'll post the source code this weekend. For now, it has been a long week, it's late, and I'll be locked away in a hotel all weekend building slides for my Silverlight precon at TechEd and taking care of other jobs that have piled up in my to-do list. Time to get some sleep.

UPDATE: The source code has been posted and you can download it here. In addition, I posted a new version of SilverLife that uses DispatcherTimer rather a Storyboard to drive cell animations. You can download the new version here.

I was having dinner with Walt Ritscher tonight when he posed an interesting question: how can a Silverlight app load its own XAML from an application assembly?

I thought I knew the answer, because I had just finished doing a lot of research into the various ways to package code and resources in Silverlight 2.0. And I knew that the XAML files in a Silverlight 2.0 app get embedded as resources in the application assembly, which in turn gets embedded in the application package (the application's XAP file). But when I got back to my hotel and tried it, it didn't work. So I played around some and found the secret incantation.

Here's the Page.xaml file in the test app that I wrote:

<UserControl x:Class="SilverlightTestApp.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">

        <TextBlock x:Name="Output" />

    </Grid>

</UserControl>

And here's the output from the program:

XAML Output 

Finally, here's the code in Page.xaml.cs that produces the output by loading Page.xaml from the application assembly and assigning it to a TextBlock object:

StreamResourceInfo sri = Application.GetResourceStream

    (new Uri("SilverlightTestApp;component/Page.xaml", UriKind.Relative));

StreamReader reader = new StreamReader(sri.Stream);

Output.Text = reader.ReadToEnd();

The trick is to include "assemblyname;component" in the URI passed to GetResourceStream, even though the assembly you're loading from is the application assembly and not a library assembly. Normally you don't have to include the assembly name if you're targeting the application assembly, but this is obviously an exception--at least in Beta 1.

I don't know how useful this information is, but it ought to make a good icebreaker at a Silverlight party.

Like a lot of folks, I was pretty impressed the first time I saw Silverlight 2.0's new Deep Zoom feature at work. So I downloaded Deep Zoom Composer and starting building apps to see what makes Deep Zoom tick. Deep Zoom Composer is cool, but what really makes Deep Zoom shine is the MultiScaleImage control. It is amazing how much you can do with so little code.

I built an app that incorporates some of my favorite travel photos:

Deep Zoom

You can zoom in and out with the mousewheel or pan around by dragging with the left mouse button down. Some of the photos I used are higher-res than others, but in most of them you can zoom in enough to reveal quite a lot of detail.

You can view the app online, and you can download the source code. The download doesn't include the images because the images comprise more than 30 MB. But you can generate your own images and metadata with Deep Zoom Composer and plug them into my demo. All you have to do is copy the output folder created by Composer into DeepZoom_Web's ClientBin folder and point the MultiScaleImage control declared in Page.xaml to the info.bin or items.bin file in the output folder. Then fire up the app and zoom away.

When I exported from Deep Zoom Composer, I checked the "Create Collection" box to export a collection of images rather than one monolithic image. Although my code currently doesn't take advantage of it, exporting a collection allows you to use the SubImages property of the MultiScaleImage control to addess the individual images and even to change their position, aspect ratio, opacity, and Z-index. It appears (though I haven't tried it yet) that you can also change the zoom factor of individual images. Look for an updated demo in the near future that demonstrates some of these enticing capabilities.

Quick: can you spot what's wrong with this code?

Thread thread = new Thread(new ThreadStart(RunClock));

thread.Start();

  ...

private void RunClock()

{

    while (true)

    {

        Clock.Text = DateTime.Now.ToLongTimeString();

    }

} 

The intent is to launch a thread and have that thread run an infinite loop updating a XAML TextBlock object that shows the current time of day. Aside from the questionable design (there are much more efficient ways to keep a time-of-day display up to date), there's something seriously wrong with this code. In fact, if you run it you'll find that the time-of-day display never updates at all. And therein lies a story.

I was surprised last year when I saw that the Silverlight 1.1 alpha had a System.Threading namespace complete with all the classes I needed to launch threads, synchronize threads, acquire threads from a thread pool, etc. Silverlight 2.0 Beta 1 contains all these classes and more. I'm still amazed that I can write multithreaded apps that run in a browser, and I've been doing a lot of that lately--not because I need to, but just because I can. Silverlight 2.0's BackgroundWorker class simplifies the process of running tasks on background threads, and DispatchTimer is a handy alternative to empty Storyboards for programmable game timers. But you don't have to use either of them; you can use Thread.Start, ThreadPool.QueueUserWorkItem, asynchronous delegates, and other mechanisms familiar to .NET developers to relegate tasks to background threads.

There is one nuance to Silverlight threading that you should be aware of, however: controls and other XAML objects can only be updated by the UI thread (the main thread that drives a Silverlight app). The reason the attempt to set the TextBlock's Text property in the example above fails is that we're attempting to set it from a background thread. For the sample to work, you need to modify it as follows:

private delegate void UpdateUIDelegate();

  ...

Thread thread = new Thread(new ThreadStart(RunClock));

thread.Start();

  ...

private void RunClock()

{

    UpdateUIDelegate action = new UpdateUIDelegate(UpdateUI));

 

    while (true)

    {

        Clock.Dispatcher.BeginInvoke(action);

    }

}

 

private void UpdateUI()

{

    Clock.Text = DateTime.Now.ToLongTimeString();

}

In the corrected code, the background thread marshals the call back to the UI thread by calling UpdateUI through a delegate invoked via the TextBlock object's Dispatcher property. Dispatcher is inherited from DependencyObject and is therefore present in all UI objects. For old folks like me who used to program Windows, this is analagous to using PostMessage to post a message to another thread. You don't have to use the Dispatcher property of the object you intend to update; you can use Dispatcher on any UI object to marshal the call to the UI thread.

Programmers who have written multithreaded WPF apps won't be surprised by any of this because WPF also requires UI objects to be updated by UI threads. And of course there are ways to avoid using Dispatcher altogether. The BackgroundWorker class, for example, fires events on UI threads, so there's no need to do any marshaling before updating a UI object. But there are times in Silverlight when you simply can't avoid marshaling from a background thread to a UI thread, and when those circumstances arise, it's useful to know about Dispatcher.

A few weeks ago I posted a short code sample demonstrating how to do mousewheel zooms in Silverlight 1.1. They're easier to do in Silverlight 2.0 and can be done without calling out of managed code, thanks to the new HtmlWindow class (and the HtmlPage.Window property, which returns a reference to an HtmlWindow).

Here's a summary. First, assume you have a ScaleTransform that facilitates zooms:

<ScaleTransform x:Name="ZoomTransform" />

Next, when the page loads, use HtmlWindow.AttachEvent to register a managed handler for mousewheel events:

HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheelTurned);

HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheelTurned);

HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheelTurned);

Here's the event handler that responds to mousewheel events by manipulating the ScaleTransform:

private void OnMouseWheelTurned(Object sender, HtmlEventArgs args)

{

    double delta = 0;

    ScriptObject e = args.EventObject;

 

    if (e.GetProperty("wheelDelta") != null) // IE and Opera

    {

        delta = ((double)e.GetProperty("wheelDelta"));

        if (HtmlPage.Window.GetProperty("opera") != null)

            delta = -delta;

    }

    else if (e.GetProperty("detail") != null) // Mozilla and Safari

    {

        delta = -((double)e.GetProperty("detail"));

    }

 

    if (delta > 0)

    {

        // Zoom in

        ZoomTransform.ScaleX += 0.1;

        ZoomTransform.ScaleY += 0.1;

    }

    else if (delta < 0)

    {

        // Zoom out

        ZoomTransform.ScaleX -= 0.1;

        ZoomTransform.ScaleY -= 0.1;

    }

 

    if (delta != 0)

    {

        args.PreventDefault();

        e.SetProperty("returnValue", false);

    }

}

Anything affected by the ScaleTransform will now scale up and down with mousewheel movements.

My mousewheel event handler is browser-independent and correctly reports the direction of travel, but it doesn't take into account the differences in the magnitude of travel reported by various browsers. Scott Hanselman pointed me to an excellent blog post by Pete Blois that includes downloadable source code for a MouseWheelHelper class that accounts for differences in magnitude, too. Check it out if you want to make mousewheel handling simple as pie in Silverlight 2.0.

Silverlight 1.1 lacked a managed equivalent of JavaScript's window.alert, so when I wanted to pop up a message box (actually, an alert box) in Silverlight 1.1, I used the platform's DOM integration features to fire a scriptable event from C# and handle the event in JavaScript. Then, in the event handler, I called window.alert.

There's no need for such shenanigans in Silverlight 2.0 because you can call window.alert via a managed wrapper:

HtmlPage.Window.Alert("Error!");

HtmlPage.Window contains a reference to an HtmlWindow object representing the browser window. You can also use HtmlWindow.AttachEvent to register managed handlers for mousewheel events, making it relatively easy to process mousewheel events in Silverlight 2.0 without the intervention of JavaScript. I'll provide an example in a future blog post.

In Silverlight 1.1, it was impossible to load images from the local file system unless you uploaded them to a server first. In Silverlight 2.0, it's a piece of cake to load local images as long as you involve the user by displaying an OpenFileDialog:

OpenFileDialog ofd = new OpenFileDialog();

ofd.Filter = "JPEG Files (*.jpg;*.jpeg)|*.jpg;*.jpeg | All Files (*.*)|*.*";

ofd.FilterIndex = 1;

 

if (ofd.ShowDialog() == DialogResult.OK)

{

    Stream stream = ofd.SelectedFile.OpenRead();

    BitmapImage bi = new BitmapImage();

    bi.SetSource(stream);

    MyImage.Source = bi;

    stream.Close();

}

In this example, "MyImage" is a reference to a XAML Image object.

I just returned from DevWeek in London. The excitement level around Silverlight was very high, and the highlight of my week was doing Silverlight presentations for standing-room-only crowds. Thanks to Dave Wheeler for talking up Silverlight in his keynote and getting the crowds thinking about XAML!

 

Silverlight 2.0 Beta 1 shipped yesterday, so I thought now would be a good time to deploy a new version of SilverLife. SilverLife is a Silverlight implementation of John Conway's game of Life. Version 1.0, which I published in August of last year, targeted the Silverlight 1.1 alpha. Version 2.0 targets the new Silverlight 2.0 beta and can be viewed at http://www.wintellect.com/silverlife. You can download the source code from www.wintellect.com/downloads/silverlife_2.0.zip.

SilverLife 2.0

SilverLife 2.0 looks almost exactly like version 1.0, save for the buttons at the bottom of the grid. Silverlight 1.1 had no controls built in, so I had to manufacture something that looked like buttons. Silverlight 2.0 Beta 1 contains more than 20 built-in control types, so I used Button controls. I also took advantage of the new style support in Silverlight 2.0 to stylize the buttons. First I defined a style named "LifeButton" in XAML:

<Style x:Key="LifeButton" TargetType="Button">

  <Setter Property="Width" Value="132" />

  <Setter Property="Height" Value="44" />

  <Setter Property="FontSize" Value="16" />

</Style>

 

Then I applied the style to the four Button controls:

 

<Button x:Name="StartButton" Style="{StaticResource LifeButton}" ... />

<Button x:Name="StopButton" Style="{StaticResource LifeButton}" ... />

<Button x:Name="StepButton" Style="{StaticResource LifeButton}" ... />

<Button x:Name="ClearButton" Style="{StaticResource LifeButton}" ... />

 

This is an extremely simple example, but styles can be much more complex and can even use templates to completely redefine a control's visual tree. Styles and templates are going to be incredibly important to Silverlight 2.0 apps, so I'll be writing more about them later.

Porting SilverLife from Silverlight 1.1 to 2.0 required just a handful of changes. First I used Visual Studio 2008 to create a new Silverlight project. Then I imported most of the code and XAML from the first version of SilverLife and began patching things up. Here are some of the changes that were required:

  • The XamlReader class moved to the System.Windows.Markup namespace
  • XamlReader.Load now requires an xmlns="http://schemas.microsoft.com/client/2007" attribute
  • HtmlPage.Document.GetElementByID changed to HtmlPage.Document.GetElementById
  • The IsolatedStorageFileStream constructor now throws an IsolatedStorageException rather than an IOException if you attempt to open a file that doesn't exist

One of the most pleasant surprises in Silverlight 2.0 Beta 1 is the simplified deployment strategy. XAML files and other resources (for example, images) are now compiled into the application's assembly. Moreover, the assembly and any assemblies it depends upon are packaged in a XAP file, which is a glorified zip file containing a manifest. The upshot is that deploying the application to Wintellect's Web server required two simple steps:

  • Copy Default.html (the page that hosts the application) into the Silverlife directory
  • Create a ClientBin folder in that directory and copy Silverlife.xap into it

That's it! Other than adding a new MIME type ("application/x-silverlight-app") to IIS and mapping it to XAP files (a one-time procedure), deployment involved nothing more copying a couple of files. How cool is that?

Another pleasant surprise was the fact that Silverlight 2.0 displays a default progress UI as the XAP file is downloaded. Now users won't have to stare at a blank canvas as they wait for assemblies to be downloaded. Nor will you, the developer, have to code up a progress UI.

I'll have much, much more to say in future posts. A new day is dawning, and it has Silverlight 2.0 written all over it!

I just concluded a week in Shanghai and begin the journey home tomorrow. I had the pleasure of working with a lot of very smart (and very polite) Microsoft developers in Beijing and Shanghai these past two weeks and will be bringing home a lot of fond memories.

I had yesterday off, so after having a leisurely breakfast, walking down to Starbucks for a vanilla mocha, and working in the hotel the rest of the morning, I decided to do some sightseeing. I went to the Bund, which is Shanghai’s most popular tourist destination and is about a 15-minute cab ride from my hotel. The Bund lies alongside the Huangpu River in downtown Shanghai. There are lots of skyscrapers along the river, plus the world’s tallest TV tower (the “Oriental Pearl”). Here are some pictures. The Oriental Pearl is the tall tower on the left side in the first picture:

The Bund (1) 

The Bund (2)

While I was at the Bund, I took the opportunity to walk down Nanjing Street, which is Shanghai’s most popular shopping area, to do some shopping for my girls. It’s about a mile long and it’s wall-to-wall stores. Here’s what it looks like:

Nanjing Street (1)

Nanjing Street (2)

I got back to the hotel about 3:00 and hadn’t had lunch, so I walked to the Heng Shan Cafe down the street and had their yummy Sichuan (Szechuan) chili chicken. It costs $7, whereas a large cup of Starbucks coffee costs almost $6:

Sichuan Chicken

The red things are peppers, and yes, they’re hot. But I’m careful not to eat too many of them and it is a *wonderful* meal!

While in China, I've been reading The World is Flat by Pulitzer Prize winning author Thomas Friedman. It is a terrific read and a real eye-opener, especially in light of my recent experiences in India and China. It should be required reading for every high school student in America. If you haven't read it, consider picking up a copy. The world really is flat (and becoming flatter every day), and the way to succeed in a flat world is to think horizontally. Too bad our idiot politicians don't read this book. Then again, it's probably way over their heads.

Yesterday I was asked how one can do mousewheel zooms in Silverlight 1.1 given that 1.1 doesn’t provide any mechanism for registering managed handlers for mousewheel events.

It can still be done, thanks to the fact that Silverlight 1.1 permits managed methods to be called from JavaScript. Here’s an example. Start with a XAML file containing a Canvas with a ScaleTransform. Here’s one that displays a smiley face:

<Canvas>

    <Canvas.RenderTransform>

        <ScaleTransform x:Name="ZoomTransform" />

    </Canvas.RenderTransform>

    <Ellipse Canvas.Left="20" Canvas.Top="20" Height="200"

      Width="200" Stroke="Black" StrokeThickness="10" Fill="Yellow" />

    <Ellipse Canvas.Left="80" Canvas.Top="80" Height="35"

      Width="25" Stroke="Black" Fill="Black" />

    <Ellipse Canvas.Left="140" Canvas.Top="80" Height="35"

      Width="25" Stroke="Black" Fill="Black" />

    <Path Data="M 70, 150 A 60, 60 0 0 0 170, 150" Stroke="Black"

      StrokeThickness="15" StrokeStartLineCap="Round"

      StrokeEndLineCap="Round" />

</Canvas>

Next, register the XAML code-behind class as a scriptable class and include scriptable ZoomIn and ZoomOut methods, too:

[System.Windows.Browser.Scriptable]

public partial class Page : Canvas

{

    public void Page_Loaded(object o, EventArgs e)

    {

        InitializeComponent();

        WebApplication.Current.RegisterScriptableObject

            ("magic", this);

    }

 

    [System.Windows.Browser.Scriptable]

    public void ZoomIn()

    {