前言:之前学习过很多的Bootstrap组件,博主就在脑海里构思:是否可以封装一套自己Bootstrap组件库呢。再加上看到MVC的Razor语法里面直接通过后台方法输出前端控件的方式,于是打算仿照HtmlHelper封装一套BootstrapHelper,今天只是一个开头,讲述下如何封装自己的Html组件,以后慢慢完善。

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

一、揭开HtmlHelper的“面纱”

经常使用Razor写法的园友都知道,在cshtml里面,我们可以通过后台的方法输出成前端的html组件,比如我们随便看两个例子:

输出成Html之后

博主的好奇心又来了,它是怎么做到的呢?于是将 Html 对象以及 Label() 方法转到定义

由此可以看出Html对象是HtmlHelper类型的一个实例,而Label()方法则是HtmlHelper类型的一个扩展方法,所以就可以直接通过Html.Label()这种方式直接调用。不熟悉C#扩展方法的园友可以看看http://www.cnblogs.com/landeanfen/p/4632467.html。

既然我们想要封装自己的HtmlHelper,那么我们就必须要了解Label()方法里面是如何实现的,我们伟大的Reflector又派上用场了。我们来反编译System.Web.MVC.dll看看。找到LabelExtensions这个类

经过一系列的转到定义,我们找到最终的方法

同样,我们找到TextBox()最终定义的方法

哟西,原来就是TagBuilder这个一个小东西,让人觉得神奇得不要不要的。所以有时我们需要敢于反编译,或许看似高级的背后其实很简单呢~~

二、BootstrapHelper组件封装准备

1、定义BootstrapHelper

有了以上的基础做准备,接下来就是具体的实现了,我们新建了一个空的MVC项目,添加如下文件。

编译发现报错如下

将HtmlHelper转到定义发现它有两个构造函数,分别有两个、三个参数

那么,我们的BootstrapHelper也定义两个构造函数,于是代码变成这样:

namespace Extensions
{    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){ }}
}

这样通过子类复用父类的构造函数的方式即可解决以上问题。编译通过!

2、定义LabelExtensions

上面我们研究过HtmlHelper,在HtmlHelper里面,不同的html组件定义了不同的Extension(扩展),下面我们就以最简单的Label标签为例定义我们BootstrapHelper里面的Label标签。

同样,在Extensions文件夹里面我们新建了一个文件LabelExtensions.cs,用于定义Label标签的扩展,它里面的基本实现如下:

namespace Extensions
{       public static class LabelExtensions{             /// <summary>/// 通过使用指定的 HTML 帮助器和窗体字段的名称,返回Label标签         /// </summary>/// <param name="html">扩展方法实例</param>/// <param name="id">标签的id</param>/// <param name="content">标签的内容</param>/// <param name="cssClass">标签的class样式</param>/// <param name="htmlAttributes">标签的额外属性(如果属性里面含有“-”,请用“_”代替)</param>/// <returns>label标签的html字符串</returns>public static MvcHtmlString Label(this BootstrapHelper html, string id, string content, string cssClass, object htmlAttributes){            //定义标签的名称TagBuilder tag = new TagBuilder("label");            //给标签增加额外的属性IDictionary<string, object> attributes =   BootstrapHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);              if (!string.IsNullOrEmpty(id)){attributes.Add("id", id);}            if (!string.IsNullOrEmpty(cssClass)){                //给标签增加样式tag.AddCssClass(cssClass);}            //给标签增加文本tag.SetInnerText(content);tag.AddCssClass("control-label");tag.MergeAttributes(attributes);                    return MvcHtmlString.Create(tag.ToString());}}
}

我们暂且只定义一个方法,其他的重载我们很好扩展,这里给所有的BootstrapHelper里面的Label标签统一添加了“control-label”样式,当然,如果你的项目里面的label标签定义了自己的样式,那么这里改成你需要的样式即可。以上代码都比较基础,这里就不一一讲解。

3、定义BootstrapWebViewPage

以上定义了BootstrapHelper和LabelExtensions,准备工作是做好了,但是还少一个对象,比如我们在cshtml页面里面 @Html.Label("姓名") 这样写,Html变量是一个HtmlHelper类型的对象,那么,如果我们需要使用类似 @Bootstrap.Label() 这种写法,以此类推,Bootstrap变量应该也是一个BootstrapHelper类型的对象,那么如果我们要这么用,必须要先定义一个Bootstrap变量,这个变量到底在哪里定义呢。于是博主思考,Html变量是定义在哪里的呢?再次转到定义

原来是在WebViewPage这个类的子类中,同样,我们在Extensions文件夹里面也新建一个WebViewPage的子类BootstrapWebViewPage,实现代码如下:

namespace Extensions
{        public abstract class BootstrapWebViewPage<T> :                                System.Web.Mvc.WebViewPage<T>{             //在cshtml页面里面使用的变量public BootstrapHelper Bootstrap { get; set; }           /// <summary>/// 初始化Bootstrap对象              /// </summary>public override void InitHelpers(){            base.InitHelpers();Bootstrap = new BootstrapHelper(ViewContext, this);}        public override void Execute(){            //throw new NotImplementedException();}}
}

至于这里的泛型,我们以后再来做讲解,这里先不做过多纠结

4、实践

有了以上三步,所有需要的方法和变量都齐全了,貌似已经“万事俱备只欠东风”了,是不是这样呢?我们来试一把

编译,将Index.cshtml页面关闭重新打开,发现仍然找不到Bootstrap对象

怎么回事呢,Html是可以找到的,那Bootstrap变量去哪里了呢。。。

经过一番查找资料,发现在View文件夹里面有一个web.config文件(之前一直没怎么在意这个东西,现在想想里面还是有学问的哦),里面有一个节点system.web.webPages.razor下面有一个pages节点,默认是这样的:

  <system.web.webPages.razor><host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /><pages pageBaseType="System.Web.Mvc.WebViewPage"><namespaces><add namespace="System.Web.Mvc" /><add namespace="System.Web.Mvc.Ajax" /><add namespace="System.Web.Mvc.Html" /><add namespace="System.Web.Routing" /><add namespace="BootstrapHelper" /></namespaces></pages></system.web.webPages.razor>

我们将pages节点的pageBaseType改成我们的WebViewPage

  <system.web.webPages.razor><host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.2.0, Culture=neutral,  PublicKeyToken=31BF3856AD364E35" /><pages pageBaseType="Extensions.BootstrapWebViewPage"><namespaces><add namespace="System.Web.Mvc" /><add namespace="System.Web.Mvc.Ajax" /><add namespace="System.Web.Mvc.Html" /><add namespace="System.Web.Routing" /><add namespace="BootstrapHelper" /></namespaces></pages></system.web.webPages.razor>

然后编译,重新打开Index.cshtml。

OK,可以找到Bootstrap对象了。我们将Index.cshtml里面写入如下内容:

@{Layout = null;
}<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width" /><title>Index</title></head><body><div> @Html.Label("姓名")@Html.TextBox("a", "Jim")@Bootstrap.Label(null, "Bootstrap Label标签", null, null)    </div></body></html>

运行看看效果:

怎么还是报错呢?这个问题应该不难理解,因为在razor里面使用@调用后台变量和方法的时候也存在命名空间的概念,这个命名空间在哪里引用呢,还是在View文件夹里面的web.config里面,在system.web.webPages.razor节点下面存在namespace的节点,我们将自定义的Label()扩展方法所在的命名空间加进去即可。于是配置变成这样:

  <system.web.webPages.razor><host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /><pages pageBaseType="Extensions.BootstrapWebViewPage"><namespaces><add namespace="System.Web.Mvc" /><add namespace="System.Web.Mvc.Ajax" /><add namespace="System.Web.Mvc.Html" /><add namespace="System.Web.Routing" /><add namespace="BootstrapHelper" /><add namespace="Extensions"/></namespaces></pages></system.web.webPages.razor>

再次运行

三、BootstrapHelper组件完善

通过上面一系列发现坑、填坑的经历,一个最最简单的BootstrapHelper组件已经基本可用。我们将LabelExtensions简单完善下:

namespace Extensions
{       public static class LabelExtensions{             public static MvcHtmlString Label(this BootstrapHelper html,        string id){                   return Label(html, id, null, null, null);}                public static MvcHtmlString Label(this BootstrapHelper html,         string content){            return Label(html, null, content, null, null);}                public static MvcHtmlString Label(this BootstrapHelper html,          string id, string content){            return Label(html, id, content, null, null);}        

         public static MvcHtmlString Label(this BootstrapHelper html,          string id, string content, object htmlAttributes){                        return Label(html, id, content, null, htmlAttributes);}       

          /// <summary>/// 通过使用指定的 HTML 帮助器和窗体字段的名称,返回Label标签         /// </summary>/// <param name="html">扩展方法实例</param>/// <param name="id">标签的id</param>/// <param name="content">标签的内容</param>/// <param name="cssClass">标签的class样式</param>/// <param name="htmlAttributes">标签的额外属性        (如果属性里面含有“-”,请用“_”代替)</param>/// <returns>label标签的html字符串</returns>public static MvcHtmlString Label(this BootstrapHelper html,    string id, string content, string cssClass, object htmlAttributes){            //定义标签的名称TagBuilder tag = new TagBuilder("label");                      //给标签增加额外的属性IDictionary<string, object> attributes =     BootstrapHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);                      if (!string.IsNullOrEmpty(id)){attributes.Add("id", id);}            if (!string.IsNullOrEmpty(cssClass)){                //给标签增加样式tag.AddCssClass(cssClass);}            //给标签增加文本tag.SetInnerText(content);tag.AddCssClass("control-label");tag.MergeAttributes(attributes);                      return MvcHtmlString.Create(tag.ToString());}}
}

呵呵,是不是有模有样~~可能又有人要说博主“山寨”了,呵呵,不管山寨不山寨,你觉得爽就行。

四、总结

这篇先到这里,一路填坑,基本功能总算可用。还有一些需要完善的地方,比如泛型,比如lamada表达式等等,来日方长,博主有时间完善下。还有最基础的一些表单控件,我们都需要封装,这个估计还有点工作量,只能慢慢来完善了,等完善都一定的程度会开源在git上,希望自己能够坚持下去!如果你觉得本文对你有帮助,请帮忙推荐下,您的推荐是博主坚持完善的动力。

原文地址:http://www.cnblogs.com/landeanfen/p/5729551.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

一步一步封装自己的HtmlHelper组件:BootstrapHelper相关推荐

  1. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)...

    阅读目录 一.NumberBoxExtensions 二.DateTimeBoxExtensions 1.初始方案 2.改进方案 三.TextareExtensions 四.SelectExtensi ...

  2. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(二)

    阅读目录 一.新增泛型的BootstrapHelper 二.TextBoxExtensions 三.RadioExtensions和CheckboxExtensions 四.ButtonExtensi ...

  3. 艾伟_转载:[一步一步MVC]第五回:让TagBuilder丰富你的HtmlHelper

    本系列文章导航 [一步一步MVC]第一回:使用ActionSelector控制Action的选择 [一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 [ ...

  4. 一步一步学Silverlight 2系列(8):使用样式封装控件观感

    概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...

  5. 一步一步使用 DialogFragment 封装链式调用 Dialog

    前言 日常开发中,Dialog 是一个每个 app 所必备的. 2018-01-31更新 最后封装好的 BaseDialogFragment 已经添加到我的快速开发 lib 包中. 可以通过:impl ...

  6. 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](三)

    前言 上一篇<一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)>我们通过如下操作: 创建实体及工具类 创建Re ...

  7. 手挽手带你学React:四档(上)一步一步学会react-redux (自己写个Redux)

    手挽手带你学React入门四档,用人话教你react-redux,理解redux架构,以及运用在react中.学完这一章,你就可以开始自己的react项目了. 之前在思否看到过某个大神的redux搭建 ...

  8. netcore权限控制_记录这两年是如何一步一步转型到.net core+k8s

    2017年12月份,我离开北京,回到了武汉,开始在现在这家公司担任架构师工作.经过2年的时间,逐步完成以.net core+k8s为核心的技术架构.文末有彩蛋. 以下整理这两年的主要时间节点: 201 ...

  9. 【转】一步一步学Linq to sql(五):存储过程

    普通存储过程 首先在查询分析器运行下面的代码来创建一个存储过程: create proc sp_singleresultset as set nocount on select * from cust ...

最新文章

  1. 【Java】类与对象 - 对象的组合
  2. 游戏+云服务器+自动驾驶样样全,看NVIDIA为中国市场带来了哪些好东西 | GTC China 2018...
  3. java中的匿名内部类
  4. 加密货币支付卡公司与BCH达成合作
  5. 华硕k555l拆光驱_2L大小的迷你电脑用起来有什么区别?华硕VC66
  6. jexus php 重写,如何让我们的PHP在Jexus中跑起来
  7. ArcEngine编辑功能的实现(二)
  8. 18年石油大学c语言网考答案,石油大学华东C语言2018在线考试.doc
  9. eoiioe linux下解压命令大全
  10. 工业控制中无线局域网应用前景分析
  11. BP神经网络算法:将参数矩阵向量化
  12. 前后端怎么连接_如何搭建前后端分离的测试平台
  13. win10安装NET Framework 3.5提示0x800f0906原因及解决方法
  14. 设计模式(4)——工厂模式
  15. mysql2008完全卸载教程_完美卸载SQL Server 2008的方法
  16. py12306 购票助手
  17. 几分钟就可做出的酷炫PPT动画效果
  18. Unity AI 之 行为树 的简单介绍
  19. 船舶导航软件测试,北斗卫星船舶定位及信息通信应用
  20. ECS架构 Entitas-CSharp学习之路(三)

热门文章

  1. 常见的http状态码
  2. 8606 二叉树遍历的建设和运营
  3. LeetCode --- Valid Parentheses
  4. 公司服务器iSCSI网络硬盘连接故障
  5. Mysql 常用函数总结
  6. SharePoint 常见问题
  7. JavaScript对SEO的影响及解决之道
  8. MFC和Win32之三___CGdiObject类和windows Gdi对象
  9. 2021 .NET Conf China 主题分享之-轻松玩转.NET大规模版本升级
  10. 基于ABP落地领域驱动设计-05.实体创建和更新最佳实践