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

      刪除

提醒

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