Pages

Display device specific views in MVC 3


Display device specific views in MVC 3

In MVC 3, view can be render based in requested device, each request come with browser type string called “User-Agent” string.
Example of user-agent sting as below
Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Mozilla/5.0 (BlackBerry; U; BlackBerry 9850; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0.115 Mobile Safari/534.11+
BlackBerry9700/5.0.0.862 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/167
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; Media Center PC 6.0; InfoPath.3; MS-RTC LM 8; Zune 4.7)
To override default behaviour of MVC rendering engine, create a class with inherit from RazorViewEngine as below;
    Internal class MobileViewEngine : RazorViewEngine
    {
        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            ViewEngineResult result = null;
            var request = controllerContext.HttpContext.Request;
 
            if (request.IsSupportedMobileDevice() && HttpExtentions.HasMobileSpecificViews)
            {
                var viewPathAndName = request.GetMobileViewsDirectoryName() + viewName;
 
                result = base.FindView(controllerContext, viewPathAndName, masterName, true);
 
                if (result == null || result.View == null)
                {
                    result = base.FindView(controllerContext, viewPathAndName, masterName, false);
                }
            }
            else
            {
                result = base.FindView(controllerContext, viewName, masterName, useCache);
            }
            return result;
        }
 
        protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
        {
            return base.FileExists(controllerContext, virtualPath);
        }
 
        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            return base.FindPartialView(controllerContext, partialViewName, useCache);
        }
    }
Following custom configuration required to identify devices, See post how to create custom settings
  <configSections>
        <!--your other sections-->
      <section name="viewTypeSettings" type="Mvc3Demo.Core.ViewTypeSettings,Mvc3Demo" />  
  </configSections>
 
    <viewTypeSettings>
    <viewTypes>
      <viewType name="Phone" deviceIdentifiers="iPhone,iPod,Droid,Blackberry"></viewType>
      <viewType name="Tablet" deviceIdentifiers="iPad,Playbook,Transformer,Xoom"></viewType>
    </viewTypes>
  </viewTypeSettings>
  
If you request URL is http://youdomain.com/home/ from desktop then Home/index.schtml view will be displayed, if request come from any tablet then Home/Tablet/index.schtml view will be displayed, if request come from any mobile then Home/Mobile/index.schtml view will be displayed.
In the MobileViewEngine, we have used an extension method which is a static class as below
   public static class HttpExtentions
    {
        private static readonly bool ConfigCheck;
 
        private static readonly IEnumerable<DeviceType> MobileDevicesList;
 
        static HttpExtentions()
        {
            var customSettins = ViewTypeSettings.Settings;
 
            MobileDevicesList = customSettins.ViewTypes
                                       .Select(x => new DeviceType
                                           {
                                               ViewType = x.Name,
                                               DeviceIdentifiers = x.DeviceIdentifiers.Split(',')
                                           })
                                       .ToList();
 
            ConfigCheck = MobileDevicesList.Any();
        }
 
        public static bool HasMobileSpecificViews
        {
            get { return ConfigCheck; }
        }
 
        /// <summary>
        /// Used to enable debugging using alternative devices
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static bool IsSupportedMobileDevice(this HttpRequestBase request)
        {
 
            bool isMobile = request.Browser.IsMobileDevice;
            string userAgent = request.UserAgent.ToLowerInvariant();
            
            isMobile = isMobile || MobileDevicesList.Any(x => x.DeviceIdentifiers.Any(userAgent.Contains));
 
            return isMobile;
        }
 
        /// <summary>
        /// Gets the name of the mobile views directory.
        /// </summary>
        /// <value>
        /// The name of the mobile views directory.
        /// </value>
        public static string GetMobileViewsDirectoryName(this HttpRequestBase request)
        {
            var directoryName = string.Empty;
            string userAgent = request.UserAgent.ToLowerInvariant();
            var deviceType = MobileDevicesList
                .First(x => x.DeviceIdentifiers.Any(userAgent.Contains));
 
            if (deviceType != null)
            {
                directoryName = deviceType.ViewType;
            }
 
            return !string.IsNullOrEmpty(directoryName) ? String.Format("{0}/", directoryName) : string.Empty;
        }
 
        public static string GetUserIpAddress(this HttpRequestBase request)
        {
            return request.UserHostAddress;
            //return request.ServerVariables["REMOTE_ADDR"];
        }
 
        public static string GetHostName(this HttpRequestBase request)
        {
            IPHostEntry ip = Dns.GetHostEntry(request.UserHostName);
            return ip.HostName;
        }
 
    }
 
    internal class DeviceType
    {
        public string ViewType { getset; }
        public string[] DeviceIdentifiers { getset; }
    }
   



No comments:

Post a Comment