How to check if returning View is of type PartialViewResult or a ViewUserControl
In the method CreateView() (check my View Engine below) or in my custom action filter (also below) I have to somehow check if the View we are requesting is a ViewUserControl. Because otherwise I get an error saying
"A master name cannot be specified when the view is a ViewUserControl."
when I have "modal=true" in QueryString and the View request is ViewUsercontrol because you cannot set master page on ViewUserControls (obviously).
This is my custom view engine code right now:
public class PendingViewEngine : VirtualPathProviderViewEngine
{
public PendingViewEngine()
{
// This is where we tell MVC where to look for our files.
/* {0} = view name or master page name
* {1} = controller name */
MasterLocationFormats = new[] {"~/Views/Shared/{0}.master", "~/Views/{0}.master"};
ViewLocationFormats = new[]
{
"~/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx", "~/Views/Shared/{0}.ascx",
"~/Views/{1}/{0}.ascx"
}开发者_开发问答;
PartialViewLocationFormats = new[] {"~/Views/{1}/{0}.ascx", "~/Views/Shared/{0}.ascx"};
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return new WebFormView(partialPath, "");
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
return new WebFormView(viewPath, masterPath);
}
}
My action filter:
public class CanReturnModalView : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// setup the request, view and data
HttpRequestBase request = filterContext.RequestContext.HttpContext.Request;
bool modal = false;
if (request.QueryString["modal"] != null)
modal = bool.Parse(request.QueryString["modal"]);
if (filterContext.Result is ViewResult)
{
ViewResult view = (ViewResult) (filterContext.Result);
// replace the view Master page file with Modal Masterpage
if (modal)
view.MasterName = "AdministrationModal";
filterContext.Result = view;
}
else if (filterContext.Result is RedirectToRouteResult)
{
RedirectToRouteResult redirect = (RedirectToRouteResult) filterContext.Result;
// append modal route value to the redirect result if modal was requested
if (modal)
redirect.RouteValues.Add("modal", true);
filterContext.Result = redirect;
}
}
}
The above ViewEngine fails on calls like this:
<% Html.RenderAction("Display", "MyController", new { zoneslug = "some-zone-slug" });%>
The action I am rendering here is this:
public ActionResult Display(string zoneslug)
{
WidgetZone zone;
if (!_repository.IsUniqueSlug(zoneslug))
zone = (WidgetZone) _repository.GetInstance(zoneslug);
else
{
// do something here
}
// WidgetZone used here is WidgetZone.ascx, so a partial
return View("WidgetZone", zone);
}
I cannot use RenderPartial because you cannot send route values to RenderPartial the way you can to RenderAction. To my knowledge there is no way to provide RouteValueDictionary to RenderPartial() like the way you can to RenderAction().
You can achieve the described behaviour by calling PartialView instead of view:
Create your view like you would now:
<% Html.RenderAction("PartialIndex", new { test = "input" }); %>
As you can see there is nothing special to it.
Then in your controller action simply call PartialView instead of View:
public ActionResult PartialIndex(string test)
{
var viewResult = PartialView("PartialIndex", (object)test);
return viewResult;
}
And also override the View method to overrule the mastername when the modal parameter is passed into the query string.
public bool IsModal
{
get
{
var modalParameter = Request.QueryString["modal"];
if (string.IsNullOrEmpty(modalParameter))
return false;
bool modalValue;
var success = bool.TryParse(modalParameter, out modalValue);
return success && modalValue;
}
}
protected override ViewResult View(string viewName, string masterName, object model)
{
if (IsModal)
masterName = "Alternate";
return base.View(viewName, masterName, model);
}
The View method override is only hit when you render an actual view (and not a partial one), so it is safe to assume that a masterName can be set.
Try this ( untested ). FindView returns an ViewEngineResults which has a property called IView View which could be a PartialViewResult. Just check and return and use an empty master if nothing exists.
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if( base.FindView(controllerContext, viewName, "", useCache).View is PartialViewResult )
return base.FindView(controllerContext, viewName, "", useCache);
if (controllerContext.HttpContext.Request.IsAjaxRequest())
return base.FindView(controllerContext, viewName, "Modal", useCache);
return base.FindView(controllerContext, viewName, "Site", useCache);
}
}
精彩评论