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

 

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。