2014-01-21 129 views
0

在我的MVC 4控制器,我想重写视图()方法修改视图渲染路径之前

ViewResult View(string viewName, string masterName, object model) {} 

所以,我可以操纵的观点被操作方法呈现。为此,我希望能够获取视图文件的物理路径。我曾尝试以下:

string viewName = this.ControllerContext.RouteData.Route 
    .GetVirtualPath(this.ControllerContext.RequestContext, null) 
    .VirtualPath; 

例如,这可能会返回“/错误/ MissingParameters”当我真正想它返回是一样的东西:

"~/Views/Errors/MissingParameters" 

,或者甚至更好:

"~/Views/Errors/MissingParameters.cshtml" 

我想补充的并发症,我还需要它来应付地区,所以如果我有同样的例子在一个名为“调查”一区运行,我希望它返回类似:

"~/Areas/Surveys/Views/Errors/MissingParameters" 

我想这样做的原因是,我使用的意见全球化尝试,所以我可能有两种观点:

"~/Views/Errors/MissingParameters.cshtml"  // default view (en-GB) 
"~/Views/Errors/MissingParameters_de-DE.cshtml" // German view (de-DE) 

,我希望能够检查视图在引用它之前存在于当前语言/文化。

任何意见将不胜感激。

谢谢。

回答

4

编辑:这部分将不工作或难以实现

你宁愿使用一个动作过滤器可以让你在执行之前操作Result

特别是你需要一个结果过滤器。实施IResultFilter.onResultExecuting方法,并在那里更改结果。特别是当您实施此方法时:

void OnResultExecuting(ResultExecutingContext filterContext) 

您可以访问ResultExecutingContext.Result Property。此属性将包含您的视图。如果您将其投射到System.Web.Mvc.ViewResultBase,您将可以访问ViewName,您将可以对其进行更改。

如果您从未实施过滤器,则此为good hands-on-lab on the subject。在这种情况下,它实现了另一种过滤器,但它是一样的。

作为对OP评论的回答,ViewName丢失和View仍然为空是非常正常的。 ViewName只有在视图返回名称的情况下才会为空,如下所示:return View("Index");。而且,ViewName只是,而不是观点的全部路径。所以这不是一个解决方案。因此,为了让这个解决方案能够正常工作,您必须处理路线数据,控制器上下文等以查找视图。 (关于这一点下文)。

EDIT:溶液,注册自定义视图引擎

当MVC必须呈现它得到从路径数据中的信息,则控制器上下文中,视图名称的图(如上面所解释的那样可以是空的)以及适用的惯例。

特别是,在MVC中有一个注册视图引擎的集合,它需要找到调用方法的视图。视图引擎将返回一个ViewEngineResult,该视图具有找到的视图(如果找到了视图),或返回的视图已被非法查找的路径列表。

因此,要修改模板路径,您可以重写此功能:让原始类查找视图,如果找到,请修改路径。

的办展需要采取论文步骤:

  1. 继承您正在使用的视图引擎(我sampel代码继承了Razor视图引擎)
  2. 注册您的VIE引擎,所以,它的查询原来的视图引擎之前(在我的示例代码中,我只需清除注册引擎列表,并注册矿原始列表包括剃须刀和Web表单视图引擎。)

这是继承的视图引擎代码:

public class CustomRazorViewEngine : FixedRazorViewEngine 
{ 
    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) 
    { 
     ViewEngineResult result 
      = base.FindView(controllerContext, viewName, masterName, useCache); 
     if (result.View != null) 
     { 
      // Modify here !! 
     } 
     return result; 
    } 

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) 
    { 
     ViewEngineResult result 
      = base.FindPartialView(controllerContext, partialViewName, useCache); 
     if (result.View != null) 
     { 
      // Modify here !! 
     } 
     return result; 
    } 

    static readonly PropertyInfo ViewPathProp 
     = typeof(RazorView).GetProperty("ViewPath"); 

    public void SetViewPath(RazorView view, string path) 
    { 
     ViewPathProp.SetValue(view, path); 
    } 

} 

注1:在您阅读// Modify here !!可以修改result.View的路径属性。投它到RazorView(result.View as RazorView).ViewPath。由于ViewPath设置程序受保护,因此需要使用反射设置它:您可以使用SetViewPath方法。

注意2:正如你所看到的,我不是继承RazorViewEngine而是FixedRazorViewEngine。如果你在MSDN中喜欢这个类,你将不会得到结果,但是如果你查看注册视图引擎列表的原始内容,你会发现这个类。我认为这取决于项目中已安装的软件包,我认为它解决了MVC4中的错误。如果你不Microsoft.Web.Mvc命名空间FINF它,继承原RazorViewEngined

注3:视图被发现后,该视图引擎执行它,使用ViewEngineResult,所以,如果你改变它,这将是与新的视觉路径执行

最后,您需要更改注册引擎的列表,在global.asax应用程序启动的事件,就像这样:

protected void Application_Start() 
{ 
    // Original content: 
    AreaRegistration.RegisterAllAreas(); 

    WebApiConfig.Register(GlobalConfiguration.Configuration); 
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 
    RouteConfig.RegisterRoutes(RouteTable.Routes); 
    BundleConfig.RegisterBundles(BundleTable.Bundles); 

    // Added content: 
    ViewEngines.Engines.Clear(); 
    ViewEngines.Engines.Add(new CustomRazorViewEngine()); 
} 

注:竟被它如果您在App_Start文件夹中创建了ViewEngineConfig类,并且调用此类的静态方法,就像使用其他所有配置完成一样。

+0

我喜欢这种方法的外观,但是当我尝试它时,ViewResultBase.View和ViewResultBase.ViewName属性总是分别为空和“”。有什么建议么? – Neilski

+0

哎呀!你是对的。只有当你返回一个带有提供的视图的视图时,'ViewName'才可用,就像'return View(“index”)'。如果不是,注册视图引擎之一负责查找视图。 MVC在每个'IViewEngine'中调用'FindView'直到找到视图。所以,我们必须找到另一个解决这个问题的方法。也许注册自定义视图引擎将是解决方案。让我试试 – JotaBe

+0

谢谢JotaBe竭尽所能,花时间详细回答我的问题 - 非常感谢。 – Neilski

1

答复从here复制。

那么,如果你不介意你的代码依赖于您所使用的特定视图引擎,你可以看看ViewContext.View财产并投它WebFormView

var viewPath = ((WebFormView)ViewContext.View).ViewPath;

我相信这会在最后为您提供视图名称。

编辑:Haacked绝对是专注于;为了使事情有点整洁我已经用如下扩展方法包装了逻辑:

public static class IViewExtensions { 
    public static string GetWebFormViewName(this IView view) { 
     if (view is WebFormView) { 
      string viewUrl = ((WebFormView)view).ViewPath; 
      string viewFileName = viewUrl.Substring(viewUrl.LastIndexOf('/')); 
      string viewFileNameWithoutExtension = Path.GetFileNameWithoutExtension(viewFileName); 
      return (viewFileNameWithoutExtension); 
     } else { 
      throw (new InvalidOperationException("This view is not a WebFormView")); 
     } 
    } 
} 

这似乎正是我以前做的事情。

另一种解决方案here ((System.Web.Mvc.RazorView)htmlHelper.ViewContext.View).ViewPath

净MVC

+0

永远不要复制答案。关闭“重复”的问题。如果你没有这个特权,请在评论中说出来。 – JotaBe

+0

@JotaBe好的。我提供了一个原始答案的链接。 – kbvishnu

+0

但是,应该做一个评论,以保持SO整洁;)http://meta.stackexchange.com/questions/10841/how-should-duplicate-questions-be-handled – JotaBe