網頁

2014年5月19日 星期一

ASP.NET MVC - CheckBoxList 與 ValidationMessage (ASP.NET MVC 4 without Bootstrap)

上一篇「ASP.NET MVC - CheckBoxList 與 ValidationMessage (ASP.NET MVC 5 with Bootstrap3)」的說明是使用 Visual Studio 2013, ASP.NET MVC 5, Bootstrap 環境下所開發的,然而如果是使用 ASP.NET MVC 4 並且沒有使用 Bootstrap 的情況下又該如何操作呢?

 


兩者的最大差別是在於有無使用 Bootstrap,因為我上一篇是使用預設的 ASP.NET MVC 5 專案範本,所以預設就會使用 Bootstrap,所以在 CheckbBoxListExtensions.cs 裡建立 CheckBox Items 時就需要做一些調整,另外在檢視頁面裡也會有一些不同。

開發環境:Visual Studio 2012, ASP.NET MVC 4, .NET Framework 4, Entity Framework 5.0

前一篇文章:
ASP.NET MVC - CheckBoxList 與 ValidationMessage (ASP.NET MVC 5 with Bootstrap3)

 

專案的檔案目錄架構與上一篇文章的相同,

image

以下為這次所使用的 CheckBoxListExtensions.cs 內容,

CheckBoxListExtensions.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using MvcApplication1.Infrastructure.Enums;
 
namespace MvcApplication1.Infrastructure.Extensions
{
    public static class CheckBoxListExtensions
    {
        #region -- CheckBoxList (Horizontal) --
        /// <summary>
        /// CheckBoxList.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="name">The name.</param>
        /// <param name="listInfo">SelectListItem.</param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper,
            string name,
            IEnumerable<SelectListItem> listInfo)
        {
            return htmlHelper.CheckBoxList(name, listInfo, (IDictionary<string, object>)null, 0);
        }
 
        /// <summary>
        /// CheckBoxList.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="name">The name.</param>
        /// <param name="listInfo">SelectListItem.</param>
        /// <param name="htmlAttributes">The HTML attributes.</param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper,
            string name,
            IEnumerable<SelectListItem> listInfo,
            object htmlAttributes)
        {
            return htmlHelper.CheckBoxList(name, listInfo, (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes), 0);
        }
 
        /// <summary>
        /// CheckBoxList.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="name">The name.</param>
        /// <param name="listInfo">The list info.</param>
        /// <param name="htmlAttributes">The HTML attributes.</param>
        /// <param name="number">每個Row的顯示個數.</param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper,
            string name,
            IEnumerable<SelectListItem> listInfo,
            IDictionary<string, object> htmlAttributes,
            int number)
        {
            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentException("必須給這些 CheckBoxList 一個 Tag Name", "name");
            }
            if (listInfo == null)
            {
                throw new ArgumentNullException("listInfo", "必須要給List<SelectListItem> listInfo");
            }
            var selectListItems = listInfo as SelectListItem[] ?? listInfo.ToArray();
            if (!selectListItems.Any())
            {
                throw new ArgumentException("List<SelectListItem> listInfo 至少要有一組資料", "listInfo");
            }
 
            var sb = new StringBuilder();
            var lineNumber = 0;
 
            foreach (var info in selectListItems)
            {
                lineNumber++;
 
                var builder = new TagBuilder("input");
                if (info.Selected)
                {
                    builder.MergeAttribute("checked", "checked");
                }
                builder.MergeAttributes<string, object>(htmlAttributes);
                builder.MergeAttribute("type", "checkbox");
                builder.MergeAttribute("value", info.Value);
                builder.MergeAttribute("name", name);
                builder.MergeAttribute("id", string.Format("{0}_{1}", name, info.Value));
                sb.Append(builder.ToString(TagRenderMode.Normal));
 
                var labelBuilder = new TagBuilder("label");
                labelBuilder.MergeAttribute("for", string.Format("{0}_{1}", name, info.Value));
                labelBuilder.InnerHtml = info.Text;
                sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
 
                if (number == 0 || (lineNumber % number == 0))
                {
                    sb.Append("<br />");
                }
            }
            return MvcHtmlString.Create(sb.ToString());
        }
        #endregion
 
        #region -- CheckBoxListVertical --
        /// <summary>
        /// Checks the box list vertical.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="name">The name.</param>
        /// <param name="listInfo">The list info.</param>
        /// <param name="htmlAttributes">The HTML attributes.</param>
        /// <param name="columnNumber">The column number.</param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxListVertical(this HtmlHelper htmlHelper,
            string name,
            IEnumerable<SelectListItem> listInfo,
            IDictionary<string, object> htmlAttributes,
            int columnNumber = 1)
        {
            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentException("必須給這些 CheckBoxList 一個 Tag Name", "name");
            }
            if (listInfo == null)
            {
                throw new ArgumentNullException("listInfo", "必須要給 List<CheckBoxListInfo> listInfo");
            }
            var selectListItems = listInfo as SelectListItem[] ?? listInfo.ToArray();
            if (!selectListItems.Any())
            {
                throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");
            }
 
            var dataCount = selectListItems.Count();
 
            // calculate number of rows
            var rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(columnNumber)));
            if (dataCount <= columnNumber || dataCount - columnNumber == 1)
            {
                rows = dataCount;
            }
 
            var wrapBuilder = new TagBuilder("div");
            wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");
 
            var wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
            var wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");
            var wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));
 
            var sb = new StringBuilder();
            sb.Append(wrapStart);
 
            var lineNumber = 0;
 
            foreach (var info in selectListItems)
            {
                var builder = new TagBuilder("input");
                if (info.Selected)
                {
                    builder.MergeAttribute("checked", "checked");
                }
                builder.MergeAttributes<string, object>(htmlAttributes);
                builder.MergeAttribute("type", "checkbox");
                builder.MergeAttribute("value", info.Value);
                builder.MergeAttribute("name", name);
                builder.MergeAttribute("id", string.Format("{0}_{1}", name, info.Value));
                sb.Append(builder.ToString(TagRenderMode.Normal));
 
                var labelBuilder = new TagBuilder("label");
                labelBuilder.MergeAttribute("for", string.Format("{0}_{1}", name, info.Value));
                labelBuilder.InnerHtml = info.Text;
                sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
 
                lineNumber++;
 
                if (lineNumber.Equals(rows))
                {
                    sb.Append(wrapBreak);
                    lineNumber = 0;
                }
                else
                {
                    sb.Append("<br/>");
                }
            }
            sb.Append(wrapClose);
            return MvcHtmlString.Create(sb.ToString());
        }
        #endregion
 
        #region -- CheckBoxList (Horizonal, Vertical) --
        /// <summary>
        /// Checks the box list.
        /// </summary>
        /// <param name="htmlHelper">The HTML helper.</param>
        /// <param name="name">The name.</param>
        /// <param name="listInfo">The list info.</param>
        /// <param name="htmlAttributes">The HTML attributes.</param>
        /// <param name="position">The position.</param>
        /// <param name="number">Position.Horizontal則表示每個Row的顯示個數, Position.Vertical則表示要顯示幾個Column</param>
        /// <returns></returns>
        public static MvcHtmlString CheckBoxList(this HtmlHelper htmlHelper,
            string name,
            IEnumerable<SelectListItem> listInfo,
            IDictionary<string, object> htmlAttributes,
            Position position = Position.Horizontal,
            int number = 0)
        {
            if (String.IsNullOrEmpty(name))
            {
                throw new ArgumentException("必須給這些 CheckBoxList 一個 Tag Name", "name");
            }
            if (listInfo == null)
            {
                throw new ArgumentNullException("listInfo", "必須要給List<SelectListItem> listInfo");
            }
            var selectListItems = listInfo as SelectListItem[] ?? listInfo.ToArray();
            if (!selectListItems.Any())
            {
                throw new ArgumentException("List<SelectListItem> listInfo 至少要有一組資料", "listInfo");
            }
 
            var sb = new StringBuilder();
            var lineNumber = 0;
 
            switch (position)
            {
                case Position.Horizontal:
 
                    foreach (var info in selectListItems)
                    {
                        lineNumber++;
                        sb.Append(CreateCheckBoxItem(info, name, htmlAttributes));
 
                        if (number == 0 || (lineNumber % number == 0))
                        {
                            sb.Append("<br />");
                        }
                    }
                    sb.Append("<br />");
                    break;
 
                case Position.Vertical:
 
                    var dataCount = selectListItems.Count();
 
                    // 計算最大顯示的列數(rows)
                    var rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(number)));
                    if (dataCount <= number || dataCount - number == 1)
                    {
                        rows = dataCount;
                    }
 
                    var wrapBuilder = new TagBuilder("div");
                    wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");
 
                    var wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
                    var wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");
                    var wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));
 
                    sb.Append(wrapStart);
 
                    foreach (var info in selectListItems)
                    {
                        lineNumber++;
                        sb.Append(CreateCheckBoxItem(info, name, htmlAttributes));
 
                        if (lineNumber.Equals(rows))
                        {
                            sb.Append(wrapBreak);
                            lineNumber = 0;
                        }
                        else
                        {
                            sb.Append("<br/>");
                        }
                    }
                    sb.Append(wrapClose);
                    break;
            }
 
            return MvcHtmlString.Create(sb.ToString());
        }
 
        /// <summary>
        /// Creates the check box item.
        /// </summary>
        /// <param name="info">The info.</param>
        /// <param name="name">The name.</param>
        /// <param name="htmlAttributes">The HTML attributes.</param>
        /// <returns></returns>
        internal static string CreateCheckBoxItem(SelectListItem info, string name, IDictionary<string, object> htmlAttributes)
        {
            var sb = new StringBuilder();
 
            var builder = new TagBuilder("input");
            if (info.Selected)
            {
                builder.MergeAttribute("checked", "checked");
            }
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("type", "checkbox");
            builder.MergeAttribute("value", info.Value);
            builder.MergeAttribute("name", name);
            builder.MergeAttribute("id", string.Format("{0}_{1}", name, info.Value));
            sb.Append(builder.ToString(TagRenderMode.Normal));
 
            var labelBuilder = new TagBuilder("label");
            labelBuilder.MergeAttribute("for", string.Format("{0}_{1}", name, info.Value));
            labelBuilder.InnerHtml = info.Text;
            sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
 
            return sb.ToString();
        }
        #endregion
    }
 
}

 

HomeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication1.Infrastructure.Services;
using MvcApplication1.Models;
 
namespace MvcApplication1.Controllers
{
    public class HomeController : Controller
    {
        private readonly CategoryService categoryService = new CategoryService();
 
        private List<SelectListItem> categorySelectListItems
        {
            get
            {
                var categories = this.categoryService.GetAll();
                var items = new List<SelectListItem>();
                foreach (var c in categories)
                {
                    items.Add(item: new SelectListItem()
                    {
                        Value = c.CategoryID.ToString(),
                        Text = c.CategoryName
                    });
                }
                return items;
            }
        }
 
 
        public ActionResult Index()
        {
            var items = this.categorySelectListItems;
            ViewBag.CategoryItems = items;
 
            return View();
        }
 
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Index(Foo instance)
        {
            var items = this.categorySelectListItems;
            ViewBag.CategoryItems = items;
 
            if (ModelState.IsValid)
            {
                return View();
            }
            return View(instance);
        }
    }
}

 

~/Views/Shared/EditorTemplates/CheckBoxList.cshtml

先建立 CheckBoxList.cshtml 的 EditorTemplates,這邊的內容就與上一篇所建立的 EditorTemplates 有些不同了,最大的差別就在於最後的 ValidationMessage 的地方,

@using MvcApplication1.Infrastructure.Enums
@using MvcApplication1.Infrastructure.Extensions
@{
    var require = false;
    object validationMessage = string.Empty;
    
    var validationAttributes = Html.GetUnobtrusiveValidationAttributes("");
    if (validationAttributes.ContainsKey("data-val")
        &&
        validationAttributes.ContainsKey("data-val-required"))
    {
        require = true;
        if (!validationAttributes.TryGetValue("data-val-required", out validationMessage))
        {
            validationMessage = "This field is required.";
        }
        validationAttributes.Add("required", "required");
    }
 
    var tagName = ViewData["TagName"] == null
        ? "CheckBoxList"
        : (string)ViewData["TagName"];
 
    var checkboxItems = ViewData["CheckBoxItems"] == null
        ? new List<SelectListItem>()
        : (IEnumerable<SelectListItem>)ViewData["CheckBoxItems"];
 
    var position = ViewData["Position"] == null
        ? Position.Horizontal
        : (Position)ViewData["Position"];
 
    var numbers = 0;
    if (ViewData["Numbers"] == null)
    {
        numbers = 1;
    }
    else if (!int.TryParse(ViewData["Numbers"].ToString(), out numbers))
    {
        numbers = 1;
    }
}
 
@Html.CheckBoxList(
    tagName, 
    checkboxItems, 
    new RouteValueDictionary(validationAttributes), 
    position, 
    numbers)
 
@if (require)
{
    <span class="field-validation-valid" data-valmsg-for="@(tagName)" data-valmsg-replace="false">
        @validationMessage
    </span>
}

 

~/Views/Home/Index.cshtml

再來就主要顯示的 View,差別也是在 ValidationMessage 的部份,上一篇還需要另外使用 jQuery 去處理訊息的顯示,而這一篇並不需要這麼做,

@using MvcApplication1.Infrastructure.Enums
@model MvcApplication1.Models.Foo
@{
    ViewBag.Title = "Index";
}
 
<h2>Index</h2>
 
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
 
    <fieldset>
        <legend>Foo</legend>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>
 
        <div class="editor-label">
            @Html.LabelFor(model => model.Categories)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Categories,
                "CheckBoxList",
                new
                {
                    TagName = "Categories", 
                    CheckBoxItems = ViewBag.CategoryItems, 
                    Position = Position.Horizontal, 
                    Numbers = 3
                })
        </div>
 
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
 
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
 
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

 

執行結果

image

什麼都不填、不勾選,直接按下「Create」

image

在分類項目裡勾選一個

image

以下為完整的操作過程

20140518_CheckBoxList_MVC4

 

 

以上

沒有留言:

張貼留言