| Comments

[previously named "Silverlight as the V in ASP.NET MVC" but changed per comments]

One thing that I’m excited about is learning new technologies.  Moving to the Silverlight team, I’ve moved away from a breadth of technology knowledge to something a bit more narrow.  Now I feel like all other developers trying to keep up with the technologies we are releasing.  As such, I’m a beginner for most.  One such technology is ASP.NET MVC, which was just released to release candidate stability.

I thought I’d play around with it in the context of Silverlight and use Silverlight as the “view” in the model-view-controller concept.  It’s easy to link the two.  In fact when you create a new Silverlight project, you now have the option of creating an ASP.NET MVC application as the host:

So right from the beginning, you can marry the two together.  Now from here, how can we leverage Silverlight as a view.  Well, here’s my take…learn with me (and comment where you’d do it differently/better and why).

First, I’m still going to create my MVC architecture.  I’m using the Northwind database for simplicity sake in this learning task.  I’ve created a LINQ to SQL data model to that database (which is a local SQLExpress instance).  I then wanted to take the simple task of showing the products by category and displaying them in a simple Silverlight application.

NOTE: Yes, this Silverlight view is basically just a layout of ListBoxes, but remember, this is just a learning experiment for us.  You may also ask yourself about Authentication/Authorization…again, this post is about an experiment and not a full-featured implementation, so there are bound to be missing pieces.

I decided to create a CategoryController and a ProductController which would handle the actions to retrieve list of categories and products and then drill into the detail of a product.  From there I still need some web View to be a container for my real view, the Silverlight application.  I created a View for Category, since essentially that’s the initial view my user would see.  All it is is an index page hosting my Silverlight application:

   1: <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="SilverMvc.Web.Views.Category.Index" %>
   2: <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
   3:     <p>
   4:         <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
   5:             <param name="source" value="ClientBin/SilverlightWithMvc.xap"/>
   6:             <param name="onerror" value="onSilverlightError" />
   7:             <param name="background" value="white" />
   8:             <param name="minRuntimeVersion" value="2.0.31005.0" />
   9:             <param name="autoUpgrade" value="true" />
  10:             <a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;">
  11:                  <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
  12:             </a>
  13:         </object>
  14:     </p>
  15: </asp:Content>

Now all I have to do is start building my real view that will be here, the SilverlightWithMvc.xap application.  But first I obviously need my controllers to respond so some requests.  I wanted the CategoryController to display a list of categories and a list of products associated with a category.  But remember, my View will not be the ASPNET View, but rather my Silverlight app.  How then could I get the ViewData to be sent to my Silverlight application.

In MVC, typically your code will return a View from the action, similar to my Index view in my CategoryController:

   1: public ActionResult Index()
   2:         {
   3:             return View();
   4:         }

See where the return is an ActionResult (common) and the return is a View, also common.  This return would expect that there would be a View named Index in the project hierarchy, which in our case there is and again, is the initial hosting page for our app (located in /Views/Category/Index.aspx).  But what about our other data.  I wanted to add a controller action called List and Products that would list all the categories (List) and then list all the products for a given category (Products).  But I just wanted the data.  It turns out that the MVC framework can give us just the data instead of the View.  There is a Json return type.  So using my LINQ queries I created the two actions:

   1: public ActionResult List()
   2:         {
   3:             NorthwindDataContext db = new NorthwindDataContext();
   4:             var cats = from cat in db.Categories
   5:                        select new
   6:                        {
   7:                            cat.CategoryID,
   8:                            cat.CategoryName
   9:                        };
  11:             return Json(cats);
  12:         }
  14:         public ActionResult Products(int id)
  15:         {
  16:             NorthwindDataContext db = new NorthwindDataContext();
  17:             var prods = from prod in db.Products.Where(cat => cat.CategoryID == id)
  18:                         select new
  19:                         {
  20:                             prod.ProductID,
  21:                             prod.ProductName,
  22:                             prod.UnitPrice,
  23:                         };
  24:             return Json(prods);
  25:         }

As you can see the return type is Json and passing in the model data of what I need…as opposed to saying return View([modelData]).  I now get Json formatted data as a return result.  Great, now how to consume them?

