Custom ViewEngine ASP.NET MVC 3

2020-06-25 05:13发布

问题:

I am looking for the simplest solution for the custom viewengine for asp.net mvc. So I can over-ride the path to look for the views.

Actually, I am trying to build a theme system in my solution. I looked over web but found solution which is hard to learn and implement.

Thanks

回答1:

This is what I use. It looks for views in a theme folder. Set the theme name, in the first line of the constructor. It also supports mobile views, but you'll need something like 51 Degrees Mobi to provide you with the mobile browser details.

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace Milkshake.Commerce.MvcClient.ViewEngines
{
    /// <summary>
    /// Milkshake Commerce specific ViewEngine.
    /// </summary>
    public class MilkshakeViewEngine : WebFormViewEngine
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MilkshakeViewEngine"/> class.
        /// </summary>
        public MilkshakeViewEngine()
        {
            string themeName = AppData.ThemeName;

            var masterLocationFormats = new List<string>();
            masterLocationFormats.Add("~/Views/Themes/" + themeName + "/{0}.master");
            masterLocationFormats.AddRange(MasterLocationFormats);

            MasterLocationFormats = masterLocationFormats.ToArray();

            var partialViewLocationFormats = new List<string>();
            partialViewLocationFormats.Add("~/Views/Themes/" + themeName + "/{1}/{0}.aspx");
            partialViewLocationFormats.Add("~/Views/Themes/" + themeName + "/{1}/{0}.ascx");
            partialViewLocationFormats.Add("~/Views/Themes/" + themeName + "/{0}.aspx");
            partialViewLocationFormats.Add("~/Views/Themes/" + themeName + "/{0}.ascx");
            partialViewLocationFormats.AddRange(PartialViewLocationFormats);

            PartialViewLocationFormats = partialViewLocationFormats.ToArray();

            var viewLocationFormats = new List<string>();
            viewLocationFormats.Add("~/Views/Themes/" + themeName + "/{1}/{0}.aspx");
            viewLocationFormats.Add("~/Views/Themes/" + themeName + "/{1}/{0}.ascx");
            viewLocationFormats.Add("~/Views/Themes/" + themeName + "/{0}.aspx");
            viewLocationFormats.Add("~/Views/Themes/" + themeName + "/{0}.ascx");
            viewLocationFormats.AddRange(ViewLocationFormats);

            ViewLocationFormats = viewLocationFormats.ToArray();
        }

        /// <summary>
        /// Gets the user-selected <see cref="UserExperiences"/> setting, or returns the defualt if no experience has been selected.
        /// </summary>
        /// <returns>Returns the user experience selected by the user.</returns>
        public static UserExperiences GetUserExperienceSelected()
        {
            HttpContext context = HttpContext.Current;
            UserExperiences userExperience = context.Request.Browser.IsMobileDevice ? UserExperiences.Mobile : UserExperiences.Desktop;
            var userExperienceCookie = context.Request.Cookies["UserExperience"];

            if (userExperienceCookie != null)
            {
                if (!String.IsNullOrWhiteSpace(userExperienceCookie.Value))
                {
                    Enum.TryParse<UserExperiences>(userExperienceCookie.Value, out userExperience);
                }
            }
            else
            {
                SetUserExperience(userExperience);
            }

            return userExperience;
        }

        /// <summary>
        /// Sets the user experience cookie.
        /// </summary>
        /// <param name="experience">The user experience.</param>
        public static void SetUserExperience(UserExperiences experience)
        {
            HttpContext context = HttpContext.Current;
            HttpCookie userExperienceCookie = context.Request.Cookies["UserExperience"];

            if (userExperienceCookie == null)
            {
                userExperienceCookie = new HttpCookie("UserExperience");
            }

            userExperienceCookie.Value = experience.ToString();
            userExperienceCookie.Path = "/";
            userExperienceCookie.Expires = DateTime.UtcNow.AddDays(30);

            context.Response.Cookies.Add(userExperienceCookie);
        }

        /// <summary>
        /// Finds the specified view by using the specified controller context and master view name.
        /// </summary>
        /// <remarks>This override is used to determine if the browser is a mobile phone.
        /// If so, and it is supported, we add the phone specific folder/view name to the existing viewname,
        /// which forces the ViewManager to find a Theme specific mobile view (specific for that phone), or just use the built in mobile view.</remarks>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="viewName">The name of the view.</param>
        /// <param name="masterName">The name of the master view.</param>
        /// <param name="useCache">true to use the cached view.</param>
        /// <returns>The page view.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext"/> parameter is null (Nothing in Visual Basic).</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="viewName"/> parameter is null or empty.</exception>
        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            ViewEngineResult result = null;
            var request = controllerContext.HttpContext.Request;
            var userExperience = GetUserExperienceSelected();

            // Avoid unnecessary checks if this device isn't suspected to be a mobile device
            if (userExperience == UserExperiences.Mobile)
            {
                masterName = "Mobile/Mobile";
                result = base.FindView(controllerContext, "Mobile/" + viewName, masterName, useCache);
            }

            // Fall back to desktop view if no other view has been selected
            if (result == null || result.View == null)
            {
                result = base.FindView(controllerContext, viewName, masterName, useCache);
            }

            return result;
        }

        /// <summary>
        /// Adds the default mobile location formats.
        /// </summary>
        /// <param name="defaultLocationFormats">The default location formats.</param>
        /// <param name="viewLocationFormats">The view location formats.</param>
        private void AddDefaultMobileLocationFormats(string[] defaultLocationFormats, List<string> viewLocationFormats)
        {
            var mobileViewLocationFormats = new List<string>();

            foreach (var item in defaultLocationFormats)
            {
                if (item.Contains("Views/{1}/{0}"))
                {
                    mobileViewLocationFormats.Add(item.Replace("/{1}/{0}", "/{1}/Mobile/{0}"));
                }
                else if (item.Contains("Views/Shared/{0}"))
                {
                    mobileViewLocationFormats.Add(item.Replace("/Shared/{0}", "/Shared/Mobile/{0}"));
                }
            }

            viewLocationFormats.AddRange(mobileViewLocationFormats);
        }
    }
}