Question

OK I have inherited this code with a custom view engine. All it does is override FindView in order to return different views based on the device. Currently, iPad is detected as a mobile device and hence, mobile view is displayed. My task is to change this to desktop view.

There are two master pages X.master and X.Mobile.master. From the code, you can see that it manually adds .Mobile to the master page name for mobile devices. All I have done is setting isMobile to false for iPad.

The problem is that even though it goes to else statement with masterName being X (rather than X.Mobile), when viewing with an iPad, the view which is returned has X.Mobile.Master as its master page. So for iPad, it ends up showing the Desktop View with Mobile Masterpage. Desktop and Mobile versions are all fine. Where (and how) could this possibly happen?

A sample action method:

public ActionResult Index()
{
  return View("Index", "X");
}

Here's a simplified version of viewengine:

class MobileViewEngine : FixedWebFormViewEngine
{
  public override ViewEngineResult FindView(
    ControllerContext controllerContext,
    string viewName, 
    string masterName, 
    bool useCache
 )
  {
    ViewEngineResult result;
    bool isMobile =
        controllerContext.HttpContext.Request.Browser.IsMobileDevice;

    if (isiPad(controllerContext.HttpContext.Request))
    {
      isMobile = false;
    }

    if (isMobile)
    {
        masterName = masterName + ".Mobile";
        string viewPathAndName = "M/" + viewName;

        //Mobile view retrieved from cache
        result = base.FindView(
            controllerContext,
            viewPathAndName,
            masterName,
            true
        );

            if (result == null || result.View == null)
            {
                //Mobile view retrieved, no cache
                result = base.FindView(
                    controllerContext,
                    viewPathAndName,
                    masterName,
                    false
                );
            }
    }
    else
    {
            //desktop view retrieved
            result = base.FindView(
                controllerContext,
                viewName,
                masterName,
                false
            );
    }
  }
}
Was it helpful?

Solution

OK so after looking at ASP.NET MVC (thanks god for open source), I can see that when a request is marked as mobile (controllerContext.HttpContext.Request.Browser.IsMobileDevice == true), MVC tries viewname.mobile.cshtml (or .aspx) by default before trying the viewname.cshtml. The same goes for masterpage.

In my case, because there was indeed a masterpage.mobile, it was being rendered. As for the view, because the mobile version was in a separate folder, it was not being picked up and it was falling back to the normal viewname.cshtml.

This whole investigation took me a bit and that has motivated me to share my findings in more details in my blog.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top