Within Silverlight, we know we can consume Json data and we can take advantage of that capability to bring MVC and Silverlight together.  First let’s look at my layout UI code for the Silverlight application…it’s basically going to be a cascading ListBox view:

   1: <UserControl x:Class="SilverMvc.Page"
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   4:     <Grid x:Name="LayoutRoot" Background="White">
   5:         <StackPanel Orientation="Horizontal">
   6:             <StackPanel Orientation="Vertical" Margin="0,0,25,0" x:Name="CategoryListing">
   7:                 <TextBlock Text="Select a category..." FontWeight="Bold" />
   8:                 <ListBox Width="200" Height="150" x:Name="CategoryList" ItemsSource="{Binding}" SelectionChanged="CategoryList_SelectionChanged" />
   9:             </StackPanel>
  10:             <StackPanel Orientation="Vertical" Margin="0,0,25,0" x:Name="ProductListings" Visibility="Collapsed">
  11:                 <TextBlock Text="Select a product..." FontWeight="Bold" />
  12:                 <ListBox Width="200" Height="150" x:Name="ProductList" SelectionChanged="ProductList_SelectionChanged" />
  13:             </StackPanel>
  14:             <StackPanel Orientation="Vertical" x:Name="ProductDetail" Visibility="Collapsed">
  15:                 <TextBlock Text="Product Details:" FontWeight="Bold" />
  16:                 <Grid>
  17:                     <Grid.RowDefinitions>
  18:                         <RowDefinition Height="Auto" />
  19:                         <RowDefinition Height="Auto" />
  20:                     </Grid.RowDefinitions>
  21:                     <Grid.ColumnDefinitions>
  22:                         <ColumnDefinition Width="Auto" />
  23:                         <ColumnDefinition Width="Auto" />
  24:                     </Grid.ColumnDefinitions>
  25:                     <TextBlock Grid.Column="0" Grid.Row="0" Text="Product Name: " />
  26:                     <TextBox Width="200" Grid.Column="1" Grid.Row="0" Text="{Binding Path=ProductName, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" HorizontalAlignment="Left" VerticalAlignment="Top" />
  27:                     <TextBlock Grid.Column="0" Grid.Row="1" Text="Unit Price: " />
  28:                     <TextBox Width="200" Grid.Column="1" Grid.Row="1" Text="{Binding Path=UnitPrice, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}" HorizontalAlignment="Left" VerticalAlignment="Top" />
  29:                 </Grid>
  30:             </StackPanel>
  31:         </StackPanel>
  32:     </Grid>
  33: </UserControl>

Where one selection drives the next portion of the layout, etc.  You can see that I have several {Binding} statements in there as well as some selection changed handlers.  Let’s look at what happens on Loaded of the app:

   1: void Page_Loaded(object sender, RoutedEventArgs e)
   2:         {
   3:             WebClient mvc = new WebClient();
   4:             mvc.OpenReadCompleted += new OpenReadCompletedEventHandler(mvc_OpenReadCompleted);
   5:             mvc.OpenReadAsync(new Uri("http://localhost:33828/Category/List"));
   6:         }

You can see that I’m making a WebRequest call to a URI that happens to be our CategoryController with the List action command.  On the return event handler I’m getting that stream of data, which we know to be Json data, and setting the DataContext of my first ListBox:

   1: void mvc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
   2:         {
   3:             DataContractJsonSerializer json = new DataContractJsonSerializer(typeof(List<Category>));
   4:             List<Category> cats = (List<Category>)json.ReadObject(e.Result);
   5:             CategoryList.DisplayMemberPath = "CategoryName";
   6:             CategoryListing.DataContext = cats;
   7:         }

Now you may at this point be asking a few questions about some of my decisions here within Silverlight.  First, the use of DataContractJsonSerializer.  Where is it?  Add a reference to System.ServiceModel.Web and you’ll get it.  You can also see that I’m using the ReadObject method and casting it to a List<Category>. 

