Friday, March 2, 2012

ASP.NET MVC 4 (Part 2 - Mobile Features)

In its newer release ASP.NET MVC has brought better support for mobile devices. How so?



Well, first of all a new template is included that creates a web-site targeted for mobile devices.


Let's create a new project based on this template, run it and open it in a Windows Phone emulator.
The result should be something like this:


The demo web-site is pretty nice and includes authentication, navigation between some pages, all inside a package that really feels "mobile" and "touchy".

Looking at the generated project we can see that it's using jQuery Mobile which is, in my opinion, a really nice move, as it's rapidly becoming the standard in mobile web-applications development. If you haven't tried out jQuery Mobile you're missing out on something really special.


Anyway, this template is nice and all, but doesn't really use anything specific of ASP.NET MVC 4.

I'm much more interested in trying out another MVC 4 feature: "Browser-Specific Views". What this allows is to automatically use different views according to the target browser/device. I'll later explain how this works, but first let's create a simple web site to demonstrate this feature.
  • Empty Site template
  • Create HomeController
  • Create View "Index.cshtml" inside folder /Views/Home
  • Just add a simple "h1" tag. 
The end-result couldn't be simpler:



Now let's create a browser-specific view.
Out-of-the-box, MVC 4 brings support for a "mobile" display mode.
Let's copy our existing view and create a new one named "Index.mobile.cshtml".


Let's change it's text to say something like: "My mobile web page".

Now let's open the same url using the Windows Phone Emulator. The result should be similar to the one on the left.

As you can see the "index.mobile.cshtml" view was chosen, and rendered accordingly.






Let's return to the "how does this work" question. Well, basically MVC now includes the concept of something called an "IDisplayMode". An IDisplayMode is just something that defines a condition (typically based on the user-agent) with an associated name.

the workflow is like this:
  • Determine DisplayMode to choose based on their conditions
  • Pick View named <view><display mode>.cshtml.


As I've said, "mobile" is a displayMode included in MVC 4. Let's confirm this.

Add the line "var existingModes = DisplayModeProvider.Instance.Modes;" to global.asax.cs:
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    var existingModes = DisplayModeProvider.Instance.Modes;

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    BundleTable.Bundles.RegisterTemplateBundles();
} 
Place a breakpoint after the inserted command and run the project in debug-mode.

We can see that we have 2 modes, one for mobile and the default one. What MVC does is iterate this array and return the first Display Mode that matches the specified condition. So, the order on which they're declared is important. For example, suppose you want "Android" custom views, you would have to insert them in the DisplayModes Array before the "Mobile" display mode, or it would never be chosen.

So, how do we extend this behavior for our custom display modes?

It's just as simple as:

DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("Foo")
{
    ContextCondition = ...
});
Where the condition determines if this display mode should be chosen. Some examples:

Android:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("android")
{
    ContextCondition = context => 
        context.GetOverriddenUserAgent().ToLower().Contains("android")
});

iPhone:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iphone")
{
    ContextCondition = context => 
        context.GetOverriddenUserAgent().ToLower().Contains("iphone")
});

Also, the context.GetOverriddenBrowser() method provides a lot of information regarding the browser capabilities, allowing for some more specific scenarios like:
Browser supports Java applets:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("applets")
{
    ContextCondition = context => context.GetOverriddenBrowser().JavaApplets 
});  
Really, really nice stuff if you ask me.

No comments:

Post a Comment