程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 9.2.1 .net framework下的MVC 控件的封裝(上),9.2.1mvc

9.2.1 .net framework下的MVC 控件的封裝(上),9.2.1mvc

編輯:關於.NET

9.2.1 .net framework下的MVC 控件的封裝(上),9.2.1mvc


在寫.net core下mvc控件的編寫之前,我先說一下.net framework下我們MVC控件的做法。

MVC下控件的寫法,主要有如下三種,最後一種是泛型的寫法,mvc提供的控件都是基本控件。

1 @model UserInfo
2 
3 <input type="text" id="t2" value="t2Value" /> <!—第一種寫法 -->
4 @Html.TextBox("t1", "t1value");   <!—第二種寫法 -->
5 @Html.TextBoxFor(user => user.EMail)  <!—第三種寫法 -->

 

但是我們在寫大型系統的時候,像自動完成autocomplete、下拉多選multiselect、附件accessory、富文本編輯htmleditor、人員機構選擇oguinput等控件會在許多地方都會用到,這些控件也都包含一部分html標簽和script腳本。

就以下拉多選列表為例,其實這個控件包含了一個標簽(例如"請選擇人員:",不過我後來發現截圖沒截上),一個用於展示的select控件,一個用於存放實際選擇值的隱藏控件,以及一個JQuery的MultiSelect.js腳本,和調用MultiSelect.js的腳本。

如果在各個使用到MultiSelect的cshtml頁面中寫上這麼一堆重復性的html標簽和對應的腳本總不是一個好的技術人員該做的事情,封裝成一個MultiSelect控件是一個比較好的選擇。

 

既然做控件的封裝,首先就是抽象,將一個個的控件抽象、提取形成控件基類。這個基類,我們叫MvcControlBase。

但是,只是這樣的話,如果在頁面中使用,例如MultiSelect就得寫成這樣:

1 @{
2   MultiSelect control = new MultiSelect(…..); //創建實例
3   control.DataSource = …; //設置各種屬性,這裡需要設置數據源等屬性
4   Html.Raw(control.Render().ToString(); //呈現
5 }

 

這種寫法在cs程序中是沒有問題的,但是在cshtml中顯得有些土的掉渣了。html內的寫法盡量簡潔,像如下這樣連綴的寫法:

@Html.MultiSelect(…).SetDataSource(…).SetShowItemsCount(5).Render()

這就涉及到另外一個封裝基類,我們叫控件構建器,MvcControlBuilderBase。

 

下面就具體介紹下這兩個基類。

基控件類MvcControlBase是所有MVC控件的抽象,那麼應該包含名稱Name,Id(通過Name自動生成的),控件的標簽名稱,值,屬性集合,標簽單元格數,控件單元格數,顯示狀態等,這些屬性應該是所有控件都會用到的,以及一個前台呈現(Render)方法。

對於選擇日期控件 ,Name和Id都是theDate,標簽名稱是"日期:",值是"2016/10/17",屬性集合包含了cssclass、Style等,顯示狀態有普通、只讀、隱藏三個狀態。對於單元格數,我們使用了BootStrap作為css的框架集,如果設置了單元格數是3,那麼會自動生成col-md-3\col-xs-3\col-sm-3等cssclass。

 

由上引申出基控件的構造函數定義如下:

protected MvcControlBase(HtmlHelper helper, string name, object value, string label, object attributes)

因為顯示狀態、單元格數等都有缺省值,我們在構造函數中沒有對應的參數。

 

基控件類MvcControlBase最重要的方法是Render方法,就是在頁面中呈現控件。該方法,首先調用WriteHtml方法,生成Html標簽,然後再調用WriteScript方法,生成Script腳本,最終將標簽和腳本的內容生成MvcHtmlString返回。 

 1         public IHtmlString Render()
 2         {
 3             MvcHtmlString result = null;
 4 
 5             using (HtmlTextWriter htmlWriter = new HtmlTextWriter(new StringWriter()))
 6             {
 7                 WriteHtml(htmlWriter);
 8 
 9                 htmlWriter.WriteLine();
10 
11                 using (HtmlTextWriter scriptWriter = new HtmlTextWriter(new StringWriter()))
12                 {
13                     WriteScript(scriptWriter);
14 
15                     string scriptString = scriptWriter.InnerWriter.ToString();
16 
17                     if (!string.IsNullOrWhiteSpace(scriptString))
18                     {
19                         htmlWriter.RenderBeginTag(HtmlTextWriterTag.Script);
20                         htmlWriter.Write(scriptString);
21                         htmlWriter.RenderEndTag();
22                     }
23                 }
24 
25                 result = new MvcHtmlString(htmlWriter.InnerWriter.ToString());
26             }
27 
28             return result;
29         }

 

 

WriteHtml方法是個抽象方法,各個子控件必須予以重寫,生成控件的html元素; WriteScript方法,是個虛方法,如果子控件不需要腳本可以不重寫。

protected virtual void WriteScript(HtmlTextWriter writer) { }

protected abstract void WriteHtml(HtmlTextWriter writer);

 

我們繼續以多選下拉框控件MultiSelect為例說明如何繼承基控件。MultiSelect控件比缺省控件會多兩個屬性,一個是數據源DataSource,就是下拉列表的所有數據,還有一個是ShowItemsCount,就是下拉列表顯示的行數。此外,Value的類型也應該是List<string>,代表哪個數據被選擇,就是下拉列表的選中內容。

上圖的例子中,數據源是所有人List<UserInfo>,Value是Aaron和Adair,ShowItemsCount是9。

因為要控制select元素的展示內容和方式,因此MultiSelect前台需要部分腳本,我們選擇一個JQuery的開源的multiselect腳本。像圖中顯示的全選、查找等,是在multiselect.js腳本中實現的,我們初始化時,將該腳本的是否顯示全選、查找的選項設置為true,沒有通過我們封裝的控件暴露出來。

具體做法是,重寫WriteScript方法,編寫調用腳本的$(function() { … })的腳本,調用的腳本比較簡單,就不再詳細介紹了。然後重寫WriteHtml腳本,生成標簽和一個div。div內包含兩個控件,一個是顯示在頁面的select,一個是隱藏的hidden,用於存放選中值的。select控件的options就是從DataSource中獲取所有數據生成的,屬於Value的數據則設置選中狀態。

  1     public class MultiSelect : MvcControlBase
  2     {
  3         /// <summary>
  4         /// 初始化
  5         /// </summary>
  6         /// <param name="htmlHelper"></param>
  7         /// <param name="expression"></param>
  8         /// <param name="htmlAttributes"></param>
  9         public MultiSelect(HtmlHelper htmlHelper, string name, List<string> value, string labelName, object htmlAttributes)
 10             : base(htmlHelper, name, value, labelName, htmlAttributes)
 11         {
 12             this.DataSource = new Dictionary<string, string>();
 13             this.ShowItemsCount = 5;
 14         }
 15 
 16         #region 屬性
 17         /// <summary>
 18         /// 列表值
 19         /// </summary>
 20         internal Dictionary<string, string> DataSource { get; set; }
 21         /// <summary>
 22         /// 顯示項目的數量
 23         /// </summary>
 24         internal int ShowItemsCount { get; set; }
 25         #endregion
 26 
 27         /// <summary>
 28         /// 輸出控件JS代碼
 29         /// </summary>
 30         /// <param name="writer"></param>
 31         protected override void WriteScript(HtmlTextWriter writer)
 32         {
 33             string classes = string.Empty;
 34             if (this.DisplayStatus == FieldDisplayStatus.ReadOnly)
 35             {
 36                 classes = "ui-state-disabled";
 37             }
 38             else if (this.DisplayStatus == FieldDisplayStatus.Hidden)
 39             {
 40                 classes = " hidden";
 41             }
 42 
 43 
 44             string script = string.Format(@"
 45       $(function(){{
 46         $(""#{0}"").multiselect({{
 47         noneSelectedText: ""請選擇"",
 48         checkAllText: ""全選"",
 49         uncheckAllText: ""全不選"",
 50         selectedList: {1},
 51         classes: ""{2}""
 52         }});
 53     }});", "select-" + this.Id, this.ShowItemsCount.ToString(), classes);
 54 
 55             writer.Write(script);
 56 
 57         }
 58 
 59         /// <summary>
 60         /// 輸出控件Html代碼
 61         /// </summary>
 62         /// <param name="writer">HtmlTextWriter</param>
 63         protected override void WriteHtml(HtmlTextWriter writer)
 64         {
 65             List<string> value = this.Value as List<string>;
 66 
 67             if (this.DisplayStatus == FieldDisplayStatus.Hidden || this.LabelCellNumber <= 0)
 68             {
 69                 writer.Write(Helper.Label(LabelName, new RouteValueDictionary { { "class", string.Format("col-sm-{0} control-label hidden", LabelCellNumber) } }).ToHtmlString());
 70             }
 71             else
 72             {
 73                 writer.Write(Helper.Label(LabelName, new RouteValueDictionary { { "class", string.Format("col-sm-{0} control-label", LabelCellNumber) } }).ToHtmlString());
 74             }
 75 
 76             TagBuilder divTag = new TagBuilder("div");
 77             divTag.AddCssClass(string.Format("col-sm-{0}", ControlCellNumber));
 78 
 79             List<SelectListItem> selectList = new List<SelectListItem>();
 80             foreach (KeyValuePair<string, string> kvp in this.DataSource)
 81             {
 82                 SelectListItem item = new SelectListItem();
 83                 item.Text = kvp.Value;
 84                 item.Value = kvp.Key;
 85                 if (value != null && value.Contains(kvp.Key))
 86                 {
 87                     item.Selected = true;
 88                 }
 89                 selectList.Add(item);
 90             }
 91 
 92             //select 標簽的名字
 93             IDictionary<string, object> HtmlAttributesForSelect = new RouteValueDictionary();
 94             HtmlAttributesForSelect.Add("id", "select-" + this.Id);
 95             HtmlAttributesForSelect.Add("multiple", "multiple");
 96 
 97             divTag.InnerHtml = Helper.DropDownList(Name, selectList, HtmlAttributesForSelect).ToHtmlString();
 98             writer.Write(divTag.ToString());
 99             writer.Write("<input type=\"hidden\" id=\"" + this.Id + "\" />");
100         }
101     }

 

按照上面的方法,MultiSelect控件基本就完成了。為了實現@Html.MultiSelect(…).SetDataSource(…).SetShowItemsCount(5).Render() 這樣的寫法,我們還得寫控件構建器。一樣的,有控件基類MvcControlBase,也得有控件基構建器類MvcControlBuilderBase。控件構造器主要作用是,傳入控件,根據控件的屬性和方法,生成一個個的連綴方法。

這個構建器類MvcControlBuilderBase我只寫了一部分內容,類定義的寫法比較繞一些,也算是一種泛型的設計模式吧,也確實是解決問題的一個程序寫法,方法呢也要返回構建器自身。

 1     public abstract class MvcControlBuilderBase<TMvcControl, TBuilder>
 2         where TMvcControl : MvcControlBase
 3         where TBuilder : MvcControlBuilderBase<TMvcControl, TBuilder>
 4     {
 5         /// <summary>
 6         /// 構造函數
 7         /// </summary>
 8         /// <param name="control">當前Control的實例</param>
 9         protected MvcControlBuilderBase(TMvcControl control)
10         {
11             this.Control = control;
12         }
13 
14         /// <summary>
15         /// 要生成的控件
16         /// </summary>
17         public TMvcControl Control { get; private set; }
18 
19         /// <summary>
20         /// 設置Class名稱
21         /// </summary>
22         /// <param name="className"></param>
23         /// <returns></returns>
24         public virtual TBuilder CssClass(string className)
25         {
26             this.Control.Attributes.Merge(new { @class = className });
27             return this as TBuilder;
28         }
29 
30         /// <summary>
31         /// 設置style內容
32         /// </summary>
33         /// <param name="styleValue"></param>
34         /// <returns></returns>
35         public virtual TBuilder CssStyle(string styleValue)
36         {
37             this.Control.Attributes.Merge(new { style = styleValue });
38             return this as TBuilder;
39         }
40 
41         /// <summary>
42         /// 以匿名方式設置控件html屬性
43         /// </summary>
44         /// <param name="attributes">html屬性集合</param>
45         public virtual TBuilder HtmlAttributes(IDictionary<string, object> attributes)
46         {
47             Control.Attributes.Clear();
48             Control.Attributes.Merge(attributes);
49             return this as TBuilder;
50         }
51 
52         /// <summary>
53         /// 以Html方式輸出控件代碼
54         /// </summary>
55         public virtual IHtmlString Render()
56         {
57             return Control.Render();
58         }
59 
60         /// <summary>
61         /// 重寫tostring方法,返回html代碼
62         /// </summary>
63         /// <returns></returns>
64         public override string ToString()
65         {
66             return Render().ToString();
67         }
68     }

 

我們還是以MultiSelect控件的構建器MultiSelectBuilder為例子,寫法就比較簡單,主要實現SetDataSource和SetShowItemsCount就行了。

 1     public class MultiSelectBuilder : MvcControlBuilderBase<MultiSelect, MultiSelectBuilder>
 2     {
 3         /// <summary>
 4         /// 構造函數
 5         /// </summary>
 6         /// <param name="control">控件</param>
 7         public MultiSelectBuilder(MultiSelect control)
 8             : base(control)
 9         {
10         }
11 
12         /// <summary>
13         /// 設置List,根據傳入的Dictionary
14         /// </summary>
15         /// <param name="func"></param>
16         /// <returns></returns>
17         public MultiSelectBuilder SetDataSource(Dictionary<string, string> list)
18         {
19             Control.DataSource.Merge(list);
20             return this;
21         }
22         /// <summary>
23         /// 設置顯示項目的數量
24         /// </summary>
25         /// <param name="func"></param>
26         /// <returns></returns>
27         public MultiSelectBuilder SetShowItemsCount(int iCount)
28         {
29             Control.ShowItemsCount = iCount;
30             return this;
31         }
32     }

 

控件和控件的構建器完成後,如何在cshtml中像@Html.TextBox()一樣,只要寫成@Html.MultiSelect(…).SetDataSource(…).SetShowItemsCount(5).Render()就實現在cshtml頁面中呈現多選下拉控件呢?

這裡的Html是HtmlHelper,顧名思義HtmlHelper是html的幫助類,在這裡的主要功能是協助在cshtml頁面中呈現Html控件。我們再來看一下cshtml頁面,每個cshtml最終在運行時是一個類,這個類繼承了WebViewPage類,這個類有一個屬性就是public HtmlHelper Html { get; set; }。這裡的Html就是這個屬性。

我們應該寫一個HtmlHelper的擴展方法,來實現Html.MultiSelect()的寫法。這個方法,返回值不是控件,而是控件的構建器,也就是MultiSelectBuilder。

1         public static MultiSelectBuilder MultiSelect(this HtmlHelper helper, string name, List<string> value, string labelName, object htmlAttributes = null)
2         {
3             return new MultiSelectBuilder(new MultiSelect(helper, name, value, labelName, htmlAttributes));
4         }

 

到此為止,一個基本的控件就完成,使用控件的cshtml頁面中也只需要寫上類似@Html.MultiSelect(…).SetDataSource(…).SetShowItemsCount(5).Render()的語句就搞定了。

 

面向雲的.net core開發框架

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved