Scott Hanselman

Porting WPF Applications to the Microsoft Surface

February 18, '09 Comments [5] Posted in BabySmash | Windows Client | WPF
Sponsored By

What do you need to get through the Great Depression 2.0? Why, a giant computing table, of course! Sadly, the Surface SDK isn't available to folks without a real Surface (which, I say, is a bummer) so I can only tell you that developing for it is awesome. Lame, yes, I know.

I can't get you the SDK (yet?) or a Surface (I'm broke) but I can tell you what developing for it was like.

Of course, if you DO have an extra US$15,000 lying around, why not let your baby sit on it? That's my thinking.

BabySmash (Code and DevCenter here) has been on hold over the holidays, although I have a lot of big plans once I get on the other side of the Mix conference.

One of the things I talked about at my PDC BabySmash talk (WMV) was porting BabySmash to the Surface. BabySmash is a simple WPF application that responds to keyboard and mouse input. Since there's no keyboard or mouse in the standard Surface configuration, I wanted to see how had it would be to port it.

I had to deal with these changes in thinking:

  • Orientation matters. People might be sitting on opposite sides of the Surface
  • No mouse or keyboard. Many people can touch it at once.
  • Multifinger resizing. Folks expect to be able to multitouch a Surface and resize items, throw them around, etc.
  • Different application launcher model. The Surface needs an XML manifest and special icons for its launcher.

Other than these changes, a Surface app is still .NET WPF app, so I wasn't too worried.

Surface Simulator