Why not use System.Json and LINQ to JSON?  You could absolutely.  In doing so you could use your LINQ skills and get the data out of the Json stream and put it into a new object.  You’ll still have to create a local class representation because Silverlight can’t bind to an anonymous type.  This is the first reason I like just using the serializer.  The second reason is size.  I don’t know why I’m so picky, but I am.  Using the DataContractSerializer method here, my app is about 7K.  Adding a reference to System.Json and using those methods, my app is 27K.  For me, there is no additional benefit in code for what I’m doing to add that extra size, so I choose not to.  But you can…absolutely.  Your application needs may be different and the size cost/benefit analysis may result in a different outcome than mine…but there, I’ve stated my reasons here.

Where is Category defined?  Great question!  Category is a class defined in my Silverlight object that maps the data to a strongly-typed class..here’s the class definition:

   1: public class Category
   2:     {
   3:         public int CategoryID { get; set; }
   4:         public string CategoryName { get; set; }
   5:     }

Now that that is done, my data is retrieved, typed and bound to my UI.  Then in the category ListBox when a category is selected, we trigger a similar event to retrieve the products within that category and populate the product list using the same technique.  The final stage is that when a product is selected we populate a simple UI to show the details.  We could also have called another controller action, but here since we had most of the data in the product listing, we simply push the data to the DataContext of the layout container for the details:

   1: private void ProductList_SelectionChanged(object sender, SelectionChangedEventArgs e)
   2:         {
   3:             if (ProductList.SelectedIndex > -1)
   4:             {
   5:                 Product p = ProductList.SelectedItem as Product;
   6:                 ProductDetail.DataContext = p;
   7:                 ProductDetail.Visibility = Visibility.Visible;
   8:             }
   9:         }

