2009-05-26 113 views
30

我想根據當前的UI文化在運行時更改視圖位置。我怎樣才能實現這與默認的Web窗體視圖引擎?如何更改ASP.NET MVC中的默認視圖位置方案?

基本上我想知道如何實現WebFormViewEngine什麼是custom IDescriptorFilterSpark

是否有其他的視圖引擎,它給了我視圖位置的運行時控制?


編輯:我的網址應該看起來如下{lang}/{controller}/{action}/{id}。我不需要依賴於語言的控制器,並且視圖是用資源本地化的。然而,少數觀點在某些語言中會有所不同。所以我需要告訴視圖引擎首先查找語言特定的文件夾。

回答

31

一個簡單的解決辦法是,從ViewEngines.Engines收集相應的ViewEngineAppication_Start弄個並更新其ViewLocationFormats陣列和PartialViewLocationFormats。沒有hackery:它是默認讀/寫的。

protected void Application_Start() 
{ 
    ... 
    // Allow looking up views in ~/Features/ directory 
    var razorEngine = ViewEngines.Engines.OfType<RazorViewEngine>().First(); 
    razorEngine.ViewLocationFormats = razorEngine.ViewLocationFormats.Concat(new string[] 
    { 
     "~/Features/{1}/{0}.cshtml" 
    }).ToArray(); 
    ... 
    // also: razorEngine.PartialViewLocationFormats if required 
} 

默認一個剃鬚刀looks like this

ViewLocationFormats = new string[] 
{ 
    "~/Views/{1}/{0}.cshtml", 
    "~/Views/{1}/{0}.vbhtml", 
    "~/Views/Shared/{0}.cshtml", 
    "~/Views/Shared/{0}.vbhtml" 
}; 

注意,您可能需要更新PartialViewLocationFormats也。

1

我相信解決的辦法是創建自己的ViewEngine,它繼承自WebFormViewEngine。在構造函數中,它應該檢查當前線程的當前UI文化並添加適當的位置。只是不要忘記將它添加到你的視圖引擎。

這應該是這個樣子:

public class ViewEngine : WebFormViewEngine 
{ 
    public ViewEngine() 
    { 
     if (CultureIsX()) 
      ViewLocationFormats = new string[]{"route1/controller.aspx"}; 
     if (CultureIsY()) 
      ViewLocationFormats = new string[]{"route2/controller.aspx"}; 
    } 
} 

在Global.asax中:

ViewEngines.Engines.Add(new ViewEngine()); 
+1

您也可以在http://www.codeplex.com/oxite項目中看到實現。 – pocheptsov 2009-05-26 09:53:21

+2

對不起,這是不好的解決方案,因爲ViewEngine的實例在線程中共享,我需要根據線程UI文化渲染不同的視圖。 – 2009-05-26 10:19:34

+0

也許有可能爲每種文化添加viewEngine並重寫findView方法來中斷它們(如果線程不同)?只是一個奇怪的想法... – 2009-05-26 10:40:35

8

VirtualPathProviderViewEngine.GetPathFromGeneralName必須改變,以允許從航線的附加參數。它不公開,這就是爲什麼你必須複製GetPath,GetPathFromGeneralName,IsSpecificPath ...到你自己的ViewEngine實施。

你是對的:這看起來像一個完整的重寫。我希望GetPathFromGeneralName是公開的。

using System.Web.Mvc; 
using System; 
using System.Web.Hosting; 
using System.Globalization; 
using System.Linq; 

namespace MvcLocalization 
{ 
    public class LocalizationWebFormViewEngine : WebFormViewEngine 
    { 
     private const string _cacheKeyFormat = ":ViewCacheEntry:{0}:{1}:{2}:{3}:"; 
     private const string _cacheKeyPrefix_Master = "Master"; 
     private const string _cacheKeyPrefix_Partial = "Partial"; 
     private const string _cacheKeyPrefix_View = "View"; 
     private static readonly string[] _emptyLocations = new string[0]; 

     public LocalizationWebFormViewEngine() 
     { 
      base.ViewLocationFormats = new string[] { 
        "~/Views/{1}/{2}/{0}.aspx", 
        "~/Views/{1}/{2}/{0}.ascx", 
        "~/Views/Shared/{2}/{0}.aspx", 
        "~/Views/Shared/{2}/{0}.ascx" , 
        "~/Views/{1}/{0}.aspx", 
        "~/Views/{1}/{0}.ascx", 
        "~/Views/Shared/{0}.aspx", 
        "~/Views/Shared/{0}.ascx" 

      }; 

     } 

     private VirtualPathProvider _vpp; 

     public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) 
     { 
      if (controllerContext == null) 
       throw new ArgumentNullException("controllerContext"); 

      if (String.IsNullOrEmpty(viewName)) 
       throw new ArgumentException("viewName"); 

      string[] viewLocationsSearched; 
      string[] masterLocationsSearched; 

      string controllerName = controllerContext.RouteData.GetRequiredString("controller"); 
      string viewPath = GetPath(controllerContext, ViewLocationFormats, "ViewLocationFormats", viewName, controllerName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched); 
      string masterPath = GetPath(controllerContext, MasterLocationFormats, "MasterLocationFormats", masterName, controllerName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched); 

      if (String.IsNullOrEmpty(viewPath) || (String.IsNullOrEmpty(masterPath) && !String.IsNullOrEmpty(masterName))) 
      { 
       return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); 
      } 

