網頁

2012年2月22日 星期三

ASP.NET MVC 擴充HtmlHelper 加入 CheckBoxList 功能 - 2


距離之前那一篇「ASP.NET MVC 擴充HtmlHelper 加入 CheckBoxList 功能 - 1」都已經超過半年了,

在那篇文章中我們實作出一個水平方向呈現的CheckBoxList,

image

有了水平方向呈現的方式,也應該要有垂直方向顯示的吧?!

因為垂直方向的呈現在那時候於實作的過程中一直處於「卡關」的狀態,一直寫不出來,所以就一直Pending住,

然而就在隔了這麼久之後終於讓我做出來了,也發現到,這垂直方向的呈現並不是那麼難做,

也不需要去參考System.Web.UI.WebControls.RepeatInfoRenderVerticalRepeater」,

總之就是幾個關鍵地方而已,所以就廢話少說,直接進入主題。



垂直顯示的CheckBoxList

何謂垂直顯示的CheckBoxList呢?就先直接看呈現的結果,

image

把這一張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來做呈現,

最後所呈現的結果,如下:

image

如果是改成以7個Columns來呈現的話,就會是下圖的樣子:

image

這樣子大家就可以清楚明白了。

 

為了不要讓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>

頁面的顯示結果:

image

 

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>
頁面呈現的結果:

image



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

image

 

另外不想要下載檔案而想要線上瀏覽並且使用Git來追蹤原始碼的話,也可以到者個專案位於github的Reposoitory

devnoob / MVC3-Html.CheckBoxList-custom-extension

https://github.com/devnoob/MVC3-Html.CheckBoxList-custom-extension

image

 

以上

3 則留言:

  1. 「MVC3 @Html.CheckBoxList() custom extension」
    作者(Mikhail Tsennykh)有把他所做CheckBoxList放上NuGet Galley囉

    https://nuget.org/packages/MvcCheckBoxList

    有需要的人也可以在VS2010中透過Nuget Managament來為你的MVC專案安裝上

    回覆刪除
  2. 回覆
    1. 我這篇所寫的 CheckBoxList 功能,在最近有改寫了新版本,建議使用新版本喔
      http://kevintsengtw.blogspot.tw/2012/09/aspnet-mvc-checkboxlist-radiobuttonlist.html

      刪除