/// <summary> /// List multi-select for property, instead of /// drop down list with custom attributes. /// </summary> /// <exception cref="ArgumentNullException"> /// When expression or source is null. /// </exception> /// <typeparam name="TModel">Type of model.</typeparam> /// <typeparam name="TProperty">Type of property.</typeparam> /// <param name="htmlHelper">HtmlHelper.</param> /// <param name="expression">Expression to select needed property.</param> /// <param name="source">Source of list to select from.</param> /// <param name="htmlAttributes">Html attributes.</param> /// <returns>Html code of multi-select list to select from.</returns> public static MvcHtmlString ListMultiSelectorFor <TModel, TProperty>( this HtmlHelper <TModel> htmlHelper, Expression <Func <TModel, TProperty> > expression, SelectList source, object htmlAttributes) { FluentTagBuilder listMultiSelectorFor = htmlHelper.ConstructListSelector( expression, source, true, htmlAttributes); return(new MvcHtmlString(listMultiSelectorFor.Render())); }
/// <summary> /// Construct tree view for selected property. /// </summary> /// <typeparam name="TModel">Type of model.</typeparam> /// <typeparam name="TProperty">Type of selected property.</typeparam> /// <param name="helper">Html helper.</param> /// <param name="expression">Expression which selects property.</param> /// <param name="source">Source for tree view.</param> /// <param name="sourceUrl">Url to get child items using Ajax.</param> /// <param name="searchExpression">Search expression to filter leaves.</param> /// <returns>Constructed Tree view.</returns> public static MvcHtmlString TreeViewFor <TModel, TProperty>( this HtmlHelper <TModel> helper, Expression <Func <TModel, TProperty> > expression, TreeViewSource <TProperty> source, string sourceUrl, string searchExpression) where TProperty : struct { // CSS classes string treeViewCssClass = "tree-view"; string levelCssClassFormat = "level-{0}"; string errorCssClass = string.Format("{0}__error", treeViewCssClass); string rootListCssClass = "root-list"; // Attribute keys string levelAttribute = "data-tv-level"; string forAttribute = "data-tv-for"; string sourceUrlAttribute = "data-tv-source-url"; string searchExpresionAttribute = "data-tv-search-exp"; // Get model name from expression string modelName = ExpressionHelper.GetExpressionText(expression); // Construct root list FluentTagBuilder rootList = helper.ConstructList(expression, source) .AddCssClass(string.Format(levelCssClassFormat, "0")) .AddCssClass(rootListCssClass) .MergeAttribute(levelAttribute, "0"); // Construct tree view container FluentTagBuilder treeViewContainer = new FluentTagBuilder("div") .AppendChild(rootList) .MergeAttribute(forAttribute, modelName) .MergeAttribute(sourceUrlAttribute, sourceUrl) .MergeAttribute(searchExpresionAttribute, searchExpression) .AddCssClass(treeViewCssClass); // Add error MvcHtmlString validationMessage = helper.ValidationMessageFor(expression); FluentTagBuilder errorSpan = new FluentTagBuilder("span") .AddCssClass(errorCssClass) .AppendChild(validationMessage); treeViewContainer.AppendChild(errorSpan); return(new MvcHtmlString(treeViewContainer.Render())); }
/// <summary> /// Generates hidden indexer input for model. /// </summary> /// <param name="helper">HtmlHelper.</param> /// <param name="collectionName">Name of collection.</param> /// <param name="collectionIndex">Value of index.</param> /// <param name="htmlAttributes">Html attributes to attach to input.</param> /// <returns>Indexer input for model.</returns> public static MvcHtmlString HiddenIndexerForModel <TModel>( this HtmlHelper <TModel> helper, string collectionName, string collectionIndex, object htmlAttributes) { // Convert html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Construct name for input string inputName = string.Format("{0}.index", collectionName); // Generate input element FluentTagBuilder hiddenIndexer = new FluentTagBuilder("input") .MergeAttribute("name", inputName) .MergeAttribute("type", "hidden") .MergeAttribute("value", helper.Encode(collectionIndex)) .MergeAttribute("autocomplete", "off") .MergeAttributes(attributes); return(MvcHtmlString.Create(hiddenIndexer.Render())); }
/// <summary> /// Generate page urls according to /// url generator and paging info. /// </summary> /// <param name="helper">HtmlHelper.</param> /// <param name="pagingInfo">Information about paging.</param> /// <param name="pageUrlGenerator">Url generator for pages.</param> /// <param name="htmlAttributes">Html attributes to add to links.</param> /// <returns>Generated html links.</returns> public static MvcHtmlString PageLinks( this HtmlHelper helper, PagingInfo pagingInfo, Func <int, string> pageUrlGenerator, object htmlAttributes) { if (pagingInfo == null) { throw new ArgumentNullException(nameof(pagingInfo)); } if (pageUrlGenerator == null) { throw new ArgumentNullException(nameof(pageUrlGenerator)); } string pagingContainerCss = "paging-container"; string pageLinkCss = "page-link"; string selectedLinkCss = "selected"; // Convert html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Initialize function to generate page link Func <int, FluentTagBuilder> constructPageLink = pageIndex => new FluentTagBuilder("a") .AddCssClass(pageLinkCss) .MergeAttribute("href", pageUrlGenerator(pageIndex)) .SetInnerText(pageIndex.ToString()); // Initialize function to constryct seperator dots Func <FluentTagBuilder> constructSeperatorDots = () => new FluentTagBuilder("span") .SetInnerText("..."); FluentTagBuilder pagingContainer = new FluentTagBuilder("div") .AddCssClass(pagingContainerCss); int lastPageIndex = pagingInfo.TotalPageCount; /// First of all define begining and ending index of page /// links which will be generated by loop. Maximum number of /// 9 pages can be generated by loop. First and last pages are added /// manually. // Maximum number of 4 pages should be rendered // before and after current page int rangeBeginIndex = Math.Max(2, pagingInfo.CurrentPage - 4); int rangeEndIndex = Math.Min(lastPageIndex - 1, pagingInfo.CurrentPage + 4); /// If there are more than 11 page links then 9 of them must be /// rendered by loop, otherwise all links must be displayed. // Get total count of page links which must be generated by loop. int totalCountOfPageLinksByLoop = Math.Min(lastPageIndex - 2, 9); /// Get current count of page links which will be generated by loop /// with current settings. For expample, if maximum page count is 100 /// and current page is 4, then two pages before current page (2, 3), /// current page itself and 4 pages after current page (5, 6, 7, 8) /// will be generated by loop. This means that total of 2 + 1 + 4 = 7 /// pages will be generated by loop, but this number must be 9. /// To accomplish this we have to add remaning pages from "before the current page" /// to the pages after current page. 4 must be generated before current page /// but this number is 2, then total of 6 pages should be generated after /// current page (5, 6, 7, 8, 9, 10). int currentPageCountByLoop = rangeEndIndex - rangeBeginIndex + 1; if (totalCountOfPageLinksByLoop > 5 && currentPageCountByLoop < totalCountOfPageLinksByLoop) { // Find remaing page count. // In the example above this is 4 - 2 = 2. int remainingPageCount = totalCountOfPageLinksByLoop - currentPageCountByLoop; // Add remaining pages to the before or after the current page. if (rangeBeginIndex == 2) { rangeEndIndex += remainingPageCount; } else if (rangeEndIndex == lastPageIndex - 1) { rangeBeginIndex -= remainingPageCount; } } // Add first page if (lastPageIndex >= 1) { FluentTagBuilder firstPage = constructPageLink(1) .MergeAttributes(attributes); if (1 == pagingInfo.CurrentPage) { firstPage.AddCssClass(selectedLinkCss); } pagingContainer.AppendChild(firstPage); // Add seperator dots there is gap between first page // and loop range begin index. if (rangeBeginIndex - 1 > 1) { pagingContainer.AppendChild(constructSeperatorDots()); } } // Add begaes in-between first and last for (int i = rangeBeginIndex; i <= rangeEndIndex; i++) { FluentTagBuilder pageLink = constructPageLink(i) .MergeAttributes(attributes); // Add selected class if needed if (i == pagingInfo.CurrentPage) { pageLink.AddCssClass(selectedLinkCss); } pagingContainer.AppendChild(pageLink); } // Add last page if needed if (lastPageIndex > 1) { // Add seperator dots if there is a gap between last page // and loop range end index. if (lastPageIndex - rangeEndIndex > 1) { pagingContainer.AppendChild(constructSeperatorDots()); } FluentTagBuilder lastPage = constructPageLink(lastPageIndex) .MergeAttributes(attributes); if (lastPageIndex == pagingInfo.CurrentPage) { lastPage.AddCssClass(selectedLinkCss); } pagingContainer.AppendChild(lastPage); } return(MvcHtmlString.Create(pagingContainer.Render())); }
/// <summary> /// Render list editor builder as list. /// </summary> /// <remarks> /// This is implementation of RenderList(object htmlAttributes) for /// both of IServerListEditorBuilder and IClientListEditorBuilder. /// </remarks> /// <param name="htmlAttributes">Html attributes to add to list.</param> /// <returns>ListEditorBuilder as MvcHtmlString format.</returns> public MvcHtmlString RenderList(object htmlAttributes) { // Collection name must be provided for proper binding // list editor which is manged at client side if (BuilderType == ListEditorBuilderType.ManagedAtClientSide && string.IsNullOrEmpty(CollectionName)) { throw new InvalidOperationException( "Name of collection must be provided before rendering."); } // Declare and set css class names string listEditorForCss = "list-editor-for"; string listDataCss = "list-data"; string listDataListActionContainerCss = "list-action_container"; // Get html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Create div element and merge attributes FluentTagBuilder listEditorFor = new FluentTagBuilder("div") .MergeAttributes(attributes) .AddCssClass(listEditorForCss); // If list editor will be managed at client side we will need an editor. if (BuilderType == ListEditorBuilderType.ManagedAtClientSide) { // Construct list editor. // List editor must be constructed prior to list data. // Otherwise validation attributes will be // added to hidden input elements inside list data. listEditorFor.AppendChild(ConstructListEditor()); } // Initialize list data div FluentTagBuilder listData = new FluentTagBuilder("div") .AddCssClass(listDataCss); // Initialize list action container and add to listData if needed. FluentTagBuilder listActionContainer = null; if (BuilderType == ListEditorBuilderType.ManagedAtClientSide) { listActionContainer = ConstructClientSideListActionContainer(); } else if (BuilderType == ListEditorBuilderType.ManagedAtServerSide) { listActionContainer = ConstructServerSideListActionContainer(); } if (listActionContainer != null) { listActionContainer.AddCssClass(listDataListActionContainerCss); listData.AppendChild(listActionContainer); } if (Data != null && Data.Count() > 0) { if (BuilderType == ListEditorBuilderType.ManagedAtClientSide) { // If builder will be managed at client side we need // to add indexers to be able to bind correctly // and action buttons for managing items. foreach (TModel model in Data) { using (Helper.BeginCollectionItem(CollectionName, false)) { // Append hidden indexer for correctly binding // data and action container for the row. FluentTagBuilder listDataRow = ConstructListDataRow(model) .AppendChild(Helper.HiddenIndexerForModel()) .AppendChild(ConstructClientSideListItemActionContainer(model)); // Add listDataRow to listData listData.AppendChild(listDataRow); } } } else { // If builder will be managed at server side we need // to add links for managing items. foreach (TModel model in Data) { FluentTagBuilder listDataRow = ConstructListDataRow(model) .AppendChild(ConstructServerSideListItemActionContainer(model)); // Add listDataRow to listData listData.AppendChild(listDataRow); } } } listEditorFor.AppendChild(listData); return(new MvcHtmlString(listEditorFor.Render())); }
/// <summary> /// Render sorting controller. /// </summary> /// <param name="htmlAttributes">HtmlAttributes to add to control.</param> /// <returns>MvcHtmlString.</returns> public MvcHtmlString Render(object htmlAttributes) { if (SortingFieldCollection == null || SortingFieldCollection.Count == 0) { throw new InvalidOperationException("At list one field must be configured to enable sorting."); } string sortingControlCss = "sorting-control"; string controlFieldCss = "sorting-control-field"; string controlOrderCss = "sorting-control-order"; string controlHeaderCss = "sorting-control-header"; string controlListCss = "sorting-control-list"; string selectedCss = "selected"; // Initialize sorting field list FluentTagBuilder sortingFieldList = new FluentTagBuilder("ul") .AddCssClass(controlListCss); // Currently sorted according to this field ISortingFieldCore <TModel> currentlySortedField = null; // Loop thorugh configured fields and add links for them foreach (ISortingFieldCore <TModel> fieldCore in SortingFieldCollection) { string fieldPropertyName = fieldCore.GetPropertyName(); FluentTagBuilder link = new FluentTagBuilder("a") .MergeAttribute("href", SortFieldUrlGenerator(fieldPropertyName)) .SetInnerText(fieldCore.FieldLabel); FluentTagBuilder listItem = new FluentTagBuilder("li") .AppendChild(link); if (fieldPropertyName == Info.PropertyName) { // Store currently sorted field for further use // and add selected class to it. currentlySortedField = fieldCore; listItem.AddCssClass(selectedCss); } // Append item to list sortingFieldList.AppendChild(listItem); } // Initialize down icon FluentTagBuilder downIcon = new FluentTagBuilder("i") .AddCssClass("default-icons") .SetInnerHtml("keyboard_arrow_down"); // Initialize sorting field header FluentTagBuilder fieldHeaderSpan = new FluentTagBuilder("span") .SetInnerText(currentlySortedField.FieldLabel); FluentTagBuilder fieldHeader = new FluentTagBuilder("div") .AddCssClass(controlHeaderCss) .AppendChild(fieldHeaderSpan) .AppendChild(downIcon); // Initialize sorting field FluentTagBuilder sortingField = new FluentTagBuilder("div") .AddCssClass(controlFieldCss) .AppendChild(fieldHeader) .AppendChild(sortingFieldList); // Initialize order list FluentTagBuilder sortingOrderList = new FluentTagBuilder("ul") .AddCssClass(controlListCss); // Loop through availabel orders and create list items for them foreach (SortOrder order in Enum.GetValues(typeof(SortOrder))) { // Determine label string label = order == SortOrder.Asc ? AscendingLabel : DescendingLabel; FluentTagBuilder link = new FluentTagBuilder("a") .MergeAttribute("href", SortOrderUrlGenerator(order)) .SetInnerText(label); FluentTagBuilder listItem = new FluentTagBuilder("li") .AppendChild(link); if (order == Info.Order) { listItem.AddCssClass(selectedCss); } // Append item to list sortingOrderList.AppendChild(listItem); } // Initialize sorting order header string orderHeaderLabel = Info.Order == SortOrder.Asc ? AscendingLabel : DescendingLabel; FluentTagBuilder orderHeaderSpan = new FluentTagBuilder("span") .SetInnerText(orderHeaderLabel); FluentTagBuilder orderHeader = new FluentTagBuilder("div") .AddCssClass(controlHeaderCss) .AppendChild(orderHeaderSpan) .AppendChild(downIcon); // Initialize sorting order FluentTagBuilder sortingOrder = new FluentTagBuilder("div") .AddCssClass(controlOrderCss) .AppendChild(orderHeader) .AppendChild(sortingOrderList); // Extract htmlAttribytes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Initialize sorting control, merge attributes and add controls FluentTagBuilder sortingControl = new FluentTagBuilder("div") .MergeAttributes(attributes) .AddCssClass(sortingControlCss) .AppendChild(sortingField) .AppendChild(sortingOrder); return(MvcHtmlString.Create(sortingControl.Render())); }
/// <summary> /// Create drop down button for selected property, /// with given source. /// </summary> /// <exception cref="ArgumentNullException"> /// When expression or source is null. /// </exception> /// <typeparam name="TModel">Type of model.</typeparam> /// <typeparam name="TProperty">Type of property.</typeparam> /// <param name="htmlHelper">HtmlHelper.</param> /// <param name="expression">Expression to select needed property.</param> /// <param name="source">Source of list to select from.</param> /// <param name="optionLabel">Label of drop down button if selection has not been made.</param> /// <param name="allowDefault">Allow to select default item without value.</param> /// <param name="htmlAttributes">Html attributes.</param> /// <returns>Drop down button mvc string.</returns> public static MvcHtmlString DropDownButtonFor <TModel, TProperty>( this HtmlHelper <TModel> htmlHelper, Expression <Func <TModel, TProperty> > expression, SelectList source, string optionLabel, bool allowDefault, object htmlAttributes) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (source == null || source.Items == null) { throw new ArgumentNullException(nameof(source)); } string dropDownCssClass = "dropdown-button"; string buttonContainerCssClass = "dropdown-button-container"; string headerCssClass = "dropdown-button-header"; string menuCssClass = "dropdown-menu"; string errorCssClass = "dropdown-button__error"; string selectedCssClass = "selected"; // Get model metadata ModelMetadata metadata = ModelMetadata .FromLambdaExpression(expression, htmlHelper.ViewData); // Convert html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Get modelValue string modelValue = string.Empty; if (metadata.Model != null && !string.IsNullOrEmpty(metadata.Model.ToString())) { modelValue = metadata.Model.ToString(); } // Define selected item string selectedValue = string.Empty; List <SelectListItem> listeItemSource = source .Items .Cast <SelectListItem>() .ToList(); // Check if selected value explicitly setted in the source selectedValue = listeItemSource .Where(m => m.Selected) .Select(m => m.Value) .FirstOrDefault(); /// If not explicitly set get model value as a selected value /// and set appropriate list item as selected if (string.IsNullOrEmpty(selectedValue) && !string.IsNullOrEmpty(modelValue)) { selectedValue = modelValue; SelectListItem listItem = listeItemSource .Where(m => m.Value.Equals(modelValue, StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); if (listItem != null) { listItem.Selected = true; } } // Initialize parentDiv and merge attributes FluentTagBuilder dropDownDiv = new FluentTagBuilder("div") .MergeAttributes(attributes) .AddCssClass(dropDownCssClass); // Initialize and add css class of button container FluentTagBuilder buttonContainerDiv = new FluentTagBuilder("div") .AddCssClass(buttonContainerCssClass); // If any item has been selected // get text of that item as option label string buttonHeaderText = optionLabel; if (!string.IsNullOrEmpty(selectedValue)) { string selectedItemText = listeItemSource .Where(m => m.Value.Equals(selectedValue, StringComparison.OrdinalIgnoreCase)) .Select(m => m.Text) .FirstOrDefault(); // If selected item text found set buttonHeaderText to // this text if (!string.IsNullOrEmpty(selectedItemText)) { buttonHeaderText = selectedItemText; } } // Initialize down icon FluentTagBuilder downIcon = new FluentTagBuilder("i") .AddCssClass("default-icons") .SetInnerHtml("keyboard_arrow_down"); // Initialize button header FluentTagBuilder buttonHeader = new FluentTagBuilder("span") .AddCssClass(headerCssClass) .SetInnerText(buttonHeaderText); // Add icon and header into container buttonContainerDiv.AppendChild(downIcon).AppendChild(buttonHeader); // Append button container to content builder of dropdown dropDownDiv.AppendChild(buttonContainerDiv); // Initialzie drop down menu FluentTagBuilder menu = new FluentTagBuilder("ul") .AddCssClass(menuCssClass); // If allow default set to true add option to // select default item if (allowDefault) { FluentTagBuilder defaultItem = htmlHelper.CreateDropDownButtonMenuItem( expression, new SelectListItem { Text = optionLabel, Value = "", Selected = false }); // If model is null then add selected class to default item if (metadata.Model == null || string.IsNullOrEmpty(metadata.Model.ToString())) { defaultItem.AddCssClass(selectedCssClass); } menu.AppendChild(defaultItem); } // Loop throug list items and add them to menu foreach (SelectListItem listItem in listeItemSource) { // initialize menu item FluentTagBuilder menuItem = htmlHelper .CreateDropDownButtonMenuItem(expression, listItem); // If model equals to current list item // add selected class to menu item if (!string.IsNullOrEmpty(selectedValue) && string.Equals( listItem.Value, selectedValue, StringComparison.OrdinalIgnoreCase)) { menuItem.AddCssClass(selectedCssClass); } menu.AppendChild(menuItem); } // Append menu to dropdown dropDownDiv.AppendChild(menu); // Initialize error span and add css class MvcHtmlString validationMessage = htmlHelper.ValidationMessageFor(expression); FluentTagBuilder errorSpan = new FluentTagBuilder("span") .AddCssClass(errorCssClass) .AppendChild(validationMessage); // Append error span to dropdown dropDownDiv.AppendChild(errorSpan); return(new MvcHtmlString(dropDownDiv.Render())); }
/// <summary> /// Construct list selector according to parameters. /// </summary> /// <exception cref="ArgumentNullException"> /// When expression or source is null. /// </exception> /// <typeparam name="TModel">Type of model.</typeparam> /// <typeparam name="TProperty">Type of property.</typeparam> /// <param name="htmlHelper">HtmlHelper.</param> /// <param name="expression">Expression to select needed property.</param> /// <param name="source">Source of list to select from.</param> /// <param name="enableMultiSelect">Enable selecting multiple items at once.</param> /// <param name="htmlAttributes">Html attributes.</param> /// <returns>Constructed list selector as FluentTagBuilder.</returns> internal static FluentTagBuilder ConstructListSelector <TModel, TProperty>( this HtmlHelper <TModel> htmlHelper, Expression <Func <TModel, TProperty> > expression, SelectList source, bool enableMultiSelect, object htmlAttributes) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (source == null || source.Items == null) { throw new ArgumentNullException(nameof(source)); } // Determine main class string ulCssClass = enableMultiSelect ? "list-multi-selector-for" : "list-selector-for"; string contentContainerCssClass = "content-container"; string errorCssClass = string.Format("{0}__error", ulCssClass); // Get model metadata ModelMetadata metadata = ModelMetadata .FromLambdaExpression(expression, htmlHelper.ViewData); // Declare model data and get it from metadata string modelData = null; List <string> listModelData = null; if (metadata.Model != null) { if (enableMultiSelect) { listModelData = ((IEnumerable)metadata.Model) .Cast <object>() .Where(m => m != null) .Select(m => m.ToString()) .ToList(); } else { modelData = metadata.Model.ToString(); } } // Convert html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Construct ul tag. FluentTagBuilder ulBuilder = new FluentTagBuilder("ul") .MergeAttributes(attributes) .AddCssClass(ulCssClass); // Loop through items and add them to list foreach (SelectListItem listItem in source.Items) { // Initialize icon FluentTagBuilder icon = new FluentTagBuilder("i") .AddCssClass("default-icons") .SetInnerHtml("keyboard_arrow_right"); // Initialize span FluentTagBuilder span = new FluentTagBuilder("span") .SetInnerHtml(listItem.Text); // Initialize hidden selector field to select. // If multiselect enabled, then this field should be // checkbox, otherwise it should be radio button. string hiddenSelector; if (enableMultiSelect) { // Get model name form expression string modelName = ExpressionHelper.GetExpressionText(expression); // Get validation attributes IDictionary <string, object> validationAttributes = htmlHelper.GetUnobtrusiveValidationAttributes(modelName, metadata); // Determine if list item is checked bool isChecked = listModelData != null && listModelData.Contains(listItem.Value); FluentTagBuilder hiddenCheckBox = new FluentTagBuilder("input") .MergeAttribute("name", modelName) .MergeAttribute("id", modelName) .MergeAttribute("value", listItem.Value) .MergeAttribute("type", "checkbox") .MergeAttribute("hidden", "hidden") .MergeAttributes(validationAttributes); if (isChecked) { hiddenCheckBox.MergeAttribute("checked", "true"); } hiddenSelector = hiddenCheckBox.Render(); } else { hiddenSelector = htmlHelper .RadioButtonFor(expression, listItem.Value, new { hidden = "hidden" }) .ToHtmlString(); } // Initialize div and add elements inside. FluentTagBuilder containerDiv = new FluentTagBuilder("div") .AddCssClass(contentContainerCssClass) .AppendChild(icon) .AppendChild(span) .AppendChild(hiddenSelector); // Initialize li and set inner text FluentTagBuilder liBuilder = new FluentTagBuilder("li") .SetInnerHtml(containerDiv.Render()); // Add li to listContentBuilder ulBuilder.AppendChild(liBuilder); } // Add error MvcHtmlString validationMessage = htmlHelper.ValidationMessageFor(expression); FluentTagBuilder errorSpan = new FluentTagBuilder("span") .AddCssClass(errorCssClass) .AppendChild(validationMessage); ulBuilder.AppendChild(errorSpan); return(ulBuilder); }
/// <summary> /// Create combo date selector for date time. /// </summary> /// <exception cref="ArgumentNullException"> /// When expression is null. /// </exception> /// <typeparam name="TModel">Type of model.</typeparam> /// <param name="htmlHelper">HtmlHelper.</param> /// <param name="expression">Property selector expression.</param> /// <param name="yearRange">Range of years.</param> /// <param name="htmlAttributes">Html attributes to add to combo date.</param> /// <returns>Combo date selector.</returns> public static MvcHtmlString ComboDateFor <TModel>( this HtmlHelper <TModel> htmlHelper, Expression <Func <TModel, DateTime> > expression, IEnumerable <int> yearRange, object htmlAttributes) { if (expression == null) { throw new ArgumentNullException(nameof(expression)); } string comboDateCss = "combo-date"; string dayContainerCss = "day-container"; string monthContainerCss = "month-container"; string yearContainerCss = "year-container"; string errorCss = "combo-date__error"; string dayText = "Gün"; string monthText = "Ay"; string yearText = "İl"; // Initialize yearRange if has not been provided if (yearRange == null) { yearRange = Enumerable.Range(1900, 200); } // Get model metadata ModelMetadata metadata = ModelMetadata.FromLambdaExpression( expression, htmlHelper.ViewData); string modelName = ExpressionHelper.GetExpressionText(expression); // Append HtmlFieldPrefix if there is any string fieldPrefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix; if (!string.IsNullOrEmpty(fieldPrefix)) { modelName = string.Format("{0}.{1}", fieldPrefix, modelName); } // Convert html attributes RouteValueDictionary attributes = HtmlHelper .AnonymousObjectToHtmlAttributes(htmlAttributes); // Initialize container div FluentTagBuilder comboDate = new FluentTagBuilder("div") .MergeAttributes(attributes) .AddCssClass(comboDateCss); // Initialize hidden text box for client side validation FluentTagBuilder input = new FluentTagBuilder("input") .MergeAttribute("name", modelName) .MergeAttribute("id", modelName) .MergeAttribute("type", "date") .MergeAttribute("hidden", "hidden") .MergeAttribute("readonly", "readonly"); if (metadata.Model != null) { DateTime value = Convert.ToDateTime(metadata.Model); input.MergeAttribute("value", value.ToString("yyyy-MM-dd")); } //// Get validation attributes IDictionary <string, object> validationAttributes = htmlHelper.GetUnobtrusiveValidationAttributes(modelName, metadata); // Merge validation attributes input.MergeAttributes(validationAttributes); //contentBuilder.AppendLine(input.ToString()); comboDate.AppendChild(input); // Declare date property selector Expression <Func <TModel, Int32> > datePropertySelector; // Select day property of date MemberExpression dayExpression = Expression.Property(expression.Body, "Day"); datePropertySelector = Expression.Lambda <Func <TModel, Int32> >( dayExpression, expression.Parameters); // Create drop down button for day MvcHtmlString daySelector = htmlHelper .DropDownButtonFor <TModel, int>( datePropertySelector, new SelectList(Enumerable .Range(1, 31) .Select(m => new SelectListItem { Text = m.ToString("00"), Value = m.ToString() })), dayText); // Setup day container FluentTagBuilder dayContainer = new FluentTagBuilder("div") .AddCssClass(dayContainerCss) .AppendChild(daySelector); //contentBuilder.AppendLine(dayContainer.ToString()); comboDate.AppendChild(dayContainer); // Select month property of date MemberExpression monthExpression = Expression.Property(expression.Body, "Month"); datePropertySelector = Expression.Lambda <Func <TModel, Int32> >( monthExpression, expression.Parameters); // Create drop down button for month MvcHtmlString monthSelector = htmlHelper .DropDownButtonFor <TModel, int>( datePropertySelector, new SelectList(Enumerable.Range(1, 12) .Select(r => new SelectListItem { Value = r.ToString(), Text = DateTimeFormatInfo.CurrentInfo.GetMonthName(r) })), monthText); // Setup month container FluentTagBuilder monthContainer = new FluentTagBuilder("div") .AddCssClass(monthContainerCss) .AppendChild(monthSelector); //contentBuilder.AppendLine(monthContainer.ToString()); comboDate.AppendChild(monthContainer); // Select year property of date MemberExpression yearExpression = Expression.Property(expression.Body, "Year"); datePropertySelector = Expression.Lambda <Func <TModel, Int32> >( yearExpression, expression.Parameters); // Create drop down button for month MvcHtmlString yearSelector = htmlHelper .DropDownButtonFor <TModel, int>( datePropertySelector, new SelectList(yearRange .Select(r => new SelectListItem { Text = r.ToString(), Value = r.ToString() })), yearText); // Setup year container FluentTagBuilder yearContainer = new FluentTagBuilder("div") .AddCssClass(yearContainerCss) .AppendChild(yearSelector); comboDate.AppendChild(yearContainer); // Set up error span MvcHtmlString validationMessage = htmlHelper .ValidationMessageFor(expression); FluentTagBuilder errorSpan = new FluentTagBuilder("span") .AddCssClass(errorCss) .AppendChild(validationMessage); comboDate.AppendChild(errorSpan); return(new MvcHtmlString(comboDate.Render())); }