Archive for May, 2009

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

Playing around with Silverlight 3… a late Easter Egg hunt.

Ok, I admit it – I can be a little bit silly sometimes.  I saw some entertaining articles on some popular tech websites today (e.g. gizmodo.com) and was inspired.  Inspired, that is, to do something a little less… useful.  So, I quickly coded up a little Silverlight 3 application using Blend 3 Preview, Visual Studio, and a little too much time to myself.

Since this was a quick code-up job, I will apologize in advance for not snazzing it up very much.  Nonetheless, it does the trick.

So, here’s the challenge: can you figure out what this Silverlight application does?  By the way, no cheating!  Don’t spoil the surprise by cracking open the XAP or opening it up in reflector.  I promise I’ll reveal the answer soon.  In a few days, I’ll post the source (it’s tiny) and let you play around with it!

Note – running the app requires Silverlight 3, which you can get here.

Give it a shot, and leave a comment if you figure it out!  The app follows…

Get Microsoft Silverlight

 

Update: 5/19/2009 1:22 AM: I have revealed the secret of the app!  Take a look!

Update: 7/11/2009 9:45 PM: Samples updated for the release of Silverlight 3!

, ,

5 Comments