Click here to Skip to main content
15,885,278 members
Articles / MVC

MVC Manipulate Shared Layout

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
9 Jul 2012CPOL2 min read 16.2K   4   1
The problemHave you ever needed a static way of persisting items throughout a "Post-Back" or a "Get" or "Post" without creating a static reference that you'll have to clean up later?  Have you ever needed a clean and defined way (non ViewBag version) of modifying a layout in MVC wi

The problem

Have you ever needed a static way of persisting items throughout a "Post-Back" or a "Get" or "Post" without creating a static reference that you'll have to clean up later?

Have you ever needed a clean and defined way (non ViewBag version) of modifying a layout in MVC without having to create dozens and dozens of them?

This is something that I'm currently finding opportunities in right now. The issue arose when we realized that the application we are working on needs to have many customized features on the layout and many more that just aren't defined yet, knowing there'll be more requests once the product goes live. So I after researching and researching I developed a method I thought was pretty well defined and also extendable.

The solution

Here's one way to do that.

First I created a Model called the SharedLayoutModel in our sites Core library. This class will contain the following items that are initialized on every override ViewResult in our base controller:

  • First, Second and Third Tier Navigation
  • The current RouteData used when initalizing the Layout Model
  • The current Navigation item we're on (database stored)
  • and a Title for the window.


There's many other middle-tier code in this example, many extension methods and the use of a Repository Pattern on entity types (can be discussed at a later time if needed) but the main piece in this whole philosophy I came up with is that first static instance called Instance .