      return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); 
     } 

     private string GetPath(ControllerContext controllerContext, string[] locations, string locationsPropertyName, string name, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) 
     { 
      searchedLocations = _emptyLocations; 

      if (String.IsNullOrEmpty(name)) 
       return String.Empty; 

      if (locations == null || locations.Length == 0) 
       throw new InvalidOperationException(); 

      bool nameRepresentsPath = IsSpecificPath(name); 
      string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName); 

      if (useCache) 
      { 
       string result = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, cacheKey); 
       if (result != null) 
       { 
        return result; 
       } 
      } 

      return (nameRepresentsPath) ? 
       GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) : 
       GetPathFromGeneralName(controllerContext, locations, name, controllerName, cacheKey, ref searchedLocations); 
     } 

     private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name, string controllerName, string cacheKey, ref string[] searchedLocations) 
     { 
      string result = String.Empty; 
      searchedLocations = new string[locations.Length]; 
      string language = controllerContext.RouteData.Values["lang"].ToString(); 

      for (int i = 0; i < locations.Length; i++) 
      { 
       string virtualPath = String.Format(CultureInfo.InvariantCulture, locations[i], name, controllerName,language); 

       if (FileExists(controllerContext, virtualPath)) 
       { 
        searchedLocations = _emptyLocations; 
        result = virtualPath; 
        ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); 
        break; 
       } 

       searchedLocations[i] = virtualPath; 
      } 

      return result; 
     } 

     private string CreateCacheKey(string prefix, string name, string controllerName) 
     { 
      return String.Format(CultureInfo.InvariantCulture, _cacheKeyFormat, 
       GetType().AssemblyQualifiedName, prefix, name, controllerName); 
     } 

     private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) 
     { 
      string result = name; 

      if (!FileExists(controllerContext, name)) 
      { 
       result = String.Empty; 
       searchedLocations = new[] { name }; 
      } 

      ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); 
      return result; 
     } 

     private static bool IsSpecificPath(string name) 
     { 
      char c = name[0]; 
      return (c == '~' || c == '/'); 
     } 

    } 
} 
3

1)從剃刀視圖引擎擴展類

public class LocalizationWebFormViewEngine : RazorViewEngine

2)添加的部分的位置的格式

public LocalizationWebFormViewEngine() 
{ 
    base.PartialViewLocationFormats = new string[] { 
     "~/Views/{2}/{1}/{0}.cshtml", 
     "~/Views/{2}/{1}/{0}.aspx", 
     "~/Views/{2}/Shared/{0}.cshtml", 
     "~/Views/{2}/Shared/{0}.aspx" 
    }; 

    base.ViewLocationFormats = new string[] { 
     "~/Views/{2}/{1}/{0}.cshtml", 
     "~/Views/{2}/{1}/{0}.aspx", 
     "~/Views/{2}/Shared/{0}.cshtml", 
     "~/Views/{2}/Shared/{0}.aspx" 
    }; 
} 

3)創建爲局部視圖的覆蓋方法渲染

public override ViewEngineResult FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache) 
{ 
    if (controllerContext == null) 
    { 
     throw new ArgumentNullException("controllerContext"); 
    } 
    if (String.IsNullOrEmpty(partialViewName)) 
    { 
     throw new ArgumentException("partialViewName"); 
    } 

    string[] partialViewLocationsSearched; 

    string controllerName = controllerContext.RouteData.GetRequiredString("controller"); 
    string partialPath = GetPath(controllerContext, PartialViewLocationFormats, "PartialViewLocationFormats", partialViewName, controllerName, _cacheKeyPrefix_Partial, useCache, out partialViewLocationsSearched); 

    return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this);} 
} 
1

下面是一個沒有重寫的本地化視圖引擎。

簡而言之,引擎會每次將新位置插入視圖位置查看視圖。引擎將使用兩種字符語言來查找視圖。所以如果當前的語言是es(西班牙語),它會查找~/Views/Home/Index.es.cshtml

查看代碼評論瞭解更多詳情。

更好的方法是重寫視圖位置被解析的方式,但方法不可覆蓋;也許在ASP.NET MVC 5?

public class LocalizedViewEngine : RazorViewEngine 
{ 
    private string[] _defaultViewLocationFormats; 

    public LocalizedViewEngine() 
     : base() 
    { 
     // Store the default locations which will be used to append 
     // the localized view locations based on the thread Culture 
     _defaultViewLocationFormats = base.ViewLocationFormats; 
    } 

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) 
    { 
     AppendLocalizedLocations(); 
     return base.FindPartialView(controllerContext, partialViewName, useCache:fase); 
    } 

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) 
    { 
     AppendLocalizedLocations(); 
     returnbase.FindView(controllerContext, viewName, masterName, useCache:false); 
    } 

    private void AppendLocalizedLocations() 
    { 
     // Use language two letter name to identify the localized view 
     string lang = Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; 

     // Localized views will be in the format "{action}.{lang}.cshtml" 
     string localizedExtension = string.Format(".{0}.cshtml", lang); 

     // Create an entry for views and layouts using localized extension 
     string view = "~/Views/{1}/{0}.cshtml".Replace(".cshtml", localizedExtension); 
     string shared = "~/Views/{1}/Shared/{0}".Replace(".cshtml", localizedExtension); 

     // Create a copy of the default view locations to modify 
     var list = _defaultViewLocationFormats.ToList(); 

     // Insert the new locations at the top of the list of locations 
     // so they're used before non-localized views. 
     list.Insert(0, shared); 
     list.Insert(0, view); 
     base.ViewLocationFormats = list.ToArray(); 
    } 
}