Skip navigation
desk with coffee tablet open notebook ruler and colored pencils

Use ASP.NET MVC to Architect Device-Driven Website Solutions

Reject the mobile-desktop dichotomy and start building web solutions for families of devices

The term "mobile" is ubiquitous among software developers, who typically use it to describe a version of a website that's similar to yet distinct from the desktop version of the website. Traditionally websites have been built for the desktop first and for mobile devices second. However, today's consumers don't see such a distinction between desktop and "everything else." Today everything is a device, be it a powerful desktop PC, notebook, smartphone, tablet, or smart TV. And the future will bring an ever-expanding array of devices—think of the emerging market for wearable devices.

To successfully craft website solutions for your business that provide a device-specific user experience, your first challenge is to stop using the term "mobile" and, even more importantly, to liberate yourself from the notion of a mobile-desktop dichotomy. You will need to consider carefully how you'd go about architecting a web solution that can work well on a variety of devices. Here I will discuss some of the key elements you'll need to address in developing a website that provides a high-quality user experience regardless of the device used to access the site.

It's All About the Screen Width

When it comes to serving distinct content to different devices, the most important parameters are the screen width and the operating system. Twitter Bootstrap and its responsive Cascading Style Sheets (CSS)-based machinery allow you to switch to different page layouts according to the screen size. If you use Twitter Bootstrap in your layout, you get automatic content reflow at predefined layout switch points. Also known as a breakpoint, a predefined layout switch point is just a screen width beyond which you intend to switch to a different CSS layout. Twitter Bootstrap sets its default breakpoints at 768px, 990px, and 1200px. These breakpoints correspond to four different logical devices: extra small, small, medium, and large.

You can overwrite default Bootstrap breakpoints as well as create your own CSS media queries' expressions and set screen breakpoints. In both cases, the layout associated with that screen width is automatically selected by the browser. Note that the browser looks only at the width parameter and has no way to look at other significant information about the device. A responsive page, once it is hosted in, say, a 480px-wide window, renders the same regardless of whether it's an Android browser window or an Internet Explorer (IE) browser window resized to 480px on a PC running Windows 8.

Related: Mobile Web Development: Responsive Web Design Is Key