What that guy does is holds the defined generic type in the current HttpContext.Items container and allows the end of the http request to dispose of the object when we're finished from the HttpContext. This way we don't even have to worry that our (what could've been) singleton instance is disposed of properly. IIS will take care of it for us.

 

SharedLayoutModel.cs

 

 

C#
using System; 
using System.Linq;
using System.Web.Routing;
using Imfaqncodn.Core.Arch;
using Imfaqncodn.Core.Extensions;
using Imfaqncodn.Core.Utility;

namespace Imfaqncodn.Core.Web.UI.Models.Views.Shared
{
    /// <summary>
    /// The shared offers a singleton object representation of shared properties across all layouts.
    /// </summary>
    /// <remarks></remarks>
    [Serializable]
    public sealed class SharedLayoutModel
    {

        /// <summary>
        /// 
        /// </summary>
        public static ContextInstance<SharedLayoutModel> Instance = new ContextInstance<SharedLayoutModel>("SharedLayoutModel_instance");

        #regionRequired Assignments on Initialize

        /// <summary>
        /// Set's the Nav Item Module Type for this Layout.
        /// </summary>
        /// <value>The type of the nav item module.</value>
        /// <remarks>Set in the Initialize method</remarks>
        public NavItemModuleTypeNavItemModuleType { get; set; }


        /// <summary>
        /// Set's the current Route Data for this Layout.
        /// </summary>
        /// <value>The route data.</value>
        /// <remarks>Set in the Initialize method</remarks>
        public RouteDataRouteData { get; set; }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        private HeadTitle_Title;
        /// <summary>
        /// Gets or sets the title.
        /// </summary>
        /// <value>The title.</value>
        /// <remarks></remarks>
        public HeadTitleTitle
        {
            get
            {
                if (_Title == null)
                {
                    _Title = new HeadTitle();
                }
                return _Title;
            }
            set
            {
                _Title = value;
            }
        }

        #regionNavigation

        /// <summary>
        /// First Tier Navigation Menu
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public IQueryable<NavItem> NavigationFirstTier()
        {
            return NavigationUtility.Instance.Get.GetFirstTierNavigationthis.NavItemModuleType, this.RouteData);
        }

        /// <summary>
        /// Second Tier Navigation Menu
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public IQueryable<NavItem> NavigationSecondTier()
        {
            return NavigationUtility.Instance.Get.GetSecondTierNavigation(this.NavItemModuleType, this.RouteData);
        }

        /// <summary>
        /// Third Tier Navigation Menu
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public IQueryable<NavItem> NavigationThirdTier()
        {
            return NavigationUtility.Instance.Get.GetThirdTierNavigation(this.NavItemModuleType, this.RouteData);
        }

        /// <summary>
        /// Returns the best suited Nav Item for the current Route Data provided.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public NavItemCurrentNavItem()
        {
            string controller = RouteData.GetController();
            string action = RouteData.GetAction();

            //GetControllerAction(out controller, out action);

            Imfaqncodn.Core.DA.StoreRepository<NavItem> repository = newDA.StoreRepository<NavItem>();

            NavItem navItem = repository.Find(n => n.Controller == controller && n.Action == action);
            
            return navItem;
        }

        #endregion


        /// <summary>
        /// Initialized on each start of an Actions ViewResult.
        /// </summary>
        /// <param name="routeData">The route data.</param>
        /// <param name="navItemModuleType">Defaults to UNKNOWN</param>
        /// <remarks></remarks>
        public voidInitialize(RouteData routeData, NavItemModuleType navItemModuleType)
        {
            //clear out other data here...
            this.RouteData = routeData;
            this.NavItemModuleType = navItemModuleType;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <remarks></remarks>
    public sealed class HeadTitle
    {
        /// <summary>
        /// Gets the runtime environment.
        /// </summary>
        /// <remarks></remarks>
        private stringRuntimeEnvironment 
        {
            get
            {
                return EnvironmentUtility.RuntimeEnvironment != EnvironmentType.PRODUCTION 
                    ? EnvironmentUtility.RuntimeEnvironment.ToString() 
                    : string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets the window title.
        /// </summary>
        /// <value>The window title.</value>
        /// <remarks></remarks>
        public stringWindowTitle { get; set; }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>A <see cref="System.String"/> that represents this instance.</returns>
        /// <remarks></remarks>
        public override string ToString()
        {
            string title = string.Empty;

            if (!string.IsNullOrEmpty(RuntimeEnvironment))
            {
                title += "[" + RuntimeEnvironment + "] ";
            }

            string Controller = HttpContextUtility.GetRouteData().GetController();

            string Action = HttpContextUtility.GetRouteData().GetAction();

            title += Controller;

            if (!Action.ToLower().In("index", "default"))
            {
                title += " " + Action;
            }

            return title;
        }
    }

}

 

ContextInstance.cs

C#
using System;
usingImfaqncodn.Core.Utility;
namespace Imfaqncodn.Core.Arch
{
    /// <summary>
    /// Creates a member instance of this Generic Type for the duration of an HttpContext.
    /// </summary>
    /// <example>
    /// <![CDATA[
    /// [Serializable]
    /// public class SharedLayoutModel
    /// {
        /// public static ContextInstance<SharedLayoutModel> Instance = new ContextInstance<SharedLayoutModel>("SharedLayoutModel_instance");
    /// }]]>
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <remarks></remarks>
    public sealed class ContextInstance<T> where T : class, new()
    {
        /// <summary>
        ///
        /// </summary>
        public readonly string ContextKey;
        /// <summary>
        /// Initializes a new instance of the <see cref="ContextInstance<T>"/> class.
        /// </summary>
        /// <param name="ContextKey">The context key.</param>
        /// <remarks></remarks>
        public ContextInstance(stringContextKey)
        {
            this.ContextKey = ContextKey;
        }
        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The get.</value>
        /// <remarks></remarks>
        public T Get
        {
            get
            {
                T _instance = HttpContextUtility.GetItem<T>(ContextKey);
                if (_instance == null)
                {
                    _instance = new T();
                }
                HttpContextUtility.AddItem(ContextKey, _instance);
                return _instance;
            }
            set
            {
                HttpContextUtility.AddItem(ContextKey, value);
            }
        }
    }
} 

Here's an example of the HttpContextUtility code

HttpContextUtility.cs

C#
public static HttpContext GetCurrentHttpContext()
        {
                return HttpContext.Current;
        }
        public static HttpRequest GetCurrentHttpRequest()
        {
                HttpRequest hr = null;
                if (GetCurrentHttpContext() != null)
                    return HttpContext.Current.Request;
                return hr;
        }
        public static void AddItem(objectkey, object value)
        {
                HttpContext current = GetCurrentHttpContext();
                try
                {
                        if (current != null&& current.Items != null)
                        {
                                if (current.Items.Contains(key))
                                {
                                        current.Items.Remove(key);
                                }
                                current.Items.Add(key, value);
                        }
                        else
                        {
                                throw newException("Current Context Add Items is not available.");
                        }
                }
                catch(Exceptionex)
                {
                        //Handle Exception using BusinessLogicExceptionHandler, rethrow by policy, otherwise handle within catch as desired
                        BusinessLogicExceptionHandler.HandleException(ref ex);
                }
        }
        public static object GetItem(objectkey)
        {
                HttpContext current = GetCurrentHttpContext();
                return (current != null && current.Items != null) ? current.Items[key] : null;
        }
        public static T GetItem<T>(object key) where T : class, new()
        {
                HttpContext current = GetCurrentHttpContext();
                return (current != null && current.Items != null) ? current.Items[key] as T : null;
        }
        public static S GetItemAsValueType<S>(object key) where S : struct
        {
                HttpContext current = GetCurrentHttpContext();
                return (current != null && current.Items != null) ? (S)current.Items[key] : default(S);
        } 


Example of cshtml files

 

HTML
<title> @SharedLayoutModel.Instance.Get.Title.ToString()</title>

 

_layout.cshtml

HTML
<head>    @Html.Partial("_Head")
    @RenderSection("HeadTagAdditions", required: false)
</head>
<body>
    <!-- container -->
    <div class="container root">
        <div class="container top">
            <div class="row">
                <header class="three columns push-nine">
                    <h1 class="@SharedLayoutModel.Instance.Get.NavItemModuleType.GetFlyCssIconCode()">@LayoutHelpers.GetCurrentNavItemTerm()</h1>
                    <form action="@Url.Action("Search", "Orders")"id="frmsrch"method="post">
                    <p>
                      <input type="search" name="glbsterm" id="glbsterm" placeholder="Global Search">                                                
                      <button type="submit">Search</button>
                    </p>
                    </form>
                </header>
            </div>
        </div>
   </div>
</body>

Example Controller Code

C#
void ControllerMethod(stringtitleAddition){
    SharedLayoutModel.Instance.Get.Title.WindowTitle = titleAddition;
}


And that's it! Hope this makes your layouts a little easier to manage with some definition. ;)
 

This article was originally posted at http://dillonraney.blogspot.com/feeds/posts/default

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questiona few notes.. Pin
Seishin#9-Jul-12 21:44
Seishin#9-Jul-12 21:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.