/// <summary>
        /// Create drop down menu item for property
        /// selected by expression using provided list item.
        /// </summary>
        /// <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="listItem">List item to get text and value.</param>
        /// <returns>List item with radio button and span.</returns>
        internal static FluentTagBuilder CreateDropDownButtonMenuItem <TModel, TProperty>(
            this HtmlHelper <TModel> htmlHelper,
            Expression <Func <TModel, TProperty> > expression,
            SelectListItem listItem)
        {
            FluentTagBuilder menuItem = new FluentTagBuilder("li");

            // Initialize span
            FluentTagBuilder span = new FluentTagBuilder("span")
                                    .SetInnerText(listItem.Text);

            // Initialize radio button
            string        fieldName = ExpressionHelper.GetExpressionText(expression);
            ModelMetadata metadata  = ModelMetadata.FromLambdaExpression(
                expression, htmlHelper.ViewData);

            // Get validation attributes and add hidden attribute
            var htmlAttributes = htmlHelper
                                 .GetUnobtrusiveValidationAttributes(fieldName);

            htmlAttributes.Add("hidden", "hidden");

            MvcHtmlString radioButton = htmlHelper
                                        .RadioButton(fieldName, listItem.Value, listItem.Selected, htmlAttributes);

            // Set content of list menu item
            menuItem.AppendChild(span).AppendChild(radioButton);

            return(menuItem);
        }
        /// <summary>
        /// Construct list for tree view.
        /// </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">Soruce of tree view.</param>
        /// <returns>Tree view list as FluentTagBuilder.</returns>
        private static FluentTagBuilder ConstructList <TModel, TProperty>(
            this HtmlHelper <TModel> helper,
            Expression <Func <TModel, TProperty> > expression,
            TreeViewSource <TProperty> source)
            where TProperty : struct
        {
            // CSS classes
            string listCssClass   = "tree-view-list";
            string loadedCssClass = "tv-loaded";

            // Construct tree view list
            FluentTagBuilder treeViewList = new FluentTagBuilder("ul")
                                            .AddCssClass(listCssClass);

            // If there is not any item return
            if (source == null ||
                source.ItemList == null ||
                source.ItemList.Count == 0)
            {
                return(treeViewList);
            }

            // If source is not empty add class
            treeViewList.AddCssClass(loadedCssClass);

            // Construct list items and add them to treeViewList
            foreach (TreeViewItem <TProperty> listItemSource in source.ItemList)
            {
                FluentTagBuilder listItem = helper
                                            .ConstructListItem(expression, listItemSource);
                treeViewList.AppendChild(listItem);
            }

            return(treeViewList);
        }
        /// <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>
        /// Construct list level action link container for list which will be
        /// managed at server side.
        /// </summary>
        /// <returns>List action container.</returns>
        private FluentTagBuilder ConstructServerSideListActionContainer()
        {
            if (ListActionCollection == null ||
                ListActionCollection.Count == 0)
            {
                return(null);
            }

            FluentTagBuilder listActionContainer = new FluentTagBuilder("div");

            foreach (IListEditorActionCore <TModel> action in ListActionCollection)
            {
                FluentTagBuilder listAction = action.GenerateLink();
                if (listAction != null)
                {
                    listActionContainer.AppendChild(listAction);
                }
            }

            return(listActionContainer);
        }
        /// <summary>
        /// Construct editor according to configured fields.
        /// </summary>
        /// <returns>Constructed editor.</returns>
        private FluentTagBuilder ConstructListEditor()
        {
            // Declare and set css classes
            string listEditorCss         = "list-editor";
            string listEditorRowCss      = "list-editor-row";
            string listEditorRowLabelCss = "list-editor-row__label";
            string listEditorRowFieldCss = "list-editor-row__field";

            string listEditorDataTemplateCss = "list-editor-data__template";

            string listEditorActionContainerCss = "list-editor-action-container";
            string listEditorSaveActionCss      = "list-editor-action__save";
            string listEditorCancelActionCss    = "list-editor-action__cancel";

            // Initialize list editor
            FluentTagBuilder listEditor = new FluentTagBuilder("div")
                                          .AddCssClass(listEditorCss)
                                          .MergeAttribute("hidden", "hidden");

            foreach (IFieldCore <TModel> fieldCore in FieldCollection)
            {
                // Initialize editor label
                FluentTagBuilder editorLabel = null;

                // If editor type has not been set to Hidden add label for field
                if (fieldCore.EditorType != EditorType.Hidden)
                {
                    editorLabel = new FluentTagBuilder("div")
                                  .AddCssClass(listEditorRowLabelCss)
                                  .AppendChild(fieldCore.LabelFor());
                }

                // Initialize editor field
                FluentTagBuilder editorField = new FluentTagBuilder("div")
                                               .AddCssClass(listEditorRowFieldCss)
                                               .AppendChild(fieldCore.EditorFor());

                // Initialize editor row, add label and editor.
                FluentTagBuilder listEditorRow = new FluentTagBuilder("div")
                                                 .AddCssClass(listEditorRowCss)
                                                 .AppendChild(editorLabel)
                                                 .AppendChild(editorField);

                // Add row to list editor
                listEditor.AppendChild(listEditorRow);
            }

            // Initialize actions
            FluentTagBuilder saveAction = new FluentTagBuilder("button")
                                          .MergeAttribute("type", "button")
                                          .AddCssClass(listEditorSaveActionCss)
                                          .SetInnerText(SaveButtonLabel);

            FluentTagBuilder cancelAction = new FluentTagBuilder("button")
                                            .MergeAttribute("type", "button")
                                            .AddCssClass(listEditorCancelActionCss)
                                            .SetInnerText(CancelButtonLabel);

            FluentTagBuilder actionContainer = new FluentTagBuilder("div")
                                               .AddCssClass(listEditorActionContainerCss)
                                               .AppendChild(saveAction)
                                               .AppendChild(cancelAction);

            // Add actions panel to list editor
            listEditor.AppendChild(actionContainer);

            // Initialize data template for adding or editing items.
            string dataTemplateIndex = "_template_index_";
            string htmlFieldPrefix   = string.Format(
                "{0}[{1}]", CollectionName, dataTemplateIndex);

            // Use BeginHtmlFieldPrefixScope instead of BeginCollectionItem,
            // because these items must not bound when data is posted,
            // and this index must not be reused by BeginCollectionItem
            // to keep consistency.
            using (Helper.BeginHtmlFieldPrefixScope(htmlFieldPrefix))
            {
                MvcHtmlString hiddenIndexer = Helper.HiddenIndexerForModel(
                    new { disabled = "disabled" });

                FluentTagBuilder listDataRowTemplate = ConstructListDataRow(null)
                                                       .AppendChild(hiddenIndexer)
                                                       .AppendChild(ConstructClientSideListItemActionContainer(null));

                FluentTagBuilder dataTemplate = new FluentTagBuilder("div")
                                                .MergeAttribute("hidden", "hidden")
                                                .AddCssClass(listEditorDataTemplateCss)
                                                .AppendChild(listDataRowTemplate);

                // Append dataTemplate to list editor.
                listEditor.AppendChild(dataTemplate);
            }

            return(listEditor);
        }
        /// <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>
        /// Construct one row of list editor.
        /// </summary>
        /// <param name="model">Model object to construct list row according to.</param>
        /// <returns>Constructed row of list.</returns>
        private FluentTagBuilder ConstructListDataRow(TModel model)
        {
            // Declare and set css class names
            string listDataRowCss              = "list-data-row";
            string listDataFieldRowCss         = "list-data-field-row";
            string listDataCalptionFieldRowCss = "list-data-field-row__caption";
            string listdataImageFiledRowCss    = "list-data-field-row__iamge";
            string listDataLabelCss            = "list-data-display__label";
            string listDataDisplayFieldCss     = "list-data-display__field";

            // Store current model for further use
            TModel oldModel = Helper.ViewData.Model;

            try
            {
                // Set model object of HtmlHelper to be able
                // to get correct display, editor and hidden for fields.
                Helper.ViewData.Model = model;

                // Create list data row and merge attributes
                FluentTagBuilder listDataRow = new FluentTagBuilder("div")
                                               .MergeAttributes(GetDataRowAttributes(model))
                                               .AddCssClass(listDataRowCss);

                // Loop through each configured field and add them to the row.
                foreach (IFieldCore <TModel> fieldCore in FieldCollection)
                {
                    /// Do not render field if provided condition is not met
                    /// when list is managed at server side.
                    if (BuilderType == ListEditorBuilderType.ManagedAtServerSide &&
                        !fieldCore.ShouldBeRendered(model))
                    {
                        continue;
                    }

                    if (fieldCore.EditorType != EditorType.Hidden)
                    {
                        // If EditorType has not been set to Hidden,
                        // construct normal field row

                        // Initialize field
                        FluentTagBuilder displayField = new FluentTagBuilder("div")
                                                        .AddCssClass(listDataDisplayFieldCss);

                        // If field should be rendered as image construct image tag
                        // for the field otherwise costruct span for it.
                        if (fieldCore.IsAsImage)
                        {
                            FluentTagBuilder iamgeField = new FluentTagBuilder("img")
                                                          .MergeAttribute("src", fieldCore.Evaluate(model));

                            displayField.AppendChild(iamgeField);
                        }
                        else
                        {
                            FluentTagBuilder displaySpan = new FluentTagBuilder("span")
                                                           .AppendChild(fieldCore.DisplayFor());

                            displayField.AppendChild(displaySpan);
                        }

                        // If this list editor will be managed at client side,
                        // add hidden input field to be able to bind correctly.
                        if (BuilderType == ListEditorBuilderType.ManagedAtClientSide)
                        {
                            displayField.AppendChild(fieldCore.HiddenFor());
                        }

                        // Initialize row for label and field
                        FluentTagBuilder fieldRow = new FluentTagBuilder("div")
                                                    .AddCssClass(listDataFieldRowCss);

                        // Add caption field row css class if needed
                        if (fieldCore.IsAsCaption)
                        {
                            fieldRow.AddCssClass(listDataCalptionFieldRowCss);
                        }

                        // Add image filed row css class if needed
                        if (fieldCore.IsAsImage)
                        {
                            fieldRow.AddCssClass(listdataImageFiledRowCss);
                        }

                        // Add label for field if needed.
                        if (!fieldCore.IsWithoutLabel)
                        {
                            // Initialize label for field
                            FluentTagBuilder label = new FluentTagBuilder("div")
                                                     .AddCssClass(listDataLabelCss)
                                                     .AppendChild(fieldCore.LabelFor());

                            fieldRow.AppendChild(label);
                        }

                        // Append display field to the row.
                        fieldRow.AppendChild(displayField);

                        // Add fieldRow to listEditorRow
                        listDataRow.AppendChild(fieldRow);
                    }
                    else
                    {
                        // Initialize row for label and field
                        FluentTagBuilder fieldRow = new FluentTagBuilder("div")
                                                    .AddCssClass(listDataFieldRowCss)
                                                    .AppendChild(fieldCore.HiddenFor());

                        // If EditorType has been set to Hidden,
                        // just construct hidden input for field
                        listDataRow.AppendChild(fieldRow);
                    }
                }

                return(listDataRow);
            }
            finally
            {
                // Restore model of helper
                Helper.ViewData.Model = oldModel;
            }
        }
        /// <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()));
        }
Beispiel #9
0
        /// <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);
        }
Beispiel #12
0
        /// <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()));
        }