在Nop中有一个Html扩展的类叫HtmlExtensions,主要源码:
public static class HtmlExtensions { #region Admin area extensions public static MvcHtmlString Hint(this HtmlHelper helper, string value) { // Create tag builder var builder = new TagBuilder("img"); // Add attributes var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); var url = MvcHtmlString.Create(urlHelper.Content("~/Administration/Content/images/ico-help.gif")).ToHtmlString(); builder.MergeAttribute("src", url); builder.MergeAttribute("alt", value); builder.MergeAttribute("title", value); // Render tag return MvcHtmlString.Create(builder.ToString()); } public static HelperResult LocalizedEditor<T, TLocalizedModelLocal>(this HtmlHelper<T> helper, string name, Func<int, HelperResult> localizedTemplate, Func<T, HelperResult> standardTemplate, bool ignoreIfSeveralStores = false) where T : ILocalizedModel<TLocalizedModelLocal> where TLocalizedModelLocal : ILocalizedModelLocal { return new HelperResult(writer => { var localizationSupported = helper.ViewData.Model.Locales.Count > 1; if (ignoreIfSeveralStores) { var storeService = EngineContext.Current.Resolve<IStoreService>(); if (storeService.GetAllStores().Count >= 2) { localizationSupported = false; } } if (localizationSupported) { var tabStrip = new StringBuilder(); tabStrip.AppendLine(string.Format("<div id='{0}'>", name)); tabStrip.AppendLine("<ul>"); //default tab tabStrip.AppendLine("<li class='k-state-active'>"); tabStrip.AppendLine("Standard"); tabStrip.AppendLine("</li>"); foreach (var locale in helper.ViewData.Model.Locales) { //languages var language = EngineContext.Current.Resolve<ILanguageService>().GetLanguageById(locale.LanguageId); tabStrip.AppendLine("<li>"); var urlHelper = new UrlHelper(helper.ViewContext.RequestContext); var iconUrl = urlHelper.Content("~/Content/images/flags/" + language.FlagImageFileName); tabStrip.AppendLine(string.Format("<img class='k-image' alt='' src='{0}'>", iconUrl)); tabStrip.AppendLine(HttpUtility.HtmlEncode(language.Name)); tabStrip.AppendLine("</li>"); } tabStrip.AppendLine("</ul>"); //default tab tabStrip.AppendLine("<div>"); tabStrip.AppendLine(standardTemplate(helper.ViewData.Model).ToHtmlString()); tabStrip.AppendLine("</div>"); for (int i = 0; i < helper.ViewData.Model.Locales.Count; i++) { //languages tabStrip.AppendLine("<div>"); tabStrip.AppendLine(localizedTemplate(i).ToHtmlString()); tabStrip.AppendLine("</div>"); } tabStrip.AppendLine("</div>"); tabStrip.AppendLine("<script>"); tabStrip.AppendLine("$(document).ready(function() {"); tabStrip.AppendLine(string.Format("$('#{0}').kendoTabStrip(", name)); tabStrip.AppendLine("{"); tabStrip.AppendLine("animation: {"); tabStrip.AppendLine("open: {"); tabStrip.AppendLine("effects: "fadeIn""); tabStrip.AppendLine("}"); tabStrip.AppendLine("}"); tabStrip.AppendLine("});"); tabStrip.AppendLine("});"); tabStrip.AppendLine("</script>"); writer.Write(new MvcHtmlString(tabStrip.ToString())); } else { standardTemplate(helper.ViewData.Model).WriteTo(writer); } }); } public static MvcHtmlString DeleteConfirmation<T>(this HtmlHelper<T> helper, string buttonsSelector) where T : BaseNopEntityModel { return DeleteConfirmation(helper, "", buttonsSelector); } public static MvcHtmlString DeleteConfirmation<T>(this HtmlHelper<T> helper, string actionName, string buttonsSelector) where T : BaseNopEntityModel { if (String.IsNullOrEmpty(actionName)) actionName = "Delete"; var modalId = MvcHtmlString.Create(helper.ViewData.ModelMetadata.ModelType.Name.ToLower() + "-delete-confirmation") .ToHtmlString(); var deleteConfirmationModel = new DeleteConfirmationModel { Id = helper.ViewData.Model.Id, ControllerName = helper.ViewContext.RouteData.GetRequiredString("controller"), ActionName = actionName, WindowId = modalId }; var window = new StringBuilder(); window.AppendLine(string.Format("<div id='{0}' style='display:none;'>", modalId)); window.AppendLine(helper.Partial("Delete", deleteConfirmationModel).ToHtmlString()); window.AppendLine("</div>"); window.AppendLine("<script>"); window.AppendLine("$(document).ready(function() {"); window.AppendLine(string.Format("$('#{0}').click(function (e) ", buttonsSelector)); window.AppendLine("{"); window.AppendLine("e.preventDefault();"); window.AppendLine(string.Format("var window = $('#{0}');", modalId)); window.AppendLine("if (!window.data('kendoWindow')) {"); window.AppendLine("window.kendoWindow({"); window.AppendLine("modal: true,"); window.AppendLine(string.Format("title: '{0}',", EngineContext.Current.Resolve<ILocalizationService>().GetResource("Admin.Common.AreYouSure"))); window.AppendLine("actions: ['Close']"); window.AppendLine("});"); window.AppendLine("}"); window.AppendLine("window.data('kendoWindow').center().open();"); window.AppendLine("});"); window.AppendLine("});"); window.AppendLine("</script>"); return MvcHtmlString.Create(window.ToString()); } public static MvcHtmlString NopLabelFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, bool displayHint = true) { var result = new StringBuilder(); var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); var hintResource = string.Empty; object value; if (metadata.AdditionalValues.TryGetValue("NopResourceDisplayName", out value)) { var resourceDisplayName = value as NopResourceDisplayName; if (resourceDisplayName != null && displayHint) { var langId = EngineContext.Current.Resolve<IWorkContext>().WorkingLanguage.Id; hintResource = EngineContext.Current.Resolve<ILocalizationService>() .GetResource(resourceDisplayName.ResourceKey + ".Hint", langId); result.Append(helper.Hint(hintResource).ToHtmlString()); } } result.Append(helper.LabelFor(expression, new { title = hintResource })); return MvcHtmlString.Create(result.ToString()); } public static MvcHtmlString OverrideStoreCheckboxFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, Expression<Func<TModel, TValue>> forInputExpression, int activeStoreScopeConfiguration) { var dataInputIds = new List<string>(); dataInputIds.Add(helper.FieldIdFor(forInputExpression)); return OverrideStoreCheckboxFor(helper, expression, activeStoreScopeConfiguration, null, dataInputIds.ToArray()); } public static MvcHtmlString OverrideStoreCheckboxFor<TModel, TValue1, TValue2>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, Expression<Func<TModel, TValue1>> forInputExpression1, Expression<Func<TModel, TValue2>> forInputExpression2, int activeStoreScopeConfiguration) { var dataInputIds = new List<string>(); dataInputIds.Add(helper.FieldIdFor(forInputExpression1)); dataInputIds.Add(helper.FieldIdFor(forInputExpression2)); return OverrideStoreCheckboxFor(helper, expression, activeStoreScopeConfiguration, null, dataInputIds.ToArray()); } public static MvcHtmlString OverrideStoreCheckboxFor<TModel, TValue1, TValue2, TValue3>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, Expression<Func<TModel, TValue1>> forInputExpression1, Expression<Func<TModel, TValue2>> forInputExpression2, Expression<Func<TModel, TValue3>> forInputExpression3, int activeStoreScopeConfiguration) { var dataInputIds = new List<string>(); dataInputIds.Add(helper.FieldIdFor(forInputExpression1)); dataInputIds.Add(helper.FieldIdFor(forInputExpression2)); dataInputIds.Add(helper.FieldIdFor(forInputExpression3)); return OverrideStoreCheckboxFor(helper, expression, activeStoreScopeConfiguration, null, dataInputIds.ToArray()); } public static MvcHtmlString OverrideStoreCheckboxFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, string parentContainer, int activeStoreScopeConfiguration) { return OverrideStoreCheckboxFor(helper, expression, activeStoreScopeConfiguration, parentContainer); } private static MvcHtmlString OverrideStoreCheckboxFor<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, int activeStoreScopeConfiguration, string parentContainer = null, params string[] datainputIds) { if (String.IsNullOrEmpty(parentContainer) && datainputIds == null) throw new ArgumentException("Specify at least one selector"); var result = new StringBuilder(); if (activeStoreScopeConfiguration > 0) { //render only when a certain store is chosen const string cssClass = "multi-store-override-option"; string dataInputSelector = ""; if (!String.IsNullOrEmpty(parentContainer)) { dataInputSelector = "#" + parentContainer + " input, #" + parentContainer + " textarea, #" + parentContainer + " select"; } if (datainputIds != null && datainputIds.Length > 0) { dataInputSelector = "#" + String.Join(", #", datainputIds); } var onClick = string.Format("checkOverriddenStoreValue(this, '{0}')", dataInputSelector); result.Append(helper.CheckBoxFor(expression, new Dictionary<string, object> { { "class", cssClass }, { "onclick", onClick }, { "data-for-input-selector", dataInputSelector }, })); } return MvcHtmlString.Create(result.ToString()); } /// <summary> /// Render CSS styles of selected index /// </summary> /// <param name="helper">HTML helper</param> /// <param name="currentIndex">Current tab index (where appropriate CSS style should be rendred)</param> /// <param name="indexToSelect">Tab index to select</param> /// <returns>MvcHtmlString</returns> public static MvcHtmlString RenderSelectedTabIndex(this HtmlHelper helper, int currentIndex, int indexToSelect) { if (helper == null) throw new ArgumentNullException("helper"); //ensure it's not negative if (indexToSelect < 0) indexToSelect = 0; //required validation if (indexToSelect == currentIndex) { return new MvcHtmlString(" class='k-state-active'"); } return new MvcHtmlString(""); } #endregion #region Common extensions public static MvcHtmlString RequiredHint(this HtmlHelper helper, string additionalText = null) { // Create tag builder var builder = new TagBuilder("span"); builder.AddCssClass("required"); var innerText = "*"; //add additional text if specified if (!String.IsNullOrEmpty(additionalText)) innerText += " " + additionalText; builder.SetInnerText(innerText); // Render tag return MvcHtmlString.Create(builder.ToString()); } public static string FieldNameFor<T, TResult>(this HtmlHelper<T> html, Expression<Func<T, TResult>> expression) { return html.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); } public static string FieldIdFor<T, TResult>(this HtmlHelper<T> html, Expression<Func<T, TResult>> expression) { var id = html.ViewData.TemplateInfo.GetFullHtmlFieldId(ExpressionHelper.GetExpressionText(expression)); // because "[" and "]" aren't replaced with "_" in GetFullHtmlFieldId return id.Replace('[', '_').Replace(']', '_'); } /// <summary> /// Creates a days, months, years drop down list using an HTML select control. /// The parameters represent the value of the "name" attribute on the select control. /// </summary> /// <param name="html">HTML helper</param> /// <param name="dayName">"Name" attribute of the day drop down list.</param> /// <param name="monthName">"Name" attribute of the month drop down list.</param> /// <param name="yearName">"Name" attribute of the year drop down list.</param> /// <param name="beginYear">Begin year</param> /// <param name="endYear">End year</param> /// <param name="selectedDay">Selected day</param> /// <param name="selectedMonth">Selected month</param> /// <param name="selectedYear">Selected year</param> /// <param name="localizeLabels">Localize labels</param> /// <returns></returns> public static MvcHtmlString DatePickerDropDowns(this HtmlHelper html, string dayName, string monthName, string yearName, int? beginYear = null, int? endYear = null, int? selectedDay = null, int? selectedMonth = null, int? selectedYear = null, bool localizeLabels = true) { var daysList = new TagBuilder("select"); var monthsList = new TagBuilder("select"); var yearsList = new TagBuilder("select"); daysList.Attributes.Add("name", dayName); monthsList.Attributes.Add("name", monthName); yearsList.Attributes.Add("name", yearName); var days = new StringBuilder(); var months = new StringBuilder(); var years = new StringBuilder(); string dayLocale, monthLocale, yearLocale; if (localizeLabels) { var locService = EngineContext.Current.Resolve<ILocalizationService>(); dayLocale = locService.GetResource("Common.Day"); monthLocale = locService.GetResource("Common.Month"); yearLocale = locService.GetResource("Common.Year"); } else { dayLocale = "Day"; monthLocale = "Month"; yearLocale = "Year"; } days.AppendFormat("<option value='{0}'>{1}</option>", "0", dayLocale); for (int i = 1; i <= 31; i++) days.AppendFormat("<option value='{0}'{1}>{0}</option>", i, (selectedDay.HasValue && selectedDay.Value == i) ? " selected="selected"" : null); months.AppendFormat("<option value='{0}'>{1}</option>", "0", monthLocale); for (int i = 1; i <= 12; i++) { months.AppendFormat("<option value='{0}'{1}>{2}</option>", i, (selectedMonth.HasValue && selectedMonth.Value == i) ? " selected="selected"" : null, CultureInfo.CurrentUICulture.DateTimeFormat.GetMonthName(i)); } years.AppendFormat("<option value='{0}'>{1}</option>", "0", yearLocale); if (beginYear == null) beginYear = DateTime.UtcNow.Year - 100; if (endYear == null) endYear = DateTime.UtcNow.Year; if (endYear > beginYear) { for (int i = beginYear.Value; i <= endYear.Value; i++) years.AppendFormat("<option value='{0}'{1}>{0}</option>", i, (selectedYear.HasValue && selectedYear.Value == i) ? " selected="selected"" : null); } else { for (int i = beginYear.Value; i >= endYear.Value; i--) years.AppendFormat("<option value='{0}'{1}>{0}</option>", i, (selectedYear.HasValue && selectedYear.Value == i) ? " selected="selected"" : null); } daysList.InnerHtml = days.ToString(); monthsList.InnerHtml = months.ToString(); yearsList.InnerHtml = years.ToString(); return MvcHtmlString.Create(string.Concat(daysList, monthsList, yearsList)); } public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object additionalData = null) { return helper.Action("WidgetsByZone", "Widget", new { widgetZone = widgetZone, additionalData = additionalData }); } /// <summary> /// Renders the standard label with a specified suffix added to label text /// </summary> /// <typeparam name="TModel">Model</typeparam> /// <typeparam name="TValue">Value</typeparam> /// <param name="html">HTML helper</param> /// <param name="expression">Expression</param> /// <param name="htmlAttributes">HTML attributes</param> /// <param name="suffix">Suffix</param> /// <returns>Label</returns> public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes, string suffix) { string htmlFieldName = ExpressionHelper.GetExpressionText(expression); var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); string resolvedLabelText = metadata.DisplayName ?? (metadata.PropertyName ?? htmlFieldName.Split(new [] { '.' }).Last()); if (string.IsNullOrEmpty(resolvedLabelText)) { return MvcHtmlString.Empty; } var tag = new TagBuilder("label"); tag.Attributes.Add("for", TagBuilder.CreateSanitizedId(html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFieldName))); if (!String.IsNullOrEmpty(suffix)) { resolvedLabelText = String.Concat(resolvedLabelText, suffix); } tag.SetInnerText(resolvedLabelText); var dictionary = ((IDictionary<string, object>)HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); tag.MergeAttributes(dictionary, true); return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal)); } #endregion }
主要是看Widget这个扩展方法
public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object additionalData = null) { return helper.Action("WidgetsByZone", "Widget", new { widgetZone = widgetZone, additionalData = additionalData }); }
添加这个类后,就可以在cshtml页面中直接@Html.Widget了
看Widget实现,还是调用的Action方法,第一个参数action名字,第二参数Controller
找到WidgetController下面的WidgetsByZone方法
[ChildActionOnly]
public ActionResult WidgetsByZone(string widgetZone, object additionalData = null) { var cacheKey = string.Format(ModelCacheEventConsumer.WIDGET_MODEL_KEY, _storeContext.CurrentStore.Id, widgetZone); var cacheModel = _cacheManager.Get(cacheKey, () => { //model var model = new List<RenderWidgetModel>(); var widgets = _widgetService.LoadActiveWidgetsByWidgetZone(widgetZone, _storeContext.CurrentStore.Id); foreach (var widget in widgets) { var widgetModel = new RenderWidgetModel(); string actionName; string controllerName; RouteValueDictionary routeValues; widget.GetDisplayWidgetRoute(widgetZone, out actionName, out controllerName, out routeValues); widgetModel.ActionName = actionName; widgetModel.ControllerName = controllerName; widgetModel.RouteValues = routeValues; model.Add(widgetModel); } return model; }); //no data? if (cacheModel.Count == 0) return Content(""); //"RouteValues" property of widget models depends on "additionalData". //We need to clone the cached model before modifications (the updated one should not be cached) var clonedModel = new List<RenderWidgetModel>(); foreach (var widgetModel in cacheModel) { var clonedWidgetModel = new RenderWidgetModel(); clonedWidgetModel.ActionName = widgetModel.ActionName; clonedWidgetModel.ControllerName = widgetModel.ControllerName; if (widgetModel.RouteValues != null) clonedWidgetModel.RouteValues = new RouteValueDictionary(widgetModel.RouteValues); if (additionalData != null) { if (clonedWidgetModel.RouteValues == null) clonedWidgetModel.RouteValues = new RouteValueDictionary(); clonedWidgetModel.RouteValues.Add("additionalData", additionalData); } clonedModel.Add(clonedWidgetModel); } return PartialView(clonedModel); }
[ChildActionOnly]
Child action 和 Patial view 类似,也是在应用程序的不同地方可以重复利用相同的子内容。不同的是,它是通过调用 controller 中的 action 方法来呈现子内容的,并且一般包含了业务的处理。任何 action 都可以作为子 action 。接下来介绍如何使用它。详细信息请见 http://www.cnblogs.com/willick/archive/2013/11/14/3410855.html
var widgets = _widgetService.LoadActiveWidgetsByWidgetZone(widgetZone, _storeContext.CurrentStore.Id);
这个WidgetByZone就是查找当前已使用的@Html.Widget(“xxx”)并返回相应的控制器名字,方法名和路由值
打开试图WidgetWidgetsByZone.cshtml
@model List<RenderWidgetModel> @using Nop.Web.Models.Cms; @foreach (var widget in Model) { @Html.Action(widget.ActionName, widget.ControllerName, widget.RouteValues) }
这个视图的目的就是循环输出html,具体输出的内容在插件里面实现的,比如插件Nop.Plugin.Widgets.NivoSlider里面有个NivoSliderPlugin,这类插件必须继承自BasePlugin,和IWidgetPlugin,里面的方法GetDisplayWidgetRoute就是用于返回显示这个插件内容的action的信息,WidgetsNivoSliderController.cs里面的public ActionResult PublicInfo(string widgetZone)就是这个插件具体输出的内容,大体流程就是这样了。