When using a responsive approach, your first step is to determine your primary layout. (And if your primary layout is optimized for some mobile devices, you can even claim you're doing mobile-first development!) Any adaptation to a specific width is carried out by the CSS in media queries or automatically by Bootstrap if you're using it. That's the most you can achieve with CSS and without writing code: You can change the layout, but only to some extent.

Changing Layout and Use Cases

In building websites that work well across a variety of devices, we need to implement the responsive web design concept of a breakpoint in a broader scope. Instead of a family of screens, you should think of a family of devices. Devices in a given family share features such as the screen width, touch and HTML5 support, and possibly the operating system. Thinking in terms of your website's supporting a family of devices lets you, for instance, distinguish between iOS-based tablets and Android tablets, if doing so is important to your business.

In addition, a web solution that's architected for multiple devices offers an advantage over simply using Bootstrap to adapt your website to different devices: It gives you the opportunity to customize the graphical page layout at will. That is, you are not simply reflowing the UI by repositioning or hiding blocks. Rather, you are creating a solution that provides a completely different UI and possibly new use cases and functionality for each family of devices. At the end of the day, a web solution architected for multiple devices differs from a solution that relies on Bootstrap in the following ways:

  • You can define families of devices using any capability you can access (i.e., OS and hardware/graphical features).
  • For each family of devices, you can define an ad hoc set of views and, if needed, an ad hoc application layer.

Some infrastructure is required for building a multi-device web solution. You need a view router mechanism that, once it is informed about the requested view, manages to load and display exactly that. Second, you need some software tools that tell you more about the requesting device.

The View Router Mechanism

Since version 4, ASP.NET MVC has a built-in mechanism to support multiple views. To be precise, it's all about offering multiple display modes for the same logical page. In the ASP.NET MVC jargon, a display-mode is a class characterized by a nickname and a Boolean expression. The nickname is interpreted as the suffix to append to the Razor view file to indicate the specific version to be used when the Boolean expression evaluates to true.

By default, any ASP.NET MVC application supports two display modes. One is the default view and is characterized by an empty nickname and Boolean expression that always returns true. The second is the mobile view and is bound to the nickname "mobile" and a Boolean expression that returns true, as shown in the following sample expression:

return Request.Browser.IsMobileDevice()

Note that the IsMobileDevice expression is not really reliable and might not be able to catch latest devices. In any case, the internal code works by matching the user agent string against the content of a bunch of *.browser files that ASP.NET uses for (desktop) browser detection. As a result, when the index view is requested, if the browser is detected as mobile, a Razor file named index.mobile.cshtml is searched; otherwise index.cshtml is served.

In your code, all you need to do is to clear up predefined display modes and add your own display modes. You define a display mode for each family of devices you intend to support. After that, for each view expected in the web solution you provide specific Razor files—one for smartphones, one for tablets, and so forth. Listing 1 shows the code you need to support smartphones, tablets, and desktop devices.

public class DisplayConfig
{
        public static void RegisterDisplayModes(IList<IDisplayMode> displayModes)
        {
            var modeDesktop = new DefaultDisplayMode("")
            {
                ContextCondition = (c => c.Request.IsDesktop())
            };
            var modeSmartphone = new DefaultDisplayMode("smartphone")
            {
                ContextCondition = (c => c.Request.IsSmartphone())
            };
            var modeTablet = new DefaultDisplayMode("tablet")
            {
                ContextCondition = (c => c.Request.IsTablet())
            };
            displayModes.Clear();
            displayModes.Add(modeSmartphone);
            displayModes.Add(modeTablet);
            displayModes.Add(modeDesktop);
        }
}

You place the following code in global.asax because display modes must be defined at the startup of the application.

DisplayConfig.RegisterDisplayModes(DisplayModeProvider.Instance.Modes);

The Boolean expression is a delegate that accepts an HttpContext object and returns a Boolean. A few extension methods have been used to keep the code simpler. For example, IsTablet takes the following form:

public static Boolean IsTablet(this HttpRequestBase request)
{
       return JustTellMeIfThisIsTablet(request.UserAgent);
}

Let's hold off for a moment on discussing the nature of the code that tells whether or not the current request is coming from a tablet. With the display mode engine in place, here's what happens when a request comes in:

  1. The request is processed by the controller as usual.
  2. The controller ends by calling the View method to render the specified view. The controller just calls out the generic name of the view—something like "index."
  3. The display mode infrastructure kicks in and determines the most appropriate display mode. It loops through the list of registered modes until it finds one whose context condition returns true. Next, it takes the mode's nickname and appends that to the view name.

If the requested view name was index and, say, the matching mode was tablet, the actual view is expected to be index.tablet.cshtml. If no such a view exists, the view name then reverts to simply index.cshtml. It should be noted that index.tablet may be a totally different view from index, providing a different layout and different use cases and likely requiring different view models. (I'll return to discussing view models shortly.)

Device-Detection Tooling

If you want to serve different views, you need a framework or something that tells you when you're about to handle a tablet or a smartphone request. Device detection requires parsing the user agent, as the user agent string is the only piece of information that browsers will definitely send in. Parsing the user agent string can be a nightmare, and I strongly recommend that you get a tool to handle this task. One such tool is WURFL, a NuGet package that you can add to your ASP.NET project. WURFL provides a couple of classes to parse the user agent string into a dictionary of device capabilities. To use WURFL, here's the code you need to have in the aforementioned IsTablet function:

return device.GetCapability("is_tablet").ToBool();

In the code, the variable device is of type IDevice, the type that the WURFL library uses to describe the recognized device. You can use the WURFL library free of charge in a test environment, but you must obtain a commercial license to use WURFL when the site goes in production unless yours is an open source project. WURFL is also available in a cloud version with moderately priced subscription plans. To find out more about WURFL, go to scientiamobile.com.

Organizing Multiple Views

When you set up a multi-device solution, in which each device gets its own view, the ASP.NET controller doesn't strictly need to change and may remain as follows:

public ActionResult Index()
{
    var model = GetIndexViewModel();
    return View(model);
}

However, what do you do when the view model is different for each display mode? What if you need to fill and pass to the rendering engine a different view model class per device? Specific views will be bound to their own view model type, but what is the most effective way to fill and pass different view models?

At some point you need to have a sequence of if statements or a proliferation of classes. An option is to edit the controller factory of ASP.NET MVC and instantiate a different controller per device—SmartphoneHomeController, TabletHomeController, and the like. Each controller has its own application layer, or the application layer can be just one that sums together all functions from all device use cases. Another approach keeps the number of controllers to the bare minimum but adds some extra logic to the application layer:

public ActionResult Index()
{
   vsr deviceId = GetRequestingDeviceId();
    var model = GetIndexViewModel(deviceId);
    return View(model);
}

In addition to passing all parameters requested to calculate the view model, you also pass a value that selects the device ID. Internally, the application layer uses a switch or a bunch of if statements to fill up the correct view model class, as shown in Listing 2. Needless to say, SmartphoneIndexViewModel and TabletIndexViewModel inherit from IndexViewModel. Each view will simply reference the type it needs.

public IndexViewModel GetIndexViewModel(int deviceFamilyId = 0)
{
    if (deviceFamilyId == 1)
        return new SmartphoneIndexViewModel {…};
    if (deviceFamilyId == 2)
        return new TabletIndexViewModel {…};

    return new IndexViewModel();
}

If the logic in the application layer grows too big, you could always refactor it to device-specific classes. On the other hand, tablet or smartphone views—especially when they're significantly different from one another—fully justify the existence of a separate application layer.

It's All in the Details

Let's face it: You can simply use Twitter Bootstrap in your website and be happy. But if you do so, you must also acknowledge that this type of web solution is not architected to support multiple devices. It just works and renders well, at the moment, on a number of different devices. As soon as you need to have, say, a different tablet interface only on iPad, you'll be stuck. At that point, you should use an approach like the one I've described in this article and first and foremost get to know as much as possible about the requesting device. To offer a web solution to multiple devices with a unique experience, you can no longer ignore the device details.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish