简体   繁体   中英

MVC4 mobile to dynamically use _Layout.cshtml instead of _Layout.Phone.cshtml

Please be gentle with me, this is my first question on StackOverflow following many years of lurking.

I have an MVC4 site already up and running. I am adding mobile support to the site which is all going well using the standard MVC functionality:

I need some code to allow the framework to use Layout.cshtml if there is no ".Phone" version of the view

Example 1

I have the following files:

Views/Home/HasBoth.cshtml
Views/Home/HasBoth.Phone.cshtml

Views/Shared/Layout.cshtml
Views/Shared/Layout.Phone.cshtml

Called with Desktop

  1. Call /Home/HasBoth
  2. Framework picks up is not mobile device
  3. Framework calls Layout.cshtml
  4. Framework calls HasBoth.cshtml

This is great.

Called with Mobile

  1. Call /Home/HasBoth
  2. Framework picks up is mobile device
  3. Framework calls Layout.Phone.cshtml
  4. Framework calls HasBoth.Phone.cshtml

Again, this is also great.

Example 2

I have the following files:

Views/Home/HasNoPhone.cshtml

Views/Shared/Layout.cshtml
Views/Shared/Layout.Phone.cshtml

Note: there is no Views/Home/HasNoPhone.Phone.cshtml

Called with Desktop

  1. Call /Home/HasNoPhone
  2. Framework picks up is not mobile device
  3. Framework calls Layout.cshtml
  4. Framework calls HasNoPhone.cshtml

This is great.

Called with Mobile

  1. Call /Home/HasNoPhone
  2. Framework picks up is mobile device
  3. Code here to work out there is no HasNoPhone.Phone.cshtml view
  4. Framework calls Layout.cshtml
  5. Framework calls HasNoPhone.cshtml

The above is what I want to acheive.

EDIT

Solution used

Thanks to Zoka, I am now using the following for anyone else who wishes to do something similar:

Helper method:

/// <summary>
/// Looks to see if the view the exists.
/// </summary>
/// <param name="viewName">The view name.</param>
/// <param name="controllerContext">The controller context.</param>
/// <returns>True if the view exists.</returns>
public static bool ViewExists(string viewName, ControllerContext controllerContext)
{
  ViewEngineResult result = ViewEngines.Engines.FindView(controllerContext, viewName, null);
  return result.View != null;
}

_ViewStart.cshtml :

@{                                                                                                             
  Layout = "~/Views/Shared/_Layout.cshtml";                                                                    

  // If a mobile viewing and no <view>.Phone.cshtml file is found set the override to desktop.                 
  // This will ensure _Layout.Phone.cshtml and yyy.Phone.cshtml partials are not called.                       
  string action = (string)ViewContext.Controller.ValueProvider.GetValue("action").RawValue;                    
  string controller = (string)ViewContext.RouteData.Values["Controller"];                                      
  string viewPhoneName = "~/Views/" + controller + "/" + action + ".Phone.cshtml";                             
  if (ViewContext.HttpContext.GetOverriddenBrowser().IsMobileDevice &&                                         
    MvcHelperAbove.ViewExists(viewPhoneName, ViewContext.Controller.ControllerContext) == false) 
  {                                                                                                            
    ViewContext.HttpContext.SetOverriddenBrowser(BrowserOverride.Desktop);                                     
  }                                                                                                            
}

_Layout.Phone.cshtml

...
@* Below is essential to allow css to work when going from Mobile to Desktop *@
<script>
  $(document).ready(function () {
    $.mobile.ajaxEnabled = false;
  });
</script>
...    

Thanks again all.

As I understand your question, you need to find out if some view exists. I use such function placed in any class accessible from views:

public static bool ViewExists(string _name, ControllerContext _controller_context)
{
    ViewEngineResult result = ViewEngines.Engines.FindView(_controller_context, _name, null);
    return (result.View != null);
}

Then put this into _ViewStart.cshtml:

string action = (string)ViewContext.Controller.ValueProvider.GetValue("action").RawValue;
string controller = (string)ViewContext.RouteData.Values["Controller"];
var viewPhoneName = "~/Views/" + controller + "/" + action + ".Phone.cshtml";
if (YourNamespaceAndClassForAboveHelper.ViewExists(viewPhoneName, ViewContext.Controller.ControllerContext)) {
    Layout = "~/Views/Shared/Layout.Phone.cshtml"
} else {
    Layout = "~/Views/Shared/Layout.cshtml"
}

And finally in Layout.cshml (respectively in Layout.Phone.cshtml), instead of call to RenderBody

string action = (string)ViewContext.Controller.ValueProvider.GetValue("action").RawValue;
string controller = (string)ViewContext.RouteData.Values["Controller"];
@RenderPage("~/Views/" + controller + "/" + action + ".cshtml"); @* .Phone.cshtml in Layout.Phone.cshtml *@

You could use an existing jquery mobile package:

http://www.hanselman.com/blog/MakingASwitchableDesktopAndMobileSiteWithASPNETMVC4AndJQueryMobile.aspx

Edit: Maybe you could use a page installer and register your pages in a container. That way your controller can ask your container which view he should return.

Creating WindsorViewPageActivator

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM