鍍金池/ 教程/ C#/ ASP.NET MVC 隨想錄(5)——?jiǎng)?chuàng)建 ASP.NET MVC Bootstrap Helpers
ASP.NET MVC 使用 Bootstrap 系列(4)——使用 JavaScript 插件
ASP.NET MVC 隨想錄(6)——漫談 OWIN
ASP.NET MVC 隨想錄(5)——?jiǎng)?chuàng)建 ASP.NET MVC Bootstrap Helpers
ASP.NET MVC 隨想錄(3)——使用 Bootstrap 組件
ASP.NET MVC 隨想錄(7)——鋒利的 KATANA
ASP.NET MVC 隨想錄(1)——開始使用 Bootstrap
ASP.NET MVC 隨想錄(8)——?jiǎng)?chuàng)建自定義的 Middleware 中間件
ASP.NET MVC 隨想錄(2)——使用 Bootstrap CSS 和 HTML 元素
作者簡介

ASP.NET MVC 隨想錄(5)——?jiǎng)?chuàng)建 ASP.NET MVC Bootstrap Helpers

ASP.NET MVC 允許開發(fā)者創(chuàng)建自定義的 HTML Helpers,不管是使用靜態(tài)方法還是擴(kuò)展方法。一個(gè)HTML Helper 本質(zhì)上其實(shí)是輸出一段 HTML 字符串。 HTML Helpers 能讓我們在多個(gè)頁面上公用同一段 HTML 標(biāo)記,這樣不僅提高了穩(wěn)定性也便于開發(fā)者去維護(hù)。當(dāng)然對于這些可重用的代碼,開發(fā)者也方便對他們進(jìn)行單元測試。所以,創(chuàng)建 ASP.NET MVC Bootstrap Helpers 是及其有必要的。

內(nèi)置的HTML Helpers

ASP.NET MVC 內(nèi)置了若干標(biāo)準(zhǔn) HTML Helpers,通過 @ HTML 來調(diào)用這些方法在視圖引擎中解析、渲染輸出 HTML 內(nèi)容,這允許開發(fā)者在多個(gè)視圖中重用公共的方法。

舉個(gè)例子,以下代碼產(chǎn)生一個(gè) type 等于 text 的 Input ,并且其 id 和 name 都等于 CustomerName,其 Value 等于 Northwind Traders:

@ Html.TextBox("CustomerName","Northwind Traders");

大多數(shù)內(nèi)置的 HTML helpers 提供傳入匿名類型為元素產(chǎn)生指定 HTML 屬性的選項(xiàng),對上述的 @ HTML.TextBox方法稍作修改,通過傳入匿名類型設(shè)置輸出元素的 style 屬性:

 @Html.TextBox("CustomerName","Northwind Traders", new { style="background-color:Blue;" })

創(chuàng)建自定義的 Helpers

因?yàn)?Bootstrap 提供了大量不同的組件,所以創(chuàng)建 Bootstrap helpers 可以在多個(gè)視圖上快速使用這些組件。在 ASP.NET MVC 中最簡單創(chuàng)建 Bootstrap helpers 是通過 @helper 語法來實(shí)現(xiàn)。一個(gè)自定義的 helper 可以包含任何 HTML 標(biāo)記甚至 Razor 標(biāo)記,你可以通過如下步驟來創(chuàng)建:

在項(xiàng)目的根目錄創(chuàng)建文件夾 App_Code
在 App_Code 文件夾中新建 BootstrapHelpers.cshtml 文件并加入如下代碼

@helper PrimaryButtonSmall(string id,string caption)
{
    <button id="@id" type="button" class="btn btn-primary btn-sm">@caption</button>
}

上述代碼使用 @helper 創(chuàng)建了一個(gè)新的名為 PrimaryButtonSmall helper,它接受 2個(gè)參數(shù),分別是 Id 和 caption。其中,它產(chǎn)生一個(gè) Button 類型的 HTML 標(biāo)記并設(shè)置了 Bootstrap 的樣式。

注意:任何自定義的 helpers 必須存在 App_Code 文件夾中,這樣才能被 ASP.NET MVC 視圖識別。

  • 在視圖中通過 @BootstrapHelpers.PrimaryButtonSmall("btnSave","保存")來使用新創(chuàng)建的helper。
  • 它將產(chǎn)生如下 Bootstrap HTML 元素:

http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/images/Chapter5/2.png" alt="" />

當(dāng)然,為了讓我們的 helper 更加通用性,比如指定大小、樣式等,對上述稍作如下修改,增加傳入的參數(shù):

@helper Button(string style, string size, string caption, string id)
{
    <button id="@id" type="button" class="btn btn-@style btn-@size">@caption </button>
}

現(xiàn)在我們可以這樣去使用:

@BootstrapHelpers.Button("danger","lg","危險(xiǎn)","btnDanger")

它將產(chǎn)生如下樣式的按鈕:

http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/images/Chapter5/1.png" alt="" />

不過,這種方式的 helper 唯一的不足是你需要"hard code"傳入樣式和尺寸,這可能需要你非常熟悉Bootstrap 的樣式。

使用靜態(tài)方法創(chuàng)建 Helpers

通過靜態(tài)方法同樣也能快速方便的創(chuàng)建自定義 Bootstrap helpers,同樣它也是返回了 HTML 標(biāo)記,要?jiǎng)?chuàng)建靜態(tài)方法,你可以按照如下步驟來實(shí)現(xiàn):

1.添加命了 Helpers 的文件夾
2.創(chuàng)建如下枚舉類

public class ButtonHelper
   {
   public static MvcHtmlString Button(string caption, Enums.ButtonStyle style, Enums.ButtonSize size)
   {
       if (size != Enums.ButtonSize.Normal)
       {
           return new MvcHtmlString(string.Format("<button type=\"button\" class=\"btn btn-{0} btn-{1}\">{2}</button>", style.ToString().ToLower(), ToBootstrapSize(size), caption));
       }
       return new MvcHtmlString(string.Format("<button type=\"button\" class=\"btn btn-{0}\">{1}</button>", style.ToString().ToLower(), caption));
   }

   private static string ToBootstrapSize(Enums.ButtonSize size)
   {
       string bootstrapSize = string.Empty;
       switch (size)
       {
           case Enums.ButtonSize.Large:
               bootstrapSize = "lg";
               break;

           case Enums.ButtonSize.Small:
               bootstrapSize = "sm";
               break;

           case Enums.ButtonSize.ExtraSmall:
               bootstrapSize = "xs";
               break;
       }
       return bootstrapSize;
   }
  }

Button 方法返回了一個(gè) MvcHtmlString 對象,它代表了編碼過后的 HTML 字符。

1.通過使用靜態(tài)方法來調(diào)用:

 @ButtonHelper.Button("危險(xiǎn)", Enums.ButtonStyle.Danger, Enums.ButtonSize.Large)

你可以和之前的"hard code"寫法進(jìn)行比較,盡管他們產(chǎn)生相同的結(jié)果:

@BootstrapHelpers.Button("danger","lg","危險(xiǎn)","btnDanger")

使用擴(kuò)展方法創(chuàng)建Helpers

內(nèi)置的 ASP.NET MVC helper(@HTML)是基于擴(kuò)展方法的,我們可以再對上述的靜態(tài)方法進(jìn)行升級——使用擴(kuò)展方法來創(chuàng)建 Bootstrap helpers。

1.在 Helpers 文件夾下創(chuàng)建 ButtonExtensions 類 2.修改 ButtonExtensions為Static 類型 3.修改 Namespace為System.Web.Mvc.Html,這樣方便 @HTML 調(diào)用擴(kuò)展方法 4.添加擴(kuò)展方法,返回 MvcHtmlString

 public static MvcHtmlString BootstrapButton(this HtmlHelper helper, string caption, Enums.ButtonStyle style, Enums.ButtonSize size)
    {
        if (size != Enums.ButtonSize.Normal)
        {
            return new MvcHtmlString(string.Format("<button type=\"button\" class=\"btn btn-{0} btn-{1}\">{2}</button>", style.ToString().ToLower(), ToBootstrapSize(size), caption));
        }
        return new MvcHtmlString(string.Format("<button type=\"button\" class=\"btn btn-{0}\">{1}</button>", style.ToString().ToLower(), caption));
   ` }`

因?yàn)?BootstrapButton 方法是擴(kuò)展方法,通過如下方式去調(diào)用:

@Html.BootstrapButton("很危險(xiǎn)",Enums.ButtonStyle.Danger,Enums.ButtonSize.Large)

寫法雖不同,但返回的結(jié)果都是一致的。

創(chuàng)建 Fluent Helpers

Fluent Interface(參考:http://martinfowler.com/bliki/FluentInterface.html)用于軟件開發(fā)實(shí)現(xiàn)了一種面向?qū)ο蟮?/a> API,以這種方式,它提供了更多的可讀性代碼,易于開發(fā)人員理解。通常通過鏈?zhǔn)骄幊虂韺?shí)現(xiàn)。

舉個(gè)例子,我們將創(chuàng)建一個(gè) HTML helper 來產(chǎn)生一個(gè)可關(guān)閉的警告框,使用 Fluent Interface 可以這樣來調(diào)用:

 @Html.Alert("警告").Warning().Dismissible()

所以要?jiǎng)?chuàng)建 Fluent helpers,需要實(shí)現(xiàn)如下步驟:

1.創(chuàng)建 IFluentAlert 實(shí)現(xiàn) IHtmlString 接口,這是非常重要的一步,對于 ASP.NET MVC Razor 視圖引擎,如果 @ 之后返回的類型實(shí)現(xiàn)了 IHtmlString 接口,那么視圖引擎會(huì)自動(dòng)調(diào)用 ToHtmlString() 方法,返回實(shí)際的 HTML 標(biāo)記。

public interface IAlertFluent : IHtmlString 
{

   IAlertFluent Dismissible(bool canDismiss = true);

}

2.創(chuàng)建 IAlert 實(shí)現(xiàn) IFluentAlert 接口

public interface IAlert : IAlertFluent
{

    IAlertFluent Danger();

    IAlertFluent Info();

    IAlertFluent Success();

    IAlertFluent Warning();

}

3.創(chuàng)建 Alert 繼承 IAlert 接口

public class Alert : IAlert
   {
   private Enums.AlertStyle _style;
   private bool _dismissible;
   private string _message;

   public Alert(string message)
   {
       _message = message;
   }

   public IAlertFluent Danger()
   {
       _style = Enums.AlertStyle.Danger;
       return new AlertFluent(this);
   }

   public IAlertFluent Info()
   {
       _style = Enums.AlertStyle.Info;
       return new AlertFluent(this);
   }

   public IAlertFluent Success()
   {
       _style = Enums.AlertStyle.Success;
       return new AlertFluent(this);
   }

   public IAlertFluent Warning()
   {
       _style = Enums.AlertStyle.Warning;
       return new AlertFluent(this);
   }

   public IAlertFluent Dismissible(bool canDismiss = true)
   {
       this._dismissible = canDismiss;
       return new AlertFluent(this);
   }

   public string ToHtmlString()
   {
       var alertDiv = new TagBuilder("div");
       alertDiv.AddCssClass("alert");
       alertDiv.AddCssClass("alert-" + _style.ToString().ToLower());
       alertDiv.InnerHtml = _message;

       if (_dismissible)
       {
           alertDiv.AddCssClass("alert-dismissable");
           alertDiv.InnerHtml += AddCloseButton();
       }

       return alertDiv.ToString();
   }

   private static TagBuilder AddCloseButton()
   {
       var closeButton = new TagBuilder("button");
       closeButton.AddCssClass("close");
       closeButton.Attributes.Add("data-dismiss", "alert");
       closeButton.InnerHtml = "&times;";
       return closeButton;
   }
   }

