距離之前那一篇「ASP.NET MVC 擴充HtmlHelper 加入 CheckBoxList 功能 - 1」都已經超過半年了,
在那篇文章中我們實作出一個水平方向呈現的CheckBoxList,
有了水平方向呈現的方式,也應該要有垂直方向顯示的吧?!
因為垂直方向的呈現在那時候於實作的過程中一直處於「卡關」的狀態,一直寫不出來,所以就一直Pending住,
然而就在隔了這麼久之後終於讓我做出來了,也發現到,這垂直方向的呈現並不是那麼難做,
也不需要去參考System.Web.UI.WebControls.RepeatInfo「RenderVerticalRepeater」,
總之就是幾個關鍵地方而已,所以就廢話少說,直接進入主題。
垂直顯示的CheckBoxList
何謂垂直顯示的CheckBoxList呢?就先直接看呈現的結果,
把這一張Vertical呈現的CheckBoxList與上面那張Horizontal呈現的CheckBoxList做一個比較,就很容易可以看出不同,
CheckBox的呈現順序與方向:
Horizontal是由左到右然後再從上而下的方式做顯示,我們可以設定每一個Row要顯示多少個CheckBox,
而Vertical的呈現則是由上而下然後依據每個Column要顯示的個數去顯示CheckBox,再來由左到右的顯示每個Column。
這邊所實作的CheckBoxList – Vertical是用Div來做每個Column的區塊,而非WebForm是以Table的方式來呈現,
在這邊先跟各位說明一下,以下就先來看實作的程式內容:
#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 string CheckBoxListVertical(this HtmlHelper htmlHelper,string name,
List<CheckBoxListInfo> 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");}if (listInfo.Count < 1)
{throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");}int dataCount = listInfo.Count;
// calculate number of rows
int rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(columnNumber)));
if (dataCount <= columnNumber || dataCount - columnNumber == 1)
{rows = dataCount;}TagBuilder wrapBuilder = new TagBuilder("div");wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");string wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
string wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");string wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));StringBuilder sb = new StringBuilder();
sb.Append(wrapStart);int lineNumber = 0;
foreach (CheckBoxListInfo info in listInfo){TagBuilder builder = new TagBuilder("input");if (info.IsChecked)
{builder.MergeAttribute("checked", "checked");}builder.MergeAttributes<string, object>(htmlAttributes);builder.MergeAttribute("type", "checkbox");builder.MergeAttribute("value", info.Value);
builder.MergeAttribute("name", name);
sb.Append(builder.ToString(TagRenderMode.Normal));TagBuilder labelBuilder = new TagBuilder("label");labelBuilder.MergeAttribute("for", name);
labelBuilder.InnerHtml = info.DisplayText;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()).ToString();
}#endregion
我就不去對程式的內容多做解釋,應該大家都可以看得懂,我寫得相當簡單(太難的…我也寫不出來)
接下來看看Controller的Action內容:
public ActionResult Vertical()
{
IQueryable<PostalCity> cities = _cityService.GetCollection();
var infos = new List<CheckBoxListInfo>();
foreach (var item in cities)
{
infos.Add(new CheckBoxListInfo
(
item.SORT.ToString(),
string.Concat(item.NAME, "-", item.SORT.ToString()),
false)
);
}
ViewData["CheckBoxListOfCities"] = infos;
return View();
}
上面的Action方法內的程式就跟之前那一篇水平呈現的CheckBoxList是一樣的,
如果對內容有不清楚的地方,可以複習「ASP.NET MVC 擴充HtmlHelper 加入 CheckBoxList 功能 - 1」。
View的內容:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"><h2>CheckBoxList - Vertical</h2><% var checkBoxData = ViewData["CheckBoxListOfCities"] as List<CheckBoxListInfo>; %><%= Html.CheckBoxListVertical("CheckBoxCities", checkBoxData, null, 4)%><p></p><input type="button" id="ButtonList" value="GetCheckList" /></asp:Content>
在使用CheckBoxListVertical的方法中,我們依序填入資料,並且指定以4個Columns來做呈現,
最後所呈現的結果,如下:
如果是改成以7個Columns來呈現的話,就會是下圖的樣子:
這樣子大家就可以清楚明白了。
為了不要讓CheckBoxList的方法又多出一個「CheckBoxListVertical」讓使用者混淆,
所以就把原本的「CheckBoxList」方法給做了一個多載,然後方法多增加一個參數「Position」
public enum Position
{
Horizontal,
Vertical
}
在View Page使用CheckBoxList HtmlHelper的時候,就可以使用Position來決定要水平或是垂直呈現,
下面的程式就多增加的方法多載,這個方法預設會是使用水平方向呈現,而且是一個Row中顯示所有的CheckBox。
#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 string CheckBoxList(this HtmlHelper htmlHelper,
string name,
List<CheckBoxListInfo> 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<CheckBoxListInfo> listInfo");
}
if (listInfo.Count < 1)
{
throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");
}
StringBuilder sb = new StringBuilder();
int lineNumber = 0;
switch (position)
{
case Position.Horizontal:
foreach (CheckBoxListInfo info in listInfo)
{
lineNumber++;
sb.Append(CreateCheckBoxItem(info, name, htmlAttributes));
if (number == 0 || (lineNumber % number == 0))
{
sb.Append("<br />");
}
}
break;
case Position.Vertical:
int dataCount = listInfo.Count;
// 計算最大顯示的列數(rows)
int rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(number)));
if (dataCount <= number || dataCount - number == 1)
{
rows = dataCount;
}
TagBuilder wrapBuilder = new TagBuilder("div");
wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");
string wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
string wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");
string wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));
sb.Append(wrapStart);
foreach (CheckBoxListInfo info in listInfo)
{
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()).ToString();
}
internal static string CreateCheckBoxItem(CheckBoxListInfo info, string name, IDictionary<string, object> htmlAttributes)
{
StringBuilder sb = new StringBuilder();
TagBuilder builder = new TagBuilder("input");
if (info.IsChecked)
{
builder.MergeAttribute("checked", "checked");
}
builder.MergeAttributes<string, object>(htmlAttributes);
builder.MergeAttribute("type", "checkbox");
builder.MergeAttribute("value", info.Value);
builder.MergeAttribute("name", name);
sb.Append(builder.ToString(TagRenderMode.Normal));
TagBuilder labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", name);
labelBuilder.InnerHtml = info.DisplayText;
sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
return sb.ToString();
}
#endregion
CheckBoxList – Horizontal
當我們要呈現一個水平方向呈現的CheckBoxList的時候,就可以這樣設定:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"><h2>CheckBoxList- - Horizontal</h2><% var checkBoxData = ViewData["CheckBoxListOfCities"] as List<CheckBoxListInfo>; %><%= Html.CheckBoxList("CheckBoxCities", checkBoxData, null, Position.Horizontal, 5) %><p></p><input type="button" id="ButtonList" value="GetCheckList" /><%--原本的方式--%><%--<%= Html.CheckBoxList("CheckBoxCities", checkBoxData, null, 4)%>--%></asp:Content>
頁面的顯示結果:
CheckBoxList – Vertical
當我們要呈現一個垂直方向顯示的CheckBoxList時,就如同以下的設定方式:
頁面呈現的結果:<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"><h2>CheckBoxList - Vertical</h2><% var checkBoxData = ViewData["CheckBoxListOfCities"] as List<CheckBoxListInfo>; %><%= Html.CheckBoxList("CheckBoxCities", checkBoxData, null, Position.Vertical, 5) %><p></p><input type="button" id="ButtonList" value="GetCheckList" /><%--以前的方式--%><%--<%= Html.CheckBoxListVertical("CheckBoxCities", checkBoxData, null, 7)%>--%></asp:Content>
CheckBoxListExtensions.cs
這邊就把CheckBoxListExtensions.cs的程式碼內容提供給各位參考,
程式的內容如果有很多地方的錯誤或是未盡完善之處,也請各位海量包涵,並請指正小弟錯誤的地方,謝謝。
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">CheckBoxListInfo.</param>
/// <returns></returns>
public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> 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">CheckBoxListInfo.</param>
/// <param name="htmlAttributes">The HTML attributes.</param>
/// <returns></returns>
public static string CheckBoxList(this HtmlHelper htmlHelper, string name, List<CheckBoxListInfo> 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 string CheckBoxList(this HtmlHelper htmlHelper,
string name,
List<CheckBoxListInfo> 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<CheckBoxListInfo> listInfo");
}
if (listInfo.Count < 1)
{
throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");
}
StringBuilder sb = new StringBuilder();
int lineNumber = 0;
foreach (CheckBoxListInfo info in listInfo)
{
lineNumber++;
TagBuilder builder = new TagBuilder("input");
if (info.IsChecked)
{
builder.MergeAttribute("checked", "checked");
}
builder.MergeAttributes<string, object>(htmlAttributes);
builder.MergeAttribute("type", "checkbox");
builder.MergeAttribute("value", info.Value);
builder.MergeAttribute("name", name);
sb.Append(builder.ToString(TagRenderMode.Normal));
TagBuilder labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", name);
labelBuilder.InnerHtml = info.DisplayText;
sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
if (number == 0 || (lineNumber % number == 0))
{
sb.Append("<br />");
}
}
return MvcHtmlString.Create(sb.ToString()).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 string CheckBoxListVertical(this HtmlHelper htmlHelper,
string name,
List<CheckBoxListInfo> 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");
}
if (listInfo.Count < 1)
{
throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");
}
int dataCount = listInfo.Count;
// calculate number of rows
int rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(columnNumber)));
if (dataCount <= columnNumber || dataCount - columnNumber == 1)
{
rows = dataCount;
}
TagBuilder wrapBuilder = new TagBuilder("div");
wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");
string wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
string wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");
string wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));
StringBuilder sb = new StringBuilder();
sb.Append(wrapStart);
int lineNumber = 0;
foreach (CheckBoxListInfo info in listInfo)
{
TagBuilder builder = new TagBuilder("input");
if (info.IsChecked)
{
builder.MergeAttribute("checked", "checked");
}
builder.MergeAttributes<string, object>(htmlAttributes);
builder.MergeAttribute("type", "checkbox");
builder.MergeAttribute("value", info.Value);
builder.MergeAttribute("name", name);
sb.Append(builder.ToString(TagRenderMode.Normal));
TagBuilder labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", name);
labelBuilder.InnerHtml = info.DisplayText;
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()).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 string CheckBoxList(this HtmlHelper htmlHelper,
string name,
List<CheckBoxListInfo> 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<CheckBoxListInfo> listInfo");
}
if (listInfo.Count < 1)
{
throw new ArgumentException("List<CheckBoxListInfo> listInfo 至少要有一組資料", "listInfo");
}
StringBuilder sb = new StringBuilder();
int lineNumber = 0;
switch (position)
{
case Position.Horizontal:
foreach (CheckBoxListInfo info in listInfo)
{
lineNumber++;
sb.Append(CreateCheckBoxItem(info, name, htmlAttributes));
if (number == 0 || (lineNumber % number == 0))
{
sb.Append("<br />");
}
}
break;
case Position.Vertical:
int dataCount = listInfo.Count;
// 計算最大顯示的列數(rows)
int rows = Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(dataCount) / Convert.ToDecimal(number)));
if (dataCount <= number || dataCount - number == 1)
{
rows = dataCount;
}
TagBuilder wrapBuilder = new TagBuilder("div");
wrapBuilder.MergeAttribute("style", "float: left; light-height: 25px; padding-right: 5px;");
string wrapStart = wrapBuilder.ToString(TagRenderMode.StartTag);
string wrapClose = string.Concat(wrapBuilder.ToString(TagRenderMode.EndTag), " <div style=\"clear:both;\"></div>");
string wrapBreak = string.Concat("</div>", wrapBuilder.ToString(TagRenderMode.StartTag));
sb.Append(wrapStart);
foreach (CheckBoxListInfo info in listInfo)
{
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()).ToString();
}
internal static string CreateCheckBoxItem(CheckBoxListInfo info, string name, IDictionary<string, object> htmlAttributes)
{
StringBuilder sb = new StringBuilder();
TagBuilder builder = new TagBuilder("input");
if (info.IsChecked)
{
builder.MergeAttribute("checked", "checked");
}
builder.MergeAttributes<string, object>(htmlAttributes);
builder.MergeAttribute("type", "checkbox");
builder.MergeAttribute("value", info.Value);
builder.MergeAttribute("name", name);
sb.Append(builder.ToString(TagRenderMode.Normal));
TagBuilder labelBuilder = new TagBuilder("label");
labelBuilder.MergeAttribute("for", name);
labelBuilder.InnerHtml = info.DisplayText;
sb.Append(labelBuilder.ToString(TagRenderMode.Normal));
return sb.ToString();
}
#endregion
}
public enum Position
{
Horizontal,
Vertical
}
為什麼都過了這麼久才又想起以前所寫過的CheckBoxList還沒寫Vertical的部份呢?
其實是因為這幾週看CodeProject的NewsLetter時,我都有看到一個主題:
「MVC3 @Html.CheckBoxList() custom extension」
是有進去看過內容也下載他的原始檔來看,但是因為工作忙以及另有其他事情要處理而一直忽略,
今天中午休息的時候就把他的原始檔再度開啟,然後仔細研究這個專案的MvcCheckBoxList.cs檔案,
他寫的方式比較複雜而且比較嚴謹,除了一般的使用方式外,也包涵ModalBase的使用方法,
而且看到了他有寫出垂直方向呈現的實作內容,還且一次還來個兩種…Table方式以及Div方式,
所以我就用了他程式中的以Div方式產生垂直方向呈現的Tag Render程式片段,然後加以修改為我所使用的內容,
有興趣使用功能比較嚴謹而且可以使用以Table方式呈現的CheckBoxList,可以參考這個Project的內容。
http://www.codeproject.com/Articles/292050/MVC3-Html-CheckBoxList-custom-extension
另外不想要下載檔案而想要線上瀏覽並且使用Git來追蹤原始碼的話,也可以到者個專案位於github的Reposoitory
「devnoob / MVC3-Html.CheckBoxList-custom-extension」
https://github.com/devnoob/MVC3-Html.CheckBoxList-custom-extension
以上
「MVC3 @Html.CheckBoxList() custom extension」
回覆刪除作者(Mikhail Tsennykh)有把他所做CheckBoxList放上NuGet Galley囉
https://nuget.org/packages/MvcCheckBoxList
有需要的人也可以在VS2010中透過Nuget Managament來為你的MVC專案安裝上
各种感谢.
回覆刪除我這篇所寫的 CheckBoxList 功能,在最近有改寫了新版本,建議使用新版本喔
刪除http://kevintsengtw.blogspot.tw/2012/09/aspnet-mvc-checkboxlist-radiobuttonlist.html