2014-01-21 127 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