Posts Tagged Silverlight 3 Beta

Update: Displaying background activity in a Silverlight RIA application

Well, it’s been a while since I first posted on this, but the feedback was incredible, and I got some great suggestions.  As a result, I’ve updated the Activity control and (finally) have a running sample on the web!  The fundamental idea of the control has remained the same, as well as the mechanisms by which it can work.  It’s still a prototype, but a very useful one at that!

To see the Activity control in action, take a look here: Activity Control Demo

The demo requires the Silverlight 3 beta to be installed on your machine, which can be found here: http://silverlight.net/getstarted/silverlight3/default.aspx

The demo application shows two examples of using the Activity control to display async/background activity (click the images for deep-links into the Silverlight application):

  • With .NET RIA Services and the DomainDataSource, the Activity control uses element-to-element binding to indicate to the user that data is being loaded or submitted.  The demo page is running live on my server using the Northwind sample database.  Those of you who think you can play some dirty tricks by overwriting the data on my server (yes, I provided insert/update/delete functionality) – any attempts to do so will be quickly foiled, since the server will accept the requests, but ignore them! ;)

.NET RIA Services with the Activity Control

  • Any background activity might warrant using the activity control.  The second example I’ve provided uses the activity control while I use a simple statistical method of approximating PI (yes, very geeky).  It takes millions of iterations to get a mediocre approximation of PI (I know, I’m not using an efficient method, but it does do a lot of work!).  Here, I’ve augmented the Activity control to allow the work to be stopped/cancelled:

Calculating PI with background activity

 

So by now I’m sure you’re asking: “What’s changed?”  Well, let me tell you!

  • Thanks to feedback from a variety of folks, I’ve removed the all-encompassing template that included the pop-up window.  That’s now baked into the control template.  If you’d like to change it, you can use the Blend 3 Preview to re-template the control.  In its place, I’ve added:
    • A style for the progress bar.  If you’d like to hide it, use a style to collapse the progress bar.
    • A means for setting “ActiveContent”.  The Activity control works sort of like a HeaderedContentControl.  You now have ActiveContent and ActiveContentTemplate, which allow you to define in XAML what should appear above the progress bar.  You can put any content you like here, and it’s how I added the cancel button in my PI calculation example.  This is also great because it allows you to easily change the text from “Loading…” to whatever you prefer simply be setting the ActiveContent property to the string of your choice!
  • The default template for the ActivityControl now sets IsHitTestVisible and IsEnabled to false on its content immediately when IsActive becomes true.  In my old prototype, this only happened when the Activity UI became visible, which meant users could continue to interact with the content while it was busy – a potentially dangerous combination if the user’s interactions can be overriden by the asynchronous work (such as editing values in a DataGrid when the data is being reloaded).  This was from a suggestion by Luke Tigaris in the comments on my last post.  Let me know what you think!
  • You can now call ResetActivity() on the control to force it to honor the visibility of the control immediately rather than enforcing the display delay and minimum display time.  This way, if a user cancels the background work, you can force the Activity UI to be hidden immediately.

 

Anyhoo, let me know what you think of the changes!  I’ve gotten lots of great feedback on the control so far, and I’m sure folks will come up with more as time goes on.

For both of these, you’ll need the Silverlight 3 Beta.  The Demo application is built using .NET RIA Services.

P.S.  Big thanks again to Corrina for providing the beautiful theme I used in my application.

P.P.S.  Isn’t navigation and deep-linking in the Silverlight 3 Beta cool?  That’s how I made those images link directly to the corresponding pages of my demo application.  I may post more on this in the future.  In the meantime, take a look at the code to see just how little it took to accomplish that! (hint: MainPage.xaml in the Silverlight Navigation Application project template that’s included with the Silverlight 3 Beta Tools for Visual Studio has pretty much everything that’s required)

Update 9/14/2009 – I’ve made a small update to the Activity control to fix a performance issue.  You can find it here.

, , , , , ,

39 Comments

Playing around with Silverlight 3… a late Easter Egg hunt – Revealed!