The first step was to install the Surface Simulator. It only works on 32-bit Vista, so I had to go make myself a 32-bit machine (I'm all 64-bitted up. FTW!) which was a tiny hassle. (Although there ARE some Vista 64 Surface Simulator hacks) The Simulator is exactly what it sounds like. It's not "emulating" however, as a Surface is still a Vista machine underneath. It's more of a frame and some mouse input management to handle multiple mice and make them look like multiple fingers. This works surprisingly well. In the PDC talk, about halfway in, I plugged in three mice, plus the built in touchpad and had multiple virtual "fingers" moving items around the screen.

You create your Surface app as you would a WPF app from File | New Project, hit START and it fires up the Simulator and frames up the WPF app.

Now in my case, I already had an existing WPF application, so I needed to change the type my main Window derived from from "Window" to "SurfaceWindow" and I added an assembly reference and a few namespaces:

using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;

This base class adds adds all the Surfacey goodness and events and properties that my Window will need.

"ContactDown rather than KeyDown and MouseDown

I added an event for "Contact_Down" rather than watching for keyboard events and mouse clicks that would never come.

private void SurfaceWindow_ContactDown(object sender, ContactEventArgs e)
{
controller.AddFigure(this, " ", e.GetPosition(mainGrid).X, e.GetPosition(mainGrid).Y);
}

BabySmash uses an MVC model, so I'm taking the input from the View and immediately delegating to a main controller object that handles everything.

Orientation

Your app can let the system decide which side of the Surface table the user is sitting on when it starts up. This isn't a huge deal with BabySmash, since it's just shapes, but still there's some text on the screen so I wanted the app to be right-side-up when appropriate.

There are a bunch of new properties on a SurfaceWindow, and one is:

AutoOrientsOnStartup="True"

This changed the orientation of my entire application, basically turning it upside down when need-be. This did mean that I couldn't use any low-level X and Y APIs, otherwise the coords would be "lost in translation." As long as I stuck with high-level stuff like "GetPosition" to pull my X and Y's out, the Surface SDK would handle translation and everything just worked.

Application Launcher Manifest

I needed to create a few icons for my app to live in the Surface Launcher and a manifest. There's other options I didn't use, as well as ways to create animated icons, or to have your application run as an "attract mode" app, to get folks wandering by to stop and touch the Surface.

<?xml version="1.0" encoding="utf-8" ?>

<ss:ApplicationInfo
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ss="http://schemas.microsoft.com/Surface/2007/ApplicationMetadata">
<Application>
<Title>BabySmash!</Title>
<Description>BabySmash! for Surface</Description>
<ExecutableFile>c:\BabySurface\BabySurface.exe</ExecutableFile>
<Arguments></Arguments>
<IconImageFile>c:\BabySurface\Resources\icon.png</IconImageFile>
<Preview>
<PreviewImageFile>c:\BabySurface\iconPreview.png</PreviewImageFile>
</Preview>
</Application>
</ss:ApplicationInfo>

ScatterView

In the original BabySmash app I had a canvas that would hold all the shapes and letters, but I wanted things to be more Surface-like in this (hacked-together) BabySurface version.

public void AddFigure(UserControl c, double x, double y)
{
this.figuresCanvas.Children.Add(c);
}

Fortunately there's a control called a ScatterView that makes throwing stuff on the Surface insanely easy. For example:

<s:SurfaceWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:s="http://schemas.microsoft.com/surface/presentation">
<s:ScatterView>
<Image Source="Toco Toucan.jpg"/>
<Image Source="Green Sea Turtle.jpg"/>
<Image Source="Desert Landscape.jpg"/>
</s:ScatterView>
</s:SurfaceWindow>

From their blog (here's a video of the Surface SDK in action):

ScatterView is a custom ItemsControl in our SDK which apps can databind or populate with any type of content. Simply sticking some Image elements in it gives you a basic Photos-like app without writing any code. By baking common manipulations into WPF SDK controls like this, we’re able to free developers up to focus on things that are unique to their apps while designers use Blend to add some custom pizazz.

I changed my AddFigure method to put the new shapes in a ScatterView. I also hooked up a "ContactLeave" event to the new item so that the application would giggle when you lift your finger.

public void AddFigure(UserControl c, double x, double y)
{
var s = new ScatterViewItem();
s.Content = c;
s.ContactLeave += new ContactEventHandler(s_ContactLeave);
this.figureScatterView.Items.Add(s);
}

This was cool, but it had a few issues. First, the ScatterView puts everything in a "Polaroid"  border by default:

microsoft-surface

The result was that my BabySmash shapes were inside a Photo Border. Easily removed with a custom style that marked a few properties as {x:Null}:

<Style x:Key="ScatterViewItemBabySmashStyle" TargetType="{x:Type s:ScatterViewItem}">
<Setter Property="Background" Value="{x:Null}"/>
<Setter Property="BorderBrush" Value="{x:Null}"/>
<Setter Property="Foreground" Value="{x:Null}"/>
...

The second issue was that while my BabySmash shapes were draggable and scattered nicely, they weren't resizable. Well, they WERE, except when you resized them, you were resizing the container - that is, resizing the now-invisible Polaroid border. I needed a box to put the shapes in that would effectively lie to the inner control and scale/size them as the outside scaled and resized. Enter the ViewBox, including with WPF.

public void AddFigure(UserControl c, double x, double y)
{
var s = new ScatterViewItem();
s.Center = new Point(x, y);
s.Style = this.Resources["ScatterViewItemBabySmashStyle"] as Style;

Viewbox v = new Viewbox();
v.Child = c;

s.Content = v;

s.ContactLeave += new ContactEventHandler(s_ContactLeave);
this.figureScatterView.Items.Add(s);
}

Link to Video of Developing on Surface from PDC 2007I put the Control in the ViewBox, and put the ViewBox+Control in a ScatterViewItem. Bam, resizable, scattered BabySmash shapes.

I also got dragging and inertia (physics) for free so you, ahem, the baby, can spin and throw shapes around as well.

This development was all done on the simulator. I never saw a real Surface until I got to PDC last year. The app ran exactly as should. I was a little worried that the real Surface was somehow different, but not so. That was a huge relief.

If you have a very visual, shape oriented application that could benefit from touch and a big screen, porting it to the Surface isn't rocket science.

Definitely check out the PC17 "Developing for Microsoft Surface" talk from PDC. There's a lot of good stuff in the PPT as well with insights into how Multitouch is going to work in .NET 4.0 and Windows 7 and how work on Surface now is driving that future.

And really, who WOULDN'T want a Big Ass Table? I, for one, would like one for the living room. ;)

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

The Weekly Source Code 36 - PDC, BabySmash and Silverlight Charting

November 6, '08 Comments [6] Posted in ASP.NET | ASP.NET MVC | BabySmash | Open Source | PDC | Silverlight | Source Code
Sponsored By

Scott Hanselman presenting at PDC 2008First, let me remind you that in my new ongoing quest to read source code to be a better developer, Dear Reader, I present to you thirty-fifth in a infinite number of posts of "The Weekly Source Code."

At the end of my crazy babies talk at PDC (Tips on how I prepared here) I had a big demo where I gave a URL to a Silverlight version of BabySmash that Grant and I built for the show. You can watch the presentation online if you like and fast forward to the end (around 60 minutes in) and see the big demo. Basically we had the Silverlight BabySmash talk via ADO.NET Data Services (I'll post in detail in the near future) to a SQL backend. Then I had an MVC reporting site that had some charts that would update as folks smashed. There were over 90,000 smashes during the talk.

imageThe chart was updating as folks were smashing and we even had a Baby vs. Baby fight break out where the "A" people and the "J" people were going at it. Jeff Atwood started the bloodbath with this tweet as he urged on the overflow room along with Phil Haack. That man's trouble, I tell you.

In the talk, I started out with a old .NET 1.1 chart from 2003 and showed it working, unchanged, in ASP.NET 3.5 SP1. It's just a nice reminder that things usually work just as they should. Then I upgraded it to a new .NET 4.0 ASP.NET Chart that I'll blog about in detail soon. Then, I showed the final site with the new Silverlight Charts. Tim Heuer has a great post on how to databind with these new charts.

What's really cool about these Silverlight Charts is that they are Ms-PL (Microsoft Public License) which is a REALLY relaxed license. They're released as part of the larger Silverlight Toolkit up at http://www.codeplex.com/Silverlight. There's a bunch of controls in there. It is a preview release though, so things will change, and hopefully only get better:

You can check out the Toolkit Chart samples and run them yourself here. It's nice that the chart sampler actually includes the source code within the Silverlight sample app. You can browse dozens of charts, then switch tabs and see the XAML and code-behind. This all lives in Microsoft.Windows.Controls.DataVisualization, the namespace (so far) for these controls.

image

My reporting page included a Silverlight Chart and a Virtual Earth control to show where people were smashing from. The data is coming from the Astoria ADO.NET Data Service, which is easy to get to via either JavaScript or from Silverlight.

You add the charts to your Silverlight application by adding a reference to the assembly then assigning a namespace to them:

xmlns:charting="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;assembly=Microsoft.Windows.Controls.DataVisualization"
xmlns:datavis="clr-namespace:Microsoft.Windows.Controls.DataVisualization;assembly=Microsoft.Windows.Controls.DataVisualization"

Them, lay them out. I've got two charts here, one column and one pie. I also did some stuff like the linear gradient for the background, etc. Still, pretty simple.

<charting:Chart Grid.Column="0" Height="300"  StylePalette="{StaticResource PaletteColors}" Style="{StaticResource ChartStyle1}" >
<charting:Chart.Background>
<LinearGradientBrush EndPoint="1.332,1.361" StartPoint="-0.107,-0.129">
<GradientStop Color="#FF6CA9D5"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</charting:Chart.Background>
<charting:Chart.Axes>
<charting:Axis x:Name="colAxis" Orientation="Vertical" AxisType="Linear" Minimum="0" Maximum="1"></charting:Axis>
</charting:Chart.Axes>
<charting:Chart.Series>
<charting:ColumnSeries x:Name="colSeries" ItemsSource="{StaticResource BasicValues}" DependentValueBinding="{Binding Count}" IndependentValueBinding="{Binding Character}" Title="Character">
</charting:ColumnSeries>
</charting:Chart.Series>
</charting:Chart>
<charting:Chart Style="{StaticResource ChartStyle1}" Grid.Column="1" Height="300" StylePalette="{StaticResource PaletteColors}" >
<charting:Chart.Axes>
<charting:Axis Orientation="Vertical" AxisType="Linear" Maximum="100000"></charting:Axis>
</charting:Chart.Axes>
<charting:Chart.Series>
<charting:PieSeries x:Name="pieSeries" ItemsSource="{StaticResource BasicValues}" DependentValueBinding="{Binding Count}" IndependentValueBinding="{Binding Character}" Title="Character">
</charting:PieSeries>
</charting:Chart.Series>
</charting:Chart>

We had a generic list of "CharacterSmash" data, as in List<CharacterSmash> that we'd be binding to the chart.

private readonly List<CharacterSmash> characterData = new List<CharacterSmash>();

For the purposes of the presentation, I just polled for the data by making an asynchronous call to the service, then updating the bar and pie chart when it returned:

private void RequestSmashCountData()
{
var container = new SmashMetricsContainer(new Uri("/BabySmashPDC/SmashService.svc", UriKind.Relative));

// Setup data query
var query = container.SmashCount;

// Start the async query
query.BeginExecute((asyncResult =>
{
// Get the matching results from the service call
var matches = query.EndExecute(asyncResult);
UpdateCharacterData(matches);
UpdateBarChart();
UpdatePieChart();
}), null);
}

See how the BeginExecute includes the "do this when you return" as a lambda? It's a tidy syntax.

UPDATE: Tim Heuer emailed me to say that we're re-databinding the results. Instead, he wisely points out:

"On the code where you are getting the smash metrics for the silverlight charts…I see that you are re-binding the data?

If you bind to an observablecollection and just change that the chart should change with the data…including the Y-axis growth."

Excellent point! Tim's right. The way I'm doing it works, but it's old school. If I just updated a ObservableCollection the chart would notice the changes and update itself.

The updates are also clean, just databinding the results:

private void UpdateBarChart()
{
var axis = (Axis)FindName("colAxis");
if (axis != null)
axis.Maximum = GetMaximumCount() + 50;

var colSeriesControl = (ColumnSeries)FindName("colSeries");
if (colSeriesControl != null)
colSeriesControl.ItemsSource = characterData;
}

All we had to do that was interesting at all was to make sure the Y-axis grew as the data grew.

Who do we have to thank for this charting control? David Anson is who. Basically he was the Primary Dev and only Tester on the whole thing, and you should check out his blog for lots of inside information on charting in Silverlight.

UPDATE: David had development help from Jafar Husain, Jeremy Sheldon, Delian Tchoparinov, Alex Gorev and Sean Boon and designer Mehdi Slaoui Andaloussi.

If making a complex chart seems daunting, David has ChartBuilder that you run now in your browser. It'll generate and show you the XAML you need for your chart.

image

There was so much announced at PDC, I wanted to make sure that folks heard about this important release that might have been lost in the shuffle. Even better, the source is open so if you don't like it, change it.

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

PDC 2008: BabySmash Preparations

October 26, '08 Comments [16] Posted in BabySmash | PDC
Sponsored By

As I've mentioned before, I've got a talk at PDC this year, it's "TL49" and it's called "Microsoft .NET Framework: Overview and Applications for Babies." It's on Monday at 5:15pm in Room 411.

The baby aspect is really secondary, mostly because BabySmash (and what I do with it in the talk) is Not Northwind. This was the strangest Microsoft talk I could sneak past the bosses without them noticing. It also crosses over into other talks and many other products that I'll mention as the week goes on.

Stressful times...I'm nervous because:

  • I've never tried to do some many complex and intertwining demos at once.
  • I've never had so many people help out to make it all happen.
  • It may suck.
  • I've got like 7 hours of content to fit in 75 minutes.
  • I've forgotten completely what I'm talking about. ;)

I hope enough people show up. Starting to get the pre-show jitters!

Here's a teaser of what we were able to accomplish at Tim Huckaby's Party tonight in San Diego. More soon!

852494

(That's Clemens Vaster's daughter and yes that's what you think it is. Tim has one at his house. Crazy.)

See you at PDC!

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Firefox, ClickOnce, XBAPs and .NET 3.5 SP1

September 5, '08 Comments [3] Posted in ASP.NET | BabySmash | Musings | Tools | Windows Client | WPF
Sponsored By

One of the things that I noticed immediately when I made the SmallestDotNet was that Firefox was reporting the version of the .NET Framework installed. There's a Firefox extension that is installed with .NET 3.5SP1. I was stoked about this because I'd like users of BabySmash to be able to use ClickOnce from Firefox to launch it.

ClickOnce and Firefox

When you install .NET Framework 3.5SP1, there's a key written to the registry whether Firefox is installed or not. If Firefox is installed later, it will notice the key and use the plugin. If it's already installed, it'll just work. The registry key points to a Firefox Extension (XPI) that acts like the 3rd party FFClickOnce extension that a lot of us installed before.

The registry key is at HKLM:\Software\Mozilla\Firefox\Extensions This Firefox Addin helps ClickOnce applications work from inside of Firefox 2 or Firefox 3. It'll be installed with any installation of .NET 3.5 SP1, including the smaller Client Profile.

The add-in looks like this...

Picture of the Firefox Addins dialog

And its options dialog looks like this:

image

On my system the Firefox UserAgent now looks like this:

Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 (.NET CLR 3.5.30729)

By default, as you can see in the dialog, the browser will report just the most recent version of the .NET Framework unless you say otherwise.

What happens if the FFClickOnce extension is already installed?

In Firefox 3 the FFClickOnce add on will automatically get disabled as it has not been updated.

In Firefox 2 the FFClickOnce extension does not alter the user agent string due to a safeguard against creating a user agent greater than 128 characters.  What happens when the user clicks on a .application link is dependent on the user’s configuration.

For Firefox 2.0 this is the table of possible prompting/launching configurations.  The default for each add-on is marked in bold. 

.NET Assistant FFClickOnce Result
No Prompt Prompt FFClickOnce prompts and handles the click
No Prompt No Prompt FFClickOnce handles the click
Prompt Prompt

.NET Assistant prompts and handles the click

Prompt No Prompt

FFClickOnce handles the click

When both add-ons are in the default configuration the user will get the FFClickOnce prompt and click once activation will follow the FFClickOnce path which may bypass IE download policy.  In all cases the normal ClickOnce UI will be shown.

What this all means is that ClickOnce will work in FireFox 2 and 3, whether FFClickOnce is installed or not.

How's it done?

The plugin is written with standard XUL (pronounced zoo-el) via XML and JS.

As an aside, I think it's cool that the XML namespace for XUL is:
http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul

You can go find the plugin yourself in:

C:\Windows\Microsoft.NET\Framework\v3.5\Windows Presentation Foundation\DotNetAssistantExtension

It's a .jar file, and you can copy it out and rename it .zip and open it up. The basic gist is that the plugin watches for a particular MIMEType and application, and if it matches, it launches the ClickOnce application using the the .NET Framework's PresentationHost.exe.

isClickOnce: function()
{
var mimeInfo = dialog.mLauncher.MIMEInfo;

// Checking the file extension may be second guessing the webserver
// but when 'prompt' is set the user will still have a save button.
if (mimeInfo.MIMEType == "application/x-ms-application" ||
mimeInfo.primaryExtension == "application")
{
return true;
}

return false;
},


launch_application: function()
{
this.execute(this.getSystem32Dir() + \\PresentationHost.exe,
"-LaunchApplication " + dialog.mLauncher.source.spec);

dialog.mDialog.close();
}

It's not too complex, and it's sure nice that BabySmash users can use ClickOnce to launch the smash.

XBAP

XBAPs, or XAML Browser Applications are also enabled in Firefox in .NET 3.5 using the standard NPAPI browser plugin API. The NPWPF.dll enables XBAPs under Firefox.

Image of the NPWPF.dll that enables XBAPs

XBAPs are WPF applications that run inside the Browser's Chrome. They're good for Intranet applications where you want really easy deployment, the complete .NET Framework (as opposed to Silverlight) and a browser's navigational model.

If you type in about:plugins in Firefox, you can get a list, including this one:

The about:plugins page in Firefox 

Notice the MIME Types that this plugin has registered for and the extensions (suffixes) it handles. Each of these plugins are automatically installed and enabled when you install the .NET Framework 3.5SP1, so you can count on them.

Thanks to Eric Harding, Troy Martez and Jennifer Lee for helping me pull this information together. They're going to be adding more content to the Windows Client website very soon with more details how for developers to package and use the .NET Client Profile. They'll also be releasing lots of documentation and details on ClickOnce and deployment improvements in the framework. I'll point you to everything I can dig up, Dear Reader, as soon as I can.

Related Links

Technorati Tags: ,

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web

Learning WPF with BabySmash - Manually Managing ClickOnce and some more Designer Goodness

August 1, '08 Comments [22] Posted in BabySmash | Windows Client | WPF
Sponsored By

NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.

BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Feedback and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!

One of the pieces of feedback on BabySmash! was that even though people like that the application automatically updates (via ClickOnce) when they sit down to play BabySmash! their babies want to play it NOW. They didn't like that it tries to update itself when you launch it.

When you setup a ClickOnce application, you get a few choices. You can have the application check for updates before it starts and applying updates before it starts, or you can have it check after it starts and install the updates the next time it starts up. Or, you can say Don't Check For Updates.

I personally find this dialog a little confusing. What this really means is "I'll check manually in code."

Application Updates

At this point, I'm still deploying my app as a ClickOnce app, but now the actual updating is up to me. I used to pop up the Options dialog every time the app started, but again, folks complained, so I had to figure out a way to let them know that an update is available without "stopping the action."

When the app starts up now, it fires of a call to CheckForUpdateAsync from inside System.Deployment.Application and listens for a response. This happens in the background (hence "async"):

if (ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment deployment = ApplicationDeployment.CurrentDeployment;
deployment.CheckForUpdateCompleted += deployment_CheckForUpdateCompleted;
try
{
deployment.CheckForUpdateAsync();
}
catch (InvalidOperationException e)
{
Debug.WriteLine(e.ToString());
}
}

If there is an update, I show a label in each MainWindow:

void deployment_CheckForUpdateCompleted(object sender, CheckForUpdateCompletedEventArgs e)
{
ClickOnceUpdateAvailable = e.UpdateAvailable;
if (ClickOnceUpdateAvailable)
{
foreach (MainWindow m in this.windows)
{
m.UpdateAvailableLabel.Visibility = Visibility.Visible;
}
}
}

The label is really simple, just a label with a glow:

<TextBlock x:Name="UpdateAvailableLabel" Visibility="Collapsed" Margin="15,0,0,0" FontSize="12">
<TextBlock.BitmapEffect>
<BitmapEffectGroup>
<OuterGlowBitmapEffect x:Name="UpdateGlow" GlowColor="Red" GlowSize="3"/>
</BitmapEffectGroup>
</TextBlock.BitmapEffect>
<Bold>Update Available - Visit Options to Update BabySmash!</Bold>
</TextBlock>

Except the glow "pulses" between three colors and repeats forever. I set the DesiredFrameRate to a low number like 10 fps rather than what WPF will attempt, which is 60 fps! I'll post later how we can detect how awesome the user's hardware is and scale or turn off animations and effects.

<Window.Resources>
<Storyboard x:Key="Timeline1" Timeline.DesiredFrameRate="10">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" RepeatBehavior="Forever"
AutoReverse="True" Storyboard.TargetName="UpdateGlow"
Storyboard.TargetProperty="GlowColor" >
<SplineColorKeyFrame Value="#FFCDCDCD" KeyTime="00:00:00"/>
<SplineColorKeyFrame Value="#FFB92121" KeyTime="00:00:01"/>
<SplineColorKeyFrame Value="#FF2921B9" KeyTime="00:00:02"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Window.Resources>

<Window.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{StaticResource Timeline1}"/>
</EventTrigger>
<EventTrigger RoutedEvent="FrameworkElement.Loaded"/>
</Window.Triggers>

Now I've got a nice passive FYI that there's an update.

image

I want to show an update button when the user visits the Options Dialog, which brings me to my awesome volunteer designer Felix Corke (blog). If you want a great WPF or XAML designer, give Felix money. Here's my original Option Dialog:

OldBabySmashOptions

And here's the Felix version:

image

Seriously. It hurts. Brings a tear to my eye.

When the user needs to update, I'll do two things. First, there will be a button that says UPDATE!

updatingoptions

Second, after they've hit Update there will be a Progress Bar and that will update as the new version is download in the background.

 optionsdialogupdating

The API is surprisingly easy to use. We check to see if we were launched from the Network, then I check again (really not needed since I did it earlier, but I like to double-check) for an update. This isn't asynchronous, but it's fast.

I setup two event handlers to listen to the UpdateProgress changing, and to get notification when the system has completed the download of the update. Then I fire off an asynchronous update. 

private void updateButton_Click(object sender, RoutedEventArgs e)
{
if (ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment deployment = ApplicationDeployment.CurrentDeployment;
if (deployment.CheckForUpdate())
{
MessageBoxResult res = MessageBox.Show("A new version of the application is available,
do you want to update? This will likely take a few minutes...",
"BabySmash Updater", MessageBoxButton.YesNo);
if (res == MessageBoxResult.Yes)
{
try
{
deployment.UpdateProgressChanged += deployment_UpdateProgressChanged;
deployment.UpdateCompleted += deployment_UpdateCompleted;
deployment.UpdateAsync();
}
catch (Exception)
{
MessageBox.Show("Sorry, but an error has occurred while updating.
Please try again or contact us a http://feedback.babysmash.com. We're still learning!",
"BabySmash Updater", MessageBoxButton.OK);
}
}
}
else
{
MessageBox.Show("No updates available.", "BabySmash Updater");
}
}
else
{
MessageBox.Show("Updates not allowed unless you are launched through ClickOnce from http://www.babysmash.com!");
}
}

The ProgressChanged event is really convenient because it includes the percentage complete! One less thing for me to do.

void deployment_UpdateProgressChanged(object sender, DeploymentProgressChangedEventArgs e)
{
this.updateProgress.Value = e.ProgressPercentage;
}

The Completed event is also fairly tidy (this one is simplified, and you could always use extra error handling. Note that since this was an asynchronous call, any exceptions that might have occurred elsewhere will show up here in the Error property of the AsyncCompletedEventArgs parameter.

void deployment_UpdateCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
MessageBoxResult res2 = MessageBoxResult.None;
if (e.Error == null)
{
res2 = MessageBox.Show("Update complete, do you want to restart the application to apply the update?",
"Application Updater", MessageBoxButton.YesNo);
}
else
{
MessageBox.Show("Sorry, but an error has occured while updating. Please try again or contact us a http://feedback.babysmash.com. We're still learning!",
"Application Updater", MessageBoxButton.OK);
}
if (res2 == MessageBoxResult.Yes)
{
System.Windows.Forms.Application.Restart();
}
}

I'll update this app to .NET 3.5 SP1 when it ships and I'll get a bunch of new features to make BabySmash! better like:

All in all, not much code for me to switch from an automatic ClickOnce Deployment that I had no control over to one I now have not only complete control over, but also one that fits in more nicely with our always improving UI. Also, an FYI, ClickOnce works with WinForms or WPF equally.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Page 1 of 4 in the BabySmash category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.