1. 程式人生 > >C#進階系列——一步一步封裝自己的HtmlHelper元件:BootstrapHelper(二)

C#進階系列——一步一步封裝自己的HtmlHelper元件:BootstrapHelper(二)

前言:上篇介紹了下封裝BootstrapHelper的一些基礎知識,這篇繼續來完善下。參考HtmlHelper的方式,這篇博主先來封裝下一些常用的表單元件。關於BootstrapHelper封裝的意義何在,上篇評論裡面已經討論得太多,這裡也不想過多糾結。總之一句話:凡事有得必有失,就看你怎麼去取捨。有興趣的可以看看,沒興趣的權當博主講了個“笑話”吧。

BootstrapHelper系列文章目錄

一、新增泛型的BootstrapHelper

上篇博主只定義了一個BootstrapHelper的普通型別去繼承HtmlHelper,由於考慮到需要使用lamada的方式去定義元件,博主又增加了一個BootstrapHelper的泛型型別。於是BootstrapHelper變成了這樣。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace BootstrapExtensions
{
    public class BootstrapHelper : System.Web.Mvc.HtmlHelper
    {
        /// <summary>
        /// 使用指定的檢視上下文和檢視資料容器來初始化 BootstrapHelper 類的新例項。
        
/// </summary> /// <param name="viewContext">檢視上下文</param> /// <param name="viewDataContainer">檢視資料容器</param> public BootstrapHelper(ViewContext viewContext, IViewDataContainer viewDataContainer) : base(viewContext, viewDataContainer) { }
/// <summary> /// 使用指定的檢視上下文、檢視資料容器和路由集合來初始化 BootstrapHelper 類的新例項。 /// </summary> /// <param name="viewContext">檢視上下文</param> /// <param name="viewDataContainer">檢視資料容器</param> /// <param name="routeCollection">路由集合</param> public BootstrapHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) : base(viewContext, viewDataContainer, routeCollection) { } } /// <summary> /// 表示支援在強型別檢視中呈現 Bootstrap 控制元件。 /// </summary> /// <typeparam name="TModel"></typeparam> public class BootstrapHelper<TModel> : BootstrapHelper { /// <summary> /// 使用指定的檢視上下文和檢視資料容器來初始化 <![CDATA[Net.Web.Mvc.BootstrapHelper<TModel>]]> 類的新例項。 /// </summary> /// <param name="viewContext">檢視上下文。</param> /// <param name="viewDataContainer">檢視資料容器。</param> public BootstrapHelper(ViewContext viewContext, IViewDataContainer viewDataContainer) : base(viewContext, viewDataContainer) { } /// <summary> /// 使用指定的檢視上下文、檢視資料容器和路由集合來初始化 <![CDATA[Net.Web.Mvc.BootstrapHelper<TModel>]]> 類的新例項。 /// </summary> /// <param name="viewContext">檢視上下文。</param> /// <param name="viewDataContainer">檢視資料容器。</param> /// <param name="routeCollection">路由集合。</param> public BootstrapHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection) : base(viewContext, viewDataContainer, routeCollection) { } } }

我們的Bootstrap物件也定義成泛型物件

    public abstract class BootstrapWebViewPage<TModel> : System.Web.Mvc.WebViewPage<TModel>
    {
        //在cshtml頁面裡面使用的變數
        public BootstrapHelper<TModel> Bootstrap { get; set; }

        /// <summary>
        /// 初始化Bootstrap物件
        /// </summary>
        public override void InitHelpers()
        {
            base.InitHelpers();
            Bootstrap = new BootstrapHelper<TModel>(ViewContext, this);
        }

        public override void Execute()
        {
            //throw new NotImplementedException();
        }
    }

這樣做的意義就是為了在cshtml頁面裡面可以使用 @Bootstrap.TextBoxFor(x => x.Name) 這種寫法。這個後面介紹,這裡先埋個伏筆。

二、TextBoxExtensions

TextBoxExtensions.cs的實現程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace BootstrapExtensions
{
    /// <summary>
    /// bootstrap TextBox文字框的所有擴充套件
    /// </summary>
    public static class TextBoxExtensions
    {
        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">表單元素的name屬性值</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string name)
        {
            return InputExtensions.Helper(html, InputType.Text, null, name, null, false, null);
        }

        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">表單元素的name屬性值</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string id, string name)
        {
            return InputExtensions.Helper(html, InputType.Text, id, name, null, false, null);
        }

        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">單元素的name屬性值</param>
        /// <param name="value">表單元素的value值</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string id, string name, object value)
        {
            return InputExtensions.Helper(html, InputType.Text, id, name, value, false, null);
        }

        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">單元素的name屬性值</param>
        /// <param name="value">表單元素的value值</param>
        /// <param name="placeholder">bootstrap自帶的文字框的提示輸入值</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string id, string name, object value, string placeholder)
        {
            IDictionary<string, object> attributes = new Dictionary<string, object>();
            if (!string.IsNullOrEmpty(placeholder))
            {
                attributes.Add("placeholder", placeholder);
            }
            return InputExtensions.Helper(html, InputType.Text, id, name, value, false, attributes);
        }

        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">單元素的name屬性值</param>
        /// <param name="value">表單元素的value值</param>
        /// <param name="htmlAttributes">額外屬性</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string id, string name, object value, object htmlAttributes)
        {
            IDictionary<string, object> attributes = BootstrapHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            return InputExtensions.Helper(html, InputType.Text, id, name, value, false, attributes);
        }

        /// <summary>
        /// 通過使用指定的 HTML 幫助器和窗體欄位的名稱,返回文字框標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">表單元素的name屬性值</param>
        /// <param name="value">表單元素的value值</param>
        /// <param name="placeholder">bootstrap自帶的文字框的提示輸入值</param>
        /// <param name="htmlAttributes">額外屬性</param>
        /// <returns>返回input type='text'標籤</returns>
        public static MvcHtmlString TextBox(this BootstrapHelper html, string id, string name, object value, string placeholder, object htmlAttributes)
        {
            IDictionary<string, object> attributes = BootstrapHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
            if (!string.IsNullOrEmpty(placeholder))
            {
                attributes.Add("placeholder", placeholder);
            }
            return InputExtensions.Helper(html, InputType.Text, id, name, value, false, attributes);
        }

        public static MvcHtmlString TextBoxFor<TModel, TProperty>(this BootstrapHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
        {
            var model = (TModel)html.ViewData.Model;
            string propertyName;
            object value;
            InputExtensions.GetValueByExpression<TModel, TProperty>(expression, model, out propertyName, out value);
           return InputExtensions.Helper(html, InputType.Text, propertyName, propertyName, value, false, null);
        }

    }
}
TextBoxExtensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Mvc;

namespace BootstrapExtensions
{
    /// <summary>
    /// Bootstrap表單元素Input擴充套件方法集合
    /// </summary>
    public static class InputExtensions
    {
        public static MvcHtmlString Helper(BootstrapHelper html, InputType inputType, string id, string name,  object value, bool isCheck, IDictionary<string, object> htmlAttributes)
        {
            //定義標籤的名稱
            TagBuilder tag = new TagBuilder("input");
            if (htmlAttributes == null)
            {
                htmlAttributes = new Dictionary<string, object>();
            }
            //新增name
            if (!string.IsNullOrEmpty(name))
            {
                htmlAttributes.Add("name", name);
            }
            //新增id
            if (!string.IsNullOrEmpty(id))
            {
                htmlAttributes.Add("id", id);
            }
            //新增value
            if (value != null)
            {
                htmlAttributes.Add("value", value.ToString());
            }
            //新增input的型別
            tag.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
            //新增預設樣式
            tag.AddCssClass("form-control");
            tag.MergeAttributes(htmlAttributes);

            if (inputType == InputType.Radio || inputType == InputType.CheckBox)
            {
                if (isCheck) 
                    tag.MergeAttribute("checked", "checked");
            }
            return MvcHtmlString.Create(tag.ToString());
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <param name="text">顯示文字</param>
        /// <param name="labelClass">label標籤的樣式</param>
        /// <param name="isCheck">是否選中</param>
        /// <param name="isDisabled">是否禁用</param>
        /// <param name="oAttributes">額外標籤</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString CheckBox(BootstrapHelper html, InputType inputType, string id, string name, object value, string text, string labelClass, bool isCheck, bool isDisabled, IDictionary<string, object> htmlAttributes)
        {
            //定義標籤的名稱
            TagBuilder tag = new TagBuilder("label");
            if (!string.IsNullOrEmpty(labelClass))
            {
                htmlAttributes.Add("class", labelClass);
            }
            System.Text.StringBuilder sbInput = new System.Text.StringBuilder();
            var strInputType = HtmlHelper.GetInputTypeString(inputType);
            sbInput.Append("<input type='").Append(strInputType).Append("'");
            if (!string.IsNullOrEmpty(name))
            {
                sbInput.Append(" name = '").Append(name).Append("'");
            }
            if (!string.IsNullOrEmpty(id))
            {
                sbInput.Append(" id = '").Append(id).Append("'");
            }
            if (value != null)
            {
                sbInput.Append(" value = '").Append(value.ToString()).Append("'");
            }
            if (isCheck)
            {
                sbInput.Append(" checked = 'checked'");
            }
            if (isDisabled)
            {
                sbInput.Append(" disabled");
            }
            sbInput.Append(" />");
            if (!string.IsNullOrEmpty(text))
            {
                sbInput.Append(text);
            }
            tag.InnerHtml = sbInput.ToString();

            tag.MergeAttributes(htmlAttributes);
            return MvcHtmlString.Create(tag.ToString());
        }

        //通過表示式取當前的屬性值
        public static void GetValueByExpression<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, TModel model, out string propertyName, out object value)
        {
            MemberExpression body = (MemberExpression)expression.Body;
            var lamadaName = (body.Member is PropertyInfo) ? body.Member.Name : null;
            propertyName = lamadaName;

            value = null;
            System.Reflection.PropertyInfo[] lstPropertyInfo = typeof(TModel).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var oFind = lstPropertyInfo.FirstOrDefault(x => x.Name == lamadaName);
            if (oFind != null)
            {
                value = oFind.GetValue(model, null);
            }
        }
    }
}
InputExtensions.cs

1、考慮到專案中所有基於bootstrap的TextBox文字框都有一個class="form-control"樣式,所以在封裝文字框的時候直接將它放到了標籤裡面。當然,如果你的專案裡面不需要這麼用,或者自定義了文字框樣式,這裡也可以寫入自己的樣式,這樣就不用每次宣告文字框的時候都去新增這些樣式了。

2、TextBoxFor()方法裡面融合了使用lamada的方式生成文字框,上面宣告的泛型BootstrapHelper型別物件就派上用場了,通過反射和泛型去讀取lamada裡面的屬性名和屬性值,這裡只定義了一個方法,如果還需要其他過載,可以自己新增方法。

3、在使用的時候又遇到一個問題,由於BootstrapHelper是繼承至HtmlHelper型別,那麼MVC裡面原來封裝的一些HtmlHelper的擴充套件方法對於我們的Bootstrap物件也是可以直接呼叫的,所以很多過載可能出現重複和找不到對應的過載,比如:

這樣的話很容易會出現如下錯誤:

那麼,既然問題出現了,我們就要想辦法解決。博主想到的一種解決方案是:將view的web.config裡面的Html物件所在的名稱空間註釋掉。比如:

這樣的話就能夠解決我們上面的問題,執行效果:

將上面的名稱空間註釋掉之後,在cshtml頁面裡面我們將不能再使用Html變數的相關擴充套件方法,如果你自己的Helper夠用,不用原生的擴充套件方法問題應該也不大。

三、RadioExtensions和CheckboxExtensions

關於bootstrap裡面的radio和checkbox元件,博主參考了下http://v3.bootcss.com/css/裡面的寫法,進行了如下封裝:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace BootstrapExtensions
{
    public static class RadioExtensions
    {
        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="name">name屬性</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string name)
        {
            return Radio(html, null, name, null, null, null, false, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name)
        {
            return Radio(html, id, name, null, null, null, false, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="isCheck">是否選中</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, bool isCheck)
        {
            return Radio(html, id, name, null, null, null, isCheck, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, object value)
        {
            return Radio(html, id, name, value, null, null, false, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <param name="text">顯示文字</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, object value, string text)
        {
            return Radio(html, id, name, value, text, null, false, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <param name="text">顯示文字</param>
        /// <param name="isCheck">是否選中</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, object value, string text, bool isCheck)
        {
            return Radio(html, id, name, value, text, null, isCheck, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <param name="text">顯示文字</param>
        /// <param name="labelClass">label標籤的樣式</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, object value, string text, string labelClass)
        {
            return Radio(html, id, name, value, text, labelClass, false, false, null);
        }

        /// <summary>
        /// 返回表單radio標籤
        /// </summary>
        /// <param name="html">擴充套件方法例項</param>
        /// <param name="id">id</param>
        /// <param name="name">name屬性</param>
        /// <param name="value">input的value值</param>
        /// <param name="text">顯示文字</param>
        /// <param name="labelClass">label標籤的樣式</param>
        /// <param name="isCheck">是否選中</param>
        /// <returns>返回radio標籤</returns>
        public static MvcHtmlString Radio(this BootstrapHelper html, string id, string name, object value, string text, string labelClass, bool isCheck)
        {
            return Radio(html, id, name, value, text, labelClass, isCheck, false, null