About a week and a half ago I issued a challenge to folks to figure out what the Silverlight 3 application below does.  I dropped a few hints, but nobody guessed it!  I had hoped for a few more guesses, but c’est la vie, I suppose.  In the meantime, I should fulfill my end of the bargain and reveal the secrets of my mystery application, and some of the things I learned while putting it together.

To view the app, you’ll need the Silverlight 3 Beta installed on your machine (download links here):

Windows:
http://go.microsoft.com/fwlink/?LinkID=143433
Mac:
http://go.microsoft.com/fwlink/?LinkID=143434

Get Microsoft Silverlight

First, let me show you the “visible” portion of my application.  Pardon my slow reveal, but it will help create drama, I promise!

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
        <dataControls:DataForm CurrentIndex="{Binding ElementName=dg, Mode=TwoWay, Path=SelectedIndex}" HorizontalAlignment="Right" Margin="0,8,8,223" Width="267" ItemsSource="{Binding Mode=OneWay, Path=Collection}"/>
        <chartingToolkit:Chart Height="211" HorizontalAlignment="Right" Margin="0,0,8,8" DataContext="{Binding Mode=OneWay, Path=Collection}" VerticalAlignment="Bottom" Width="267" Title="Chart Title">
            <chartingToolkit:ScatterSeries SelectedItem="{Binding ElementName=dg, Mode=TwoWay, Path=SelectedItem}" DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding Mode=OneWay}" AnimationSequence="FirstToLast" IsSelectionEnabled="True"/>
        </chartingToolkit:Chart>
        <data:DataGrid x:Name="dg" Margin="8,8,279,223" ItemsSource="{Binding Mode=OneWay, Path=Collection}" AutoGenerateColumns="False">
            <data:DataGrid.Columns>
                <data:DataGridTextColumn Header="Bar" Binding="{Binding Path=Bar}"/>
                <data:DataGridTextColumn Header="Bat" Binding="{Binding Path=Bat}"/>
                <data:DataGridTextColumn Header="Baz" Binding="{Binding Path=Baz}"/>
                <data:DataGridTextColumn Header="Foo" Binding="{Binding Path=Foo}"/>
                <data:DataGridTextColumn Header="Title" Binding="{Binding Path=Title}"/>
                <data:DataGridTextColumn Header="X" Binding="{Binding Path=X}"/>
                <data:DataGridTextColumn Header="Y" Binding="{Binding Path=Y}"/>
            </data:DataGrid.Columns>
        </data:DataGrid>
        <controls:Calendar Height="211" Margin="8,0,279,8" VerticalAlignment="Bottom"/>
    </Grid>
    <Button Grid.Row="1" Content="Reset" Click="Button_Click" Margin="8,0,8,0" Height="30" />
</Grid>

So there you go.  The XAML is fairly simple – it uses some of the Silverlight SDK controls (DataForm, DataGrid, and Calendar) as well as the Toolkit’s Chart control.  A few folks in the comments of the original post remarked on the behavior of the controls here.  Some fun things to notice: the Chart, DataGrid, and DataForm are all bound to the same collection.  Furthermore, their SelectedItems are all bound together as well.  The result is that changing a value in the DataGrid or DataForm will move it in the chart and update its value in the other controls.  All cool stuff, even though I put it in the app to throw you off!

You’ll notice that I used ElementName binding – new in SL3 (imported from WPF XAML) to accomplish all of this behavior.  With ElementName binding and many of the other new Silverlight features, you can really make rich, dynamic applications with very little code.  And with the new features in the Blend 3 preview, building this app was easier than ever.  The new Sample Data feature in Blend 3 is great for prototyping:

Blend 3's Sample Data feature makes prototyping easy!

With the Sample Data feature, you can define a data source (as I did above) and add fields that are simple (strings, ints, etc.), complex (objects on which you can add other fields"), or collections.  Blend will generate sample data ranging from lorem ipsum text to random numbers.  You can then bind your UI to this sample data and use the binding UI in Blend to set up your bindings:

Editing bindings in Blend 3 is easy!

Again, very cool stuff.  Blend 3 and Silverlight 3 make it really easy to construct rich UI for your application and do it quickly.  All in all, building this piece of the mystery application using Blend 3 took me no more than an hour, and the only reason it took that long is that I spent some time playing with some of the Blend features and the Chart control!

 

Ok, ok, I’m stalling, I know…

 

I guess it’s time for me to reveal what the real Easter Egg is.  “All of that Silverlight 3 and Blend 3 stuff is great…”, I’m sure you’re saying, “but get to the secret already!”

First, let me outline the hints I gave:

  • I pointed you to gizmodo.com – an article that appeared the day of the post.  That morning, a fun little article about an Easter Egg on a popular social networking site appeared.
  • In the comments, I pointed out that the business-like functionality was too serious of a guess!
  • The Reset button actually does something.

In truth, really only the first hint would give you and idea of what to do with this application.  Without taking a look there, you weren’t likely to figure out what to do with my application.

So, if you haven’t already figured it out from my revelations above, the easter egg in my application is that the Konami Code makes a super-secret special message appear!  I figured if it’s good enough for Facebook, it’s good enough for me!  Here’s what you need to do:

Click the application to give it focus.  Hit the following keys:

Up, Up, Down, Down, Left, Right, Left, Right, B, A

And observe!

"UberCool!" shown in the Mystery Application

And true to form, the Reset button will hid the message and allow you to re-enter the code.

Enabling this functionality turns out to be pretty simple.  For the purposes of this mystery application, I created a KonamiCode control that simply changes its visual state once the code is entered.  Its template uses the new easing functions (very easy to set up in a visual state transition in Blend 3) in Silverlight to animate the rotation of the “secret message.”  The KonamiCode control is a ContentControl, and listens for key presses on its content.  For kicks, I made the control work more generally, allowing a developer to specify the keystrokes that will trigger the visual state change.  Who knows, maybe someone will find something more useful to do with it!

With the KonamiCode control, you too can add the Konami Code Easter Egg to your Silverlight 3 applications!  It’s as simple as wrapping your UI in the control, like so:

<KonamiCode:KonamiCodeControl x:Name="kcc">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
            <dataControls:DataForm CurrentIndex="{Binding ElementName=dg, Mode=TwoWay, Path=SelectedIndex}" HorizontalAlignment="Right" Margin="0,8,8,223" Width="267" ItemsSource="{Binding Mode=OneWay, Path=Collection}"/>
            <chartingToolkit:Chart Height="211" HorizontalAlignment="Right" Margin="0,0,8,8" DataContext="{Binding Mode=OneWay, Path=Collection}" VerticalAlignment="Bottom" Width="267" Title="Chart Title">
                <chartingToolkit:ScatterSeries SelectedItem="{Binding ElementName=dg, Mode=TwoWay, Path=SelectedItem}" DependentValuePath="Y" IndependentValuePath="X" ItemsSource="{Binding Mode=OneWay}" AnimationSequence="FirstToLast" IsSelectionEnabled="True"/>
            </chartingToolkit:Chart>
            <data:DataGrid x:Name="dg" Margin="8,8,279,223" ItemsSource="{Binding Mode=OneWay, Path=Collection}" AutoGenerateColumns="False">
                <data:DataGrid.Columns>
                    <data:DataGridTextColumn Header="Bar" Binding="{Binding Path=Bar}"/>
                    <data:DataGridTextColumn Header="Bat" Binding="{Binding Path=Bat}"/>
                    <data:DataGridTextColumn Header="Baz" Binding="{Binding Path=Baz}"/>
                    <data:DataGridTextColumn Header="Foo" Binding="{Binding Path=Foo}"/>
                    <data:DataGridTextColumn Header="Title" Binding="{Binding Path=Title}"/>
                    <data:DataGridTextColumn Header="X" Binding="{Binding Path=X}"/>
                    <data:DataGridTextColumn Header="Y" Binding="{Binding Path=Y}"/>
                </data:DataGrid.Columns>
            </data:DataGrid>
            <controls:Calendar Height="211" Margin="8,0,279,8" VerticalAlignment="Bottom"/>
        </Grid>
        <Button Grid.Row="1" Content="Reset" Click="Button_Click" Margin="8,0,8,0" Height="30" />
    </Grid>
</KonamiCode:KonamiCodeControl>

Anyhoo, here’s the code.  You can download either just the control or the full source for the Mystery Application:

Enjoy!  And let me know what you think!  Even RIA apps deserve to have a little fun, and I hope this brings countless hours minutes of entertainment to folks reading my blog! :)

 

UPDATE: 5/19/2009 – 12:52 AM.  Ok, just after I posted this, the following appeared on my WordPress admin dashboard.  So perfect:

WP-Konami WordPress plugin

Furthermore, this plugin directed me to this site:  http://konamicodesites.com/

I’ll have to get listed!

 

P.S. My colleague, coworker, and fellow PM Scott Morrison just returned from TechEd 2009 and posted some of the great Silverlight 3 and .NET RIA Services content that he demoed there.  Check it out!

Update: 7/11/2009 9:48 PM – Samples updated for Silverlight 3!

, , , ,

9 Comments

Logging Navigation in the Silverlight 3 Beta

I’ve been playing around recently with my webserver – I recently switched from a hosted ASP.NET service to a virtual dedicated server, so I’m getting a chance to play with having full control over my server for the first time.  I spent some time setting up logging and statistics on the server using IIS’s logging feature and some 3rd party log-crunching software.  Having that logging data is invaluable – among other things, it helps me know which pages folks are interested in and gives me insight into whether it’s too hard to reach certain sections of my page.  With the addition of navigation controls in the Silverlight 3 Beta SDK, having a logging solution that cooperates with my web server seems only prudent!

Tim Heuer has a great blog post from December about using event tracking with Google Analytics for Silverlight applications.  My impression is that this approach would work well with the new navigation controls, but I was looking for a simple solution that would add entries to my IIS logs straight from the Silverlight application (without having to add any javascript or use the HTML bridge).

The issue at hand is that the navigation controls use the URI fragment (text after the “#” sign in the URL) to determine which Page to navigate to in a Frame control.  As a result (and rightly so), deeplinks into the Silverlight control or navigation that occurs within the Silverlight control never round-trip to the server, so there is no way for the server to log their occurrence.

Hence, my approach is fairly straightforward: make an HTTP request back to the hosting server that it will log any time my Frame is navigated.

My requirements for the experiment:

  • Reuse the built-in logging features of my web server (IIS 7, but I imagine this would work more broadly)
  • Ensure that unique URIs within my application are logged individually (so query strings, custom URIs, etc. are not lost in the logged data)
  • Avoid limiting the application by requiring the HTML bridge to be accessible or requiring additional files to be added to my website (such as javascript files or additional ASP.NET Pages)

It turns out this isn’t so difficult to do!  I started with the Silverlight Navigation Application project template that ships with the Silverlight 3 Beta Tools for Visual Studio that came out at MIX.  This project template gets me set up with a Frame, some Pages, and some buttons that cause the Frame to navigate – everything a newborn navigation application needs to grow big and strong!

I began by handling the Frame control’s “Navigated” event in MainPage.xaml:

<navigation:Frame x:Name="Frame" Source="/Views/HomePage.xaml"
                  Navigated="Frame_Navigated"
                  HorizontalContentAlignment="Stretch"
                  VerticalContentAlignment="Stretch"
                  Padding="15,10,15,10"
                  Background="White"/>

With the easy part out of the way (who knew?), I started playing with WebRequest and WebClient until I came up with something that seemed to meet my needs:

private void Frame_Navigated(object sender, NavigationEventArgs e)
{
    Uri uri = new Uri(Application.Current.Host.Source.ToString() + "?nav=" + Uri.EscapeDataString(e.Uri.ToString()));
    WebRequest wc = WebRequest.Create(uri);
    wc.Method = "POST";
    wc.BeginGetResponse((res) =>
    {
        WebResponse wr = wc.EndGetResponse(res);
    }, this);
}

I’ll walk through this line-by-line and explain my thinking:

Uri uri = new Uri(Application.Current.Host.Source.ToString() + "?nav=" + Uri.EscapeDataString(e.Uri.ToString()));

Here, I needed to come up with a file I knew would be present on the server and that isn’t likely to have any semantics that I’ll be overriding by making a request.  In addition, I wanted to choose a file that would uniquely identify the Silverlight application that is making the request.  It seemed only logical, then to use the XAP file for my Silverlight app for this purpose!  Next, I added a query string that would be sent down to the server that would uniquely identify the URI that the Frame is using.  The result, if the source URI is “/Views/HomePage.xaml” (as is shown in the XAML), the resulting URI is: “http://yourservername.com/yourSilverlightApp.xap?nav=%2FViews%2FHomePage.xaml”.  Ok, ok, it’s not pretty (thanks to the encoding of the URI), but it does the trick.  The query string here never gets used, but it does get sent to the server and logged, which leaves me with exactly the traces I was looking for!

Next:

WebRequest wc = WebRequest.Create(uri);
wc.Method = "POST";

The important takeaway from these two lines is that I used the HTTP POST method.  I spent a bunch of time trying to use GET to make the logging happen, but it had two critical drawbacks:

  • Every request would re-download the XAP, which was far more data than I wanted to transfer just to get logging going
  • WebRequest and WebClient both use the browser’s cache (and I couldn’t find a workaround that didn’t involve modifying the query string, which would have scuttled my approach), so repeat-visits to the same Page in the Silverlight application didn’t ever actually reach the server and get logged

Finally:

wc.BeginGetResponse((res) =>
{
    WebResponse wr = wc.EndGetResponse(res);
}, this);

This just shoots off the web request.  There’s nothing particularly special here aside from noting that the response is entirely ignored.

 

And that’s it!

 

Here’s the proof, straight from my IIS logs (in W3C format… IP’s redacted):

2009-05-13 04:41:06 POST /Samples/LoggedSilverlightNavigation/ClientBin/LoggedSilverlightNavigation.xap nav=%2FViews%2FAboutPage.xaml – <IP Redacted> HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+6.1;+WOW64;+Trident/4.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+Media+Center+PC+6.0) – 405 1496

2009-05-13 04:41:06 GET / feed=rss2 – <IP Redacted> HTTP/1.1 Windows-RSS-Platform/2.0+(MSIE+8.0;+Windows+NT+6.1) – 304 475

2009-05-13 04:41:06 POST /Samples/LoggedSilverlightNavigation/ClientBin/LoggedSilverlightNavigation.xap nav=%2FViews%2FHomePage.xaml – <IP Redacted> HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+6.1;+WOW64;+Trident/4.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+Media+Center+PC+6.0) – 405 1496

2009-05-13 04:41:08 POST /Samples/LoggedSilverlightNavigation/ClientBin/LoggedSilverlightNavigation.xap nav=%2FViews%2FAboutPage.xaml – <IP Redacted> HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+6.1;+WOW64;+Trident/4.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+Media+Center+PC+6.0) – 405 1496

2009-05-13 04:41:08 POST /Samples/LoggedSilverlightNavigation/ClientBin/LoggedSilverlightNavigation.xap nav=%2FViews%2FHomePage.xaml – <IP Redacted> HTTP/1.1 Mozilla/4.0+(compatible;+MSIE+8.0;+Windows+NT+6.1;+WOW64;+Trident/4.0;+SLCC2;+.NET+CLR+2.0.50727;+.NET+CLR+3.5.30729;+.NET+CLR+3.0.30729;+Media+Center+PC+6.0) – 405 1496

And more proof, from my 3rd party stats tool (ignore the large transfer sizes… they’re artifacts of my testing the GET method, which transferred a few hundred KB with each request):

AWStats Screenshot showing LoggedSilverlightNavigation.xap being queried 

If you’re curious to download the code (it’s not much more than what you’ve already seen!), you can find it here: LoggedSilverlightNavigation.zip

There’s definitely still room for improvement, though.  One thing I’d like to get working eventually is setting the Referrer header on the HTTP request to a coherent value so that I can track how people get from page to page in my applications – but this is a good first step.

I hope you find that helpful!  If anyone has any other suggestions/tips/tricks for navigation, feel free to let me know!  This was just the result of my experimentation, so if you’ve got a better way, feel free to comment!

 

P.S. Still no correct responses to my Easter Egg hunt from my last post!  Don’t give up!  I’ll post the answer later this week.

P.P.S. Mark Monster has a great post from a few months ago on his blog about tracking Silverlight support in Google Analytics.  I imagine you could use a similar technique to accomplish the type of logging I do here.

, , ,

6 Comments