2009-05-26 114 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(); 
    } 
}