需要自定义ViewEngine吗?

时间:2009-10-25 21:03:05

标签: asp.net-mvc

我有一个定制设计的网站,具体取决于点击的“商店”...

所以,我有一个文件夹结构,如:

 + _default
    - Site.Master
    - Site.css
    + Views
      - main.ascx
      - xyz.ascx
      - zbf.aspx
 + custom_design_01
    - Site.Master
    - Site.css
    + Views
      - xyz.ascx
      - zbf.aspx
 + custom_design_02
    - Site.Master
    - Site.css
    + Views
      - xyz.ascx
      - zbf.aspx

在我的控制器中,我将决定使用哪个文件夹,具体取决于请求中的参数(当文件不可用作自定义文件时,应使用默认文件夹..)

所以,控制器做了类似的事情:

    public ActionResult Index(int id)
    {
        var newRouteParamThatWouldntFitIntoTheCurrentViewMethodLikeThis = DoSomeMagicalRoutingStuff(id);

        return View(newRouteParamThatWouldntFitIntoTheCurrentViewMethodLikeThis);
        //return View();
    }

如果我没有“每个请求的动态路由”要求,我可以使用自定义的ViewEngine或其他东西......

有没有人知道如何实现这一点(使用MVC板载工具)?我真的想继续使用Html.RenderPartial方法和MVC的其他“好处”...这就是为什么我更喜欢用MVC方法/覆盖/等来解决这个问题。

更新

  • 网站设计,内容不同,使用“RenderPartial”等等。
  • 观点真的不一样......不仅仅是主页..
  • 控制器中的ID参数用于识别应使用的“文件夹”(例如custom_design_01)
  • 网址将如下所示:“/ Stores / {id_of_design} /”调用示例控制器方法..然后ctonroller返回视图“/Store/{id_of_design}/Views/zbf.aspx”,例如

谢谢!

3 个答案:

答案 0 :(得分:2)

我建议您使用专门的ViewEngine和一些自定义搜索位置。由于您仍在使用WebForms页面,因此您可以从集成的WebFormViewEngine中派生出来:

public class ThemedViewEngine : WebFormViewEngine {

    private static readonly string[] _emptyLocations = new string[0];

    //Format: {0} page name {1} controller {2} design
    string[] _masterLocations = new[] {
        "~/Views/{2}/{0}.master"
    };

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

    #region View search

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) {
        string[] viewLocationsSearched;
        string[] masterLocationsSearched;

        string viewPath = FindPath(controllerContext, _viewLocations, viewName, _cacheKeyPrefix_View, useCache, out viewLocationsSearched);
        string masterPath = FindPath(controllerContext, _masterLocations, masterName, _cacheKeyPrefix_Master, useCache, out masterLocationsSearched);

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

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

    //Same thing as above, but without master page
    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) {
        string[] viewLocationsSearched;

        string viewPath = FindPath(controllerContext, _viewLocations, partialViewName, _cacheKeyPrefix_Partial, useCache, out viewLocationsSearched);

        if (String.IsNullOrEmpty(viewPath)) {
            return new ViewEngineResult(viewLocationsSearched);
        }

        return new ViewEngineResult(CreatePartialView(controllerContext, viewPath), this);  
    }

    protected string FindPath(ControllerContext context, string[] locations, string viewName, string prefix, bool useCache, out string[] searched) {
        searched = _emptyLocations;

        if (string.IsNullOrEmpty(viewName))
            return string.Empty;

        //Prepare your data here
        string controllerName = context.RouteData.GetRequiredString("controller");
        string designName = /* YOUR WAY OF GETTING THE DESIGN */;

        string result = FindPathGeneral(context, locations, viewName, controllerName, designName, out searched);

        return result;
    }

    /// <summary>
    /// Finds the complete path for a general view page.
    /// </summary>
    private string FindPathGeneral(ControllerContext context, string[] locations, string viewName, string controllerName, string designName, out string[] searched) {
        string result = string.Empty;
        searched = new string[locations.Length];

        for (int i = 0; i < locations.Length; i++) {
            //Build virtual path from the locations defined on top
            string virtualPath = String.Format(CultureInfo.InvariantCulture, locations[i],
                viewName, controllerName, designName);

            if (FileExists(context, virtualPath)) {
                searched = _emptyLocations;
                return virtualPath;
            }

            searched[i] = virtualPath;
        }

        return result;
    }
    #endregion
}

这应该适合您的情况,前提是您在FindPath方法中找到了所需的设计名称。

为简单起见,上面的示例未实现视图缓存。您可以使用反射器(或源代码)查看WebFormViewEngine代码,以了解框架是如何完成的。

答案 1 :(得分:0)

您可以解决此问题的一种方法是创建一个Controller子类作为控制器的公共基类。您可以定义一个替代操作函数,如View(),它使用路径数据和方法参数来决定应该使用哪个视图,然后只返回一个标准ViewResult实例。 (您可以从路径中的URL中提取信息,该路径不直接用于映射到控制器或其参数。)

然而,创建WebFormViewEngine的自定义子类可能会更清晰(因为人们可能希望找到那种代码)。然后,如果不是所有的控制器都有这种行为,第一种方法可能会更好。

答案 2 :(得分:0)

这听起来很像区域,检查herehere,以及“asp.net mvc区域”的共同点。我只是不确定“默认”区域,但我认为这可以做到。请注意,ASP.NET MVC v2(现在处于Beta版本)具有本机区域支持。