4

我正在尝试覆盖位于以下位置的 nopcommerce 视图:

Nop.Admin/Views/Category/Tree.cshtml  

根据我在插件文件夹中开发的视图:

Views/Misc/Tree.cshtml

我该怎么做?

4

3 回答 3

4

试试我写的这篇详细的文章:在你的 nopCommerce 插件中显示视图的 3 种方法(嵌入式资源、主题覆盖和自定义视图引擎)

于 2013-06-29T09:32:02.460 回答
4

@wooncherk 的自定义视图引擎非常适合让我们的视图在未来被轻松覆盖。然而,在覆盖现有的核心视图方面它还不够,因为 nopCommerce 给予管理视图的优先级高于我们的自定义视图。这可以从 的虚方法GetPath()中看出Nop.Web.Framework.Themes.ThemeableVirtualPathProviderViewEngine.cs。对于那些想知道的人,ThemeableVirtualPathProviderViewEngineThemeableRazorViewEngine由@wooncherk 的类继承的CustomViewEngine类。

ThemeableVirtualPathProviderViewEngine 参考上面的截图ThemeableVirtualPathProviderViewEngine,如箭头所示,这两行确认管理视图总是比我们的自定义视图具有更高的优先级

我设法扩展了@wooncherk 的自定义视图引擎方法,以适应覆盖现有的管理核心视图。这涉及覆盖虚拟方法并将其复制GetPath()CustomViewEngine类中。在这一点上,删除两个罪魁祸首甚至整个小黑客代码块似乎是合乎逻辑的,但不要这样做,它会导致异常

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Nop.Admin.Models.Cms.RenderWidgetModel]', but this dictionary requires a model item of type 'System.Collections.Generic.List`1[Nop.Web.Models.Cms.RenderWidgetModel]'.

新的CustomViewEngine将如下:

public class CustomViewEngine: ThemeableRazorViewEngine {
    private readonly string[] _emptyLocations = null;

    public CustomViewEngine() {
        PartialViewLocationFormats = new[] {
            "~/Administration/CustomExtension/Views/{1}/{0}.cshtml",
            "~/Administration/CustomExtension/Views/Shared/{0}.cshtml"
        };

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

    protected override string GetPath(ControllerContext controllerContext, string[] locations, string[] areaLocations, string locationsPropertyName, string name, string controllerName, string theme, string cacheKeyPrefix, bool useCache, out string[] searchedLocations) {
        searchedLocations = _emptyLocations;
        if (string.IsNullOrEmpty(name)) {
            return string.Empty;
        }
        string areaName = GetAreaName(controllerContext.RouteData);

        //little hack to get nop's admin area to be in /Administration/ instead of /Nop/Admin/ or Areas/Admin/
        if (!string.IsNullOrEmpty(areaName) && areaName.Equals("admin", StringComparison.InvariantCultureIgnoreCase)) {
            var newLocations = areaLocations.ToList();
            newLocations.Insert(0, "~/Administration/Views/{1}/{0}.cshtml");
            newLocations.Insert(0, "~/Administration/Views/Shared/{0}.cshtml");

            //Insert your custom View locations to the top of the list to be given a higher precedence
            newLocations.Insert(0, "~/Administration/CustomExtension/Views/{1}/{0}.cshtml");
            newLocations.Insert(0, "~/Administration/CustomExtension/Views/Shared/{0}.cshtml");

            areaLocations = newLocations.ToArray();
        }

        bool flag = !string.IsNullOrEmpty(areaName);
        List<ViewLocation> viewLocations = GetViewLocations(locations, flag ? areaLocations : null);
        if (viewLocations.Count == 0) {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Properties cannot be null or empty.", new object[] { locationsPropertyName }));
        }
        bool flag2 = IsSpecificPath(name);
        string key = CreateCacheKey(cacheKeyPrefix, name, flag2 ? string.Empty : controllerName, areaName, theme);
        if (useCache) {
            var cached = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
            if (cached != null) {
                return cached;
            }
        }
        if (!flag2) {
            return GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, theme, key, ref searchedLocations);
        }
        return GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
    }
}

请注意,在罪魁祸首行下方添加了两行,以赋予我们的自定义视图更高的优先级。


最后,我们需要修改RouteProvider.cs

public class RouteProvider : IRouteProvider {
    public void RegisterRoutes(RouteCollection routes) {
        //Insert our CustomViewEngine into the top of the System.Web.Mvc.ViewEngines.Engines Collection to be given a higher precedence
        System.Web.Mvc.ViewEngines.Engines.Insert(0, new CustomViewEngine());
    }

    public int Priority {
        get {
            return 1;
        }
    }
}

就是这样。现在将您的自定义视图/部分视图放入视图位置,在这种情况下,它们是

~/Administration/CustomExtension/Views/{1}/{0}.cshtml
~/Administration/CustomExtension/Views/Shared/{0}.cshtml

其中 {1} 是您要覆盖的控制器的名称,而 {0} 是您要覆盖的视图/部分视图的名称。

例如,如果您是 overrding Nop.Admin/Views/Category/Tree.cshtml,请将您的自定义Tree.cshtml放在Nop.Admin/CustomExtension/Views/Category/Tree.cshtml.

于 2014-08-25T08:58:05.557 回答
2

Twisted Whisper 有正确的答案,但我想我会分享一个博客文章的链接,该文章更深入地讨论了这个问题(更多地讨论了自定义视图引擎的工作原理以及使用这种技术的其他优点),请参见此处:

点击此处查看深度讨论帖

于 2014-08-25T22:38:18.617 回答