Ariel
Ariel

Reputation: 916

MVC Change ViewName on the fly

I have a project where i want to show different views to Desktop e Mobile users, mostly for better experience and speed. Right now i'm using 51degrees to tell me if the request comes from a mobile or desktop user and then in my Action Filter i will change the view name from "Index" to "Index.Mobile", the thing is i'm getting some problems with Views where i don't want to use a Layout at all.

This is my Method:

public override void OnActionExecuted( ActionExecutedContext filterContext )
{
    var result   = filterContext.Result as ViewResult;
    var view     = filterContext.ActionDescriptor.ActionName;

    //IsMobile is filled on my OnActionExecuting method
    var IsMobile = filterContext.Controller.ViewBag.IsMobile;

    if( result == null ) return;

    string viewName;
    string masterName;

    if( IsMobile )
    {
        viewName   = view + ".Mobile";
        masterName = "_Layout.Mobile";

        //Check if view exists
        var viewResult = ViewEngines.Engines.FindView( filterContext.Controller.ControllerContext, viewName, null );

        if( viewResult.View == null )
        {
            viewName = view;
        }
    }
    else
    {
        viewName   = view;
        masterName = "_Layout";
    }

    result.ViewName   = viewName;
    result.MasterName = masterName;
}

What happens is, here i set a value for the MasterName or Layout and at two specific Controllers i need to set it as null but it still fetches the Layout i set before.

What i need is, this method will set the view and layout name and if need i will set the Layout as null inside the view i loaded.

Can that be done ?

EDIT

I tried to implement another method, Link, but i couldn't find a way to change the Layout view, only the action.

Upvotes: 2

Views: 1368

Answers (3)

Lijin Durairaj
Lijin Durairaj

Reputation: 5222

Well to change view name dynamically you can also use this, which is simple

 public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsLocal)
            {
                filterContext.Result = new ViewResult { ViewName = "Index" };
            }
        }

This worked for me :)

Upvotes: 1

Ariel
Ariel

Reputation: 916

After some debugging i saw that setting the value inside the View wasn't working because the ViewEngine would ignore this new value and keep the value setted on the OnActionExecuted method.

I managed to fix my problem using the same method but with the addition of a ViewBag telling me if this particular view needs a Layout. Since the OnActionExecuted will execute after the Controller and before the View it's possible to set the value as true/false at the Controller and then read it inside the OnActionExecuted.

This is the working example:

public override void OnActionExecuted( ActionExecutedContext filterContext )
{
    var result   = filterContext.Result as ViewResult;
    var IsMobile = filterContext.Controller.ViewBag.IsMobile;
    var view     = filterContext.ActionDescriptor.ActionName;

    if( result == null ) return;

    string viewName;
    string masterName;
    bool hasLayout = filterContext.Controller.ViewBag.HasLayout ?? true;

    if( IsMobile )
    {
        viewName   = view + ".Mobile";
        masterName = hasLayout ? "_Layout.Mobile" : null;

        //Check if new view exists
        var viewResult = ViewEngines.Engines.FindView( filterContext.Controller.ControllerContext, viewName, null );

        if( viewResult.View == null )
        {
            viewName = view;
        }
    }
    else
    {
        viewName   = view;
        masterName = hasLayout ? "_Layout" : null;
    }

    result.ViewName = viewName;
    result.MasterName = masterName;
}

If anyone has a better option besides that please share, this workaround looks nice but it feels a little bit wrong.

Upvotes: 1

TejSoft
TejSoft

Reputation: 3315

You can create a folder in your project like "Areas/Mobile" and keep your views, controllers and even models (view models for mobile) in there. You just have to create a routing rule to pass across the requests coming from a mobile device to controllers inside the "Area/Mobile". It would be much easier to manage and build a mobile version of the site.

Upvotes: 1

Related Questions