上述代碼中,通過 TagBuilder 可以快速的創(chuàng)建 HTML 元素。

4.創(chuàng)建 AlertFluent 繼承 IAlertFluent

public class AlertFluent : IAlertFluent
{
    private readonly Alert _parent;

    public AlertFluent(Alert parent)
    {
        _parent = parent;
    }

    public IAlertFluent Dismissible(bool canDismiss = true)
    {
        return _parent.Dismissible(canDismiss);
    }

    public string ToHtmlString()
    {
        return _parent.ToHtmlString();
    }
}

通過構(gòu)建這種 Fluent API,我們可以鏈?zhǔn)降娜?chuàng)建 Bootstrap 組件,這對于不熟悉 Bootstrap Framework 的人來說是非常方便的,我們可以使用 @HTML.Alert("Title").Danger().Dismissible() 來創(chuàng)建如下風(fēng)格的警告框:

http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/images/Chapter5/3.png" alt="" />

創(chuàng)建自動(dòng)閉合的 Helpers

在 ASP.NET MVC 中,內(nèi)置的 @HTML.BeginForm() helper 就是一個(gè)自動(dòng)閉合的 helper。當(dāng)然我們也能自定義自動(dòng)閉合的 helpers,只要實(shí)現(xiàn) IDisposable 接口即可。使用 IDisposable 接口,當(dāng)對象 Dispose 時(shí)我們輸出元素的閉合標(biāo)記,具體按照如下步驟:

1.所以在 Helpers 文件夾下創(chuàng)建一個(gè)名為 Panel 的文件夾 2.添加 Panel,并實(shí)現(xiàn) IDisposable 接口

public class Panel : IDisposable
{
    private readonly TextWriter _writer;

    public Panel(HtmlHelper helper, string title, Enums.PanelStyle style = Enums.PanelStyle.Default)
    {
        _writer = helper.ViewContext.Writer;

        var panelDiv = new TagBuilder("div");
        panelDiv.AddCssClass("panel-" + style.ToString().ToLower());
        panelDiv.AddCssClass("panel");

        var panelHeadingDiv = new TagBuilder("div");
        panelHeadingDiv.AddCssClass("panel-heading");

        var heading3Div = new TagBuilder("h3");
        heading3Div.AddCssClass("panel-title");
        heading3Div.SetInnerText(title);

        var panelBodyDiv = new TagBuilder("div");
        panelBodyDiv.AddCssClass("panel-body");

        panelHeadingDiv.InnerHtml = heading3Div.ToString();

        string html = string.Format("{0}{1}{2}", panelDiv.ToString(TagRenderMode.StartTag), panelHeadingDiv, panelBodyDiv.ToString(TagRenderMode.StartTag));
        _writer.Write(html);
    }

    public void Dispose()
    {
        _writer.Write("</div></div>");
    }
}

上述代碼中利用Write屬性可以在當(dāng)前視圖中輸出HTML標(biāo)記,并在Dispose方法里輸出2個(gè)閉合的div標(biāo)簽。

注意,我們重寫了TagBuilder的ToString()方法,只讓它生成div元素的開始標(biāo)簽。
3.在View中使用自動(dòng)閉合的helpers

@using (Html.Panel("Title", Enums.PanelStyle.Success))
{
    <p>這是自動(dòng)閉合的Helpers</p>
    <p>panel..</p>
}

產(chǎn)生的結(jié)果如下:

http://wiki.jikexueyuan.com/project/think-in-asp-net-mvc/images/Chapter5/4.png" alt="" />

小結(jié)

在這篇博客中,為了減少書寫HTML標(biāo)記,我們創(chuàng)建了若干Bootstrap helpers來實(shí)現(xiàn)。這些helpers的意義在于能讓不了解Bootstrap Framework的人也能快速上手Bootstrap。