You can see we get the SelectedItem as a Product (another internal class like Category) and make it the DataContext of the ProductDetail StackPanel who’s children have binding instructions.  The end result is this “view” below (the headings above the list boxes are not in the UI, but rather just labels to map to the controller action used to populate:

As I completed this little experiment, several things did come to mind…

  • What about deep linking?  The URI in the app didn’t change.
  • Now that I’m looking at a detail view, does the URI stating the initial view matter?

I think mostly these are “simple” things, but important to an MVC developer audience.  The interaction of URI semantics with Silverlight are important and things we are addressing in Silverlight 3 to make even this simple experiment easier.

So tell me, ASP.NET MVC experts, does this make sense?  Good/bad/what would you do different?  Help me learn how you would make the interaction between ASP.NET MVC and Silverlight better?

You can download my sample code for this project here (requires SQL 2008 and VS2008 with Silverlight and ASP.NET MVC tools installed): SilverlightWithMvc.zip

| Comments

One of the nice little additions to the data client services in Silverlight 2 is the removal of the need to drop out of Visual Studio to do some proxy code generation.  Prior to Silverlight 2 release, if you wanted to consume an ADO.NET Data Service (the artist formerly known as Astoria), you had to drop into a command line and execute something like:

   1: datasvcutil.exe /out:"MyDataService.cs" /uri:"http://foo.com/MyDataServiceEndpoint.svc"

While that isn’t difficult, it just wasn’t convenient as a developer productivity workflow.  I mean who wants to have to click and type more than you have to, right?  After all, ADO.NET Data Service (alright forget it, I’m calling it Astoria) endpoints are services right?  And Visual Studio does have this thing called Add Service Reference?

Alas, now we don’t have to use a command-line anymore.  With Silverlight 2 and Visual Studio tools, you now can use the service reference capability I’d like to show you a screenshot, but it’s not like the dialog window is any different than other service references, so I’ll save me and you the bandwidth.  But try it out.  Take your project, add a reference to your Astoria service.

| Comments

Man, I feel like I’m constantly on this soapbox about creating a good non-installed experience.  I’m recently sparked again by reading a recent post from Pascal Leloup entitled “Silverlight – It’s too damn complicated to setup.”  These are the times that I wish I could sit next to every developer at the moment they experience this frustration.

Part of the time it can be dealing with beta software (as is the case in one of Pascal’s scenarios here), and others I think just take some time to understand the why behind certain things.  Let me take a stab at some things here for Pascal’s (and hopefully yours too) benefit.  While his specific example was around a media player, some of these concepts go far beyond that.

ASP.NET Media Control

If you use the ASP.NET Media Control (<asp:MediaPlayer/>) you get some ease of being able to just drop it in, attach a media source (WMV/WMA/MP3) and you get a player.  One thing to note though is that the output is a Silverlight 1.0 application, not a Silveright 2 experience.  The scripts and templates for the ASP.NET Media Control are embedded resources.  You can certainly supply your own skin, but have to create it.  The documentation for the control are available here where you can see how it can be used, how you can use Javascript to program against it, and how you can use various MediaSkin files or create your own.  In my honest opinion, this is a good start, but will not likely get you to a customized user experience for the media playback experience.  Additionally, you can customize the ‘not installed’ experience as it exposes the PluginNotInstalledTemplate property that you can put whatever you want there.

Pascal says:

“…they have to make sure the Media player ASP.Net user control continue to be updated or maybe put it back to the Ajax Control Toolkit as an open source project, to be sure the community can improve it.”

I will be honest and say I’m not sure if the skin thing with using Javascript will be changing by Silverlight 2 timeframe…but the necessary scripts, etc. are continually being updated.  As I’ve said (sorry ASP.NET team), I think if you are looking for a highly customized experience, this may not be the control for you.


I’m assuming that Pascal probably is a Firefox user and the initial use of the script was continuing to show the install badge logo.  Based on the comments, it sounds like really outdated tools.  The script available on MSDN (which is the latest) has the proper install links and also fixes the problem with Firefox 3 detection.

Keep in mind though this is just a script.  It isn’t going to automatically do everything for you.  It should be considered an SDK tool and help you do some detection and provide alternate content to the user if the plugin is not installed.

Silverlight control

I assume here Pascal means the <object> tag and thus a Silverlight 2 application.  It looks like for the most part he got this working (with some media problems that I’m happy to try to resolve; oh and Pascal the media stretch fix has been checked in to the Codeplex project).  As to the Firefox 3 ‘official’ support…Silverlight 2 is still in beta and we’ve already said that we’ll be supporting Firefox 3.

Pascal comments:

“The other matter is to get a much cleaner way to implement easily Silverlight 2 on an ASPX page. Why not doing something like the Listview, with a template part where we can put our own installation links?”

Actually you can.  There are two ways you can do this, but let me show you a snippet, first using the ASP:Silverlight control:

   1: <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/_SilverlightApplication1.xap" 
   2:         MinimumVersion="2.0.30523" Width="100%" Height="100%">
   3:     <PluginNotInstalledTemplate>
   4:         <a href="http://go.microsoft.com/fwlink/?LinkID=115261" style="text-decoration: none;">
   5:             <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
   6:         </a>
   7:     </PluginNotInstalledTemplate>
   8: </asp:Silverlight>

Which essentially would be the same as:

   1: <object data="data:application/x-silverlight," type="application/x-silverlight-2-b2" 
   2:     width="100%" height="100%">
   3:     <param name="source" value="ClientBin/_SilverlightApplication1.xap"/>
   4:     <a href="http://go.microsoft.com/fwlink/?LinkID=115261" style="text-decoration: none;">
   5:         <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
   6:     </a>
   7: </object>

So you can see that ASP.NET does provide a method for putting Silverlight applications there using the ASP:Silverlight control and also exposes the PluginNotInstalledTemplate object for you to customize.  In the non-ASP.NET example, you can see that the alternate content is provided after the last <param> tag in the <object> tag.  What will happen here is that if the <object> tag is not understood, it will render the HTML it can understand.  You can see more about this on this screencast.

“Big Huge Icon”

Pascal notes:

“They automatically put a big huge icon in your face without any explanation at all to tell your grandmother what the hell to do with this bluish Silverlight logo? Super.. what?  I’d loved to add my own touch, an help button, a new logo, no hope.”

On the last sentence of no hope to add your own touch, he couldn’t be further from the truth…more on that in a second.

On the first note: I couldn’t agree more!  I get frustrated when I see the Get Microsoft Silverlight install badge.  But the challenge is that it is the best default for people who aren’t customizing their experience.  We could have come up with a super creative, very descriptive image, but what if the Silverlight content is only 10x10px?  As it stands now, the default image wouldn’t look good in that scenario!

The point here is that the developer must own the install experience.  Silverlight isn’t on every desktop, let’s be honest.  The focus in the ‘not installed’ experience should be your content/company and not Silverlight.  It is important to keep the Silverlight branding at some level because once they click install it will show them the Silverlight installer and you want to keep continuity in the message of what they are installing.  Outside of that though, you own that space.  Maybe I should lobby to change the Visual Studio templates to add comment code instead of the default badge so that it reads:


I’ve already written about some good examples of doing this.  It was even pointed out in the video player Codeplex project that I participate in, that we didn’t have guidance on that.  I cringed a little bit to even provide a sample, because I really believe the web site should own that experience…but I at least put a sample in the documentation.

When Silverlight ships we’ll be providing an updated installation guidance document talking about the various ways you can do this, with some clear samples of the SDK methods of detection of upgrades, non-installs, browser refresh/restarting, etc.  The one thing the document won’t do, however, and that is provide you that alternate HTML.  Why?  Because the non-installed experience is about your content, not Silverlight.  Imagine how you feel now when you see the “big huge icon” – it doesn’t make sense right?  Neither will it to your customers if you don’t provide them with information.  And don’t ask Microsoft to provide the samples…why would you want Microsoft to design/describe the content from your organization?


Sorry to keep repeating, but it’s about the content, not Silverlight.  Content is king.  If you are looking for a gallery of ‘not installed’ template experiences, stop looking – it doesn’t make sense.  Your brand in your application/site/organization is important and won’t be found in any templates.  Even other rich platforms don’t provide custom templates.

Be sure to take time to add this as a work item to your projects using Silverlight…don’t forget about it.  This is an important step in early adoption of your content in Silverlight and you should take time to nail that experience.  As I mentioned, we’ll provide updated guidance on any new settings/APIs that you can tap into to make the most out of that experience and minimize the impact to your end-user.

Related Resources:

| Comments

Well, I wish I was going to this event: ReMIX UK!  For one, it would be starting the day of my birthday and would be a great birthday treat!  Secondly, I would be able to meet up with my fellow escamoles chaps and maybe take in some local cuisine instead.

If you are in the UK, or for some reason really wanted to see the current value of the US dollar, make sure you head to ReMIX UK!  The lineup is pretty incredible when you look at it…here’s some Microsoft favorites:

I also saw on the agenda for the 2 day event that Tim Sneath and Jesse Liberty were listed, so it should be even richer (pun intented) content.  In addition it looks like some great stuff on the designer and creative strategy side of the business as well.  I’m sure there will be enough Silverlight stuff there to keep you going as well as WPF, ASP.NET and others!

By the way, I listed Dr. Neil above as one of my Microsoft favorites.  He’s actually one of our Regional Directors.  If you have never met Dr. Neil…do so.  He’s a great guy, very intelligent and fun to be around.  Ask him about how he manages developer teams and the ‘check-in’ metaphor for people, not just code.  Seriously, he would be a not miss either.

I wish I could travel across the pond for this one, but I’ll be hopefully approximately 25 meters under water in some area of Mexico celebrating with friends.  Have fun though!

| Comments

Stephen WaltherAre you poking around with the latest ASP.NET MVC (Model-View-Controller) bits?  If so, hopefully you are already aware of the resources available to you and the fact that Phil Haack is one of the program manager’s leading those efforts, and Scott Hanselman has been providing the community with some resources to jump start your learning.

Well, another member has just joined the ranks on the team I work for.  Stephen Walther, pictured here in his best book-cover pose ;-), has joined the team.  He actually joined earlier this month, but I’m just now getting around to welcoming him to our team and checking out the stuff he’s been doing.  He’s been on a great clip lately and I’ve been reading his latest 11 tips on ASP.NET MVC on his blog.  I’m really glad to have him on our team as I think we’ve got some great resources for ASP.NET, AJAX, MVC, Silverlight and Windows Client.

If you are messing around with the ASP.NET MVC framework, check out his site and subscribe.  We’ll be seeing a lot more of Stephen soon and I’m sure your ideas will be welcome to him as well!