C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper

Posted 懒得安分

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper相关的知识,希望对你有一定的参考价值。

前言:上篇介绍了下封装BootstrapHelper的一些基础知识,这篇继续来完善下。参考htmlHelper的方式,这篇博主先来封装下一些常用的表单组件。关于BootstrapHelper封装的意义何在,上篇评论里面已经讨论得太多,这里也不想过多纠结。总之一句话:凡事有得必有失,就看你怎么去取舍。有兴趣的可以看看,没兴趣的权当博主讲了个“笑话”吧。

本文原创地址:http://www.cnblogs.com/landeanfen/p/5746166.html

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的valu

以上是关于C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper的主要内容,如果未能解决你的问题,请参考以下文章

(原创)[C#] 一步一步自定义拖拽(Drag&Drop)时的鼠标效果:基本原理及基本实现

C#一步一步学网络辅助开发--常用抓包工具的使用

《微服务零基础入门教程》一步一步,带你走进微服务的世界

一步一步学习大数据系列

完全开源基于插件的开发框架(使用DEV实现Ribbon界面)

Netty服务端是如何一步一步启动的?