讓我們來做些很Cool的事:使用Dojo做自己的.NET MVC控件


自己最近的口頭禪換成了:這將會很Cooooooooooooooool,非常Cooooooooooooooool。所以這篇博文的名字也就變得如此奇怪

打算把這個寫成一個系列,與大家分享我在工作中弄出來的一些很Cool的功能/效果/思路/等等等等

 

一直都覺得程序員交流的最好手段是通過代碼。強烈建議配合代碼閱讀本文

源代碼下載:點此下載源代碼

頁面效果如下:點擊這里下載Html(文件很小,建議大家都下載一下看看效果,第一次加載會比較慢,因為Js文件和Css文件都是從谷歌取的)

我的博客Index:Index & Writing Plan

 

首先介紹一下Dojo。Jquery想必大家已經耳熟能詳,一個輕量級的js庫,而Dojo是一個重量型的js庫。相比Jquery,Dojo在OOP上表現更好,更適合大型應用

掃盲貼:http://www.mhtml5.com/2012/06/5174.html

 

正文開始:

給出一個Model。這個Model很簡單,只有一個String,然后有一個正則的Attribute:

public class Test
{
        [RegularExpression(@"[\\w]+", ErrorMessage = "Invalid Non-Space Text.")]
        public string b { get; set; }    
}

使用過Razor的朋友,相信已經對於這種代碼已經司空見慣了:

@model Test

@Html.TextBoxFor(o=>o.b)

這會向頁面輸出如下HTML代碼:

<input data-val="true" data-val-regex="Invalid Non-Space Text." data-val-regex-pattern="[\\w]+" id="b" name="b" type="text" value="String with Space">

可以看到,.NET MVC聰明反射了屬性b上的Attribute,為我們生成了一個正則驗證。

但是這樣會有兩個問題:

1、這個正則驗證不是很好看,而且要在表單提交的時候才會觸發,而不是焦點離開就觸發

2、如果我想使用Jquery為這個TextBox添加一個onmousemove事件,要加上如下代碼:

<script>
    $(document).ready(function () {
        $("#b").mousemove(function () {
            alert(1);
        })
    })
</script>

這樣會為剛剛的TextBox添加一個mousemove事件

但是這樣寫一點都不Cool;比起使用Jquery捕獲控件,然后綁定事件,我更希望在寫控件的時候就完成JS事件綁定;換句話說,我希望如下寫法:

<script>
    function show(str) {
        alert(str);
    }
</script>

<input data-val="true" data-val-regex="Invalid Non-Space Text." data-val-regex-pattern="[\\w]+" id="b" name="b" type="text" value="" onmousemove="show(this.value);">

 

 那么我想要這么一種效果:使用這個Model

        public class Test
        {
            [Required]
            public DateTime a { get; set; }

            [RegularExpression(@"[\\w]+", ErrorMessage = "Invalid Non-Space Text.")]
            public string b { get; set; }

            [Required]
            [Range(1, 11111)]
            public int c { get; set; }

            public List<Test> d { get; set; }

            public bool e { get; set; }
        }

再在CShtml中添加如下代碼:

@model Test

<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dijit/themes/claro/claro.css" media="screen" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/dojo.js" djconfig="parseOnLoad: true"></script>

<script>
    function show(str) {
        alert(str);
    }
</script>

<body class="claro">
        @Html.MyDateTextBoxFor(o => o.a).ToHtml()
        @Html.MyTimeTextBox(o => o.a).ToHtml()
        @Html.MyTextBoxFor(o => o.b).OnBlur("show(this.value)").ToHtml()
        @Html.MyTextBoxFor(o => o.c).ToHtml()
        @Html.MyDropDownListFor(o => o.c, a => a.b, a => a.c.ToString(), Model.d).OnChange("show(this.value)").ToHtml()
        @Html.MyButton("Submit", true).ToHtml()
        @Html.MyMultiSelectFor(o => o.c, a => a.b, a => a.c.ToString(), Model.d).OnClick("show(this.value)").ToHtml()
        @Html.MyCheckBoxFor(o => o.e).ToHtml()
</body>

然后生成出來的頁面如下:點擊這里下載Html(文件很小,建議大家都下載一下看看效果)

上面鏈接第一次加載會比較慢,因為Js文件和Css文件都是從谷歌取的

為了達到這種效果,我們要重寫.NET MVC的控件代碼。

下文以DropDownList與MultiSelect為例解釋下代碼

//=======================================
//Create By Jinn 2012.12.10  只做了MyDropDownListFor與MyMultiSelectFor,不帶For的版本注釋掉了,在最下面
//Edit By  請完善這個注釋
//=======================================

using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;

namespace System.Web.Mvc
{
    public static class MySelectExtensions
    {

        public static TagBuilder MyDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression,
            Func<TModel, string> textField, Func<TModel, string> valueField,
            IList<TModel> selectList)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            string name = ExpressionHelper.GetExpressionText(expression);
            return SelectHelper<TModel, TProperty>(htmlHelper, name, textField, valueField, selectList, metadata,SelectType.DropDownList);
        }

        public static TagBuilder MyMultiSelectFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TProperty>> expression,
            Func<TModel, string> textField, Func<TModel, string> valueField,
            IList<TModel> selectList)

        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            string name = ExpressionHelper.GetExpressionText(expression);
            return SelectHelper<TModel, TProperty>(htmlHelper, name, textField, valueField, selectList, metadata, SelectType.MultiSelect);
        }

        private static TagBuilder SelectHelper<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,string name,
           Func<TModel, string> textField /*顯示*/, Func<TModel, string> valueField /*值*/,
           IList<TModel> selectList /*數據源*/, ModelMetadata metadata /*元數據*/, SelectType tpye)
        {
            string fullName = htmlHelper.ViewBag.ID;
            string ID = fullName + "_" + name;

            StringBuilder sb = new StringBuilder();
            if (selectList != null)
            {
                foreach (var item in selectList)
                {
                    TagBuilder tag = new TagBuilder("option");
                    tag.MergeAttribute("value", valueField(item));
                    string selectedValue = valueField(item);
                    if (selectedValue == Convert.ToString(metadata.Model))
                    {
                        tag.MergeAttribute("selected", "selected");
                    }
                    tag.SetInnerText(textField(item));
                    sb.AppendLine(tag.ToString(TagRenderMode.Normal));
                }
            }

            TagBuilder tagBuilder = new TagBuilder("select")
            {
                InnerHtml = sb.ToString()
            };
            switch (tpye)
            {
                case SelectType.DropDownList:
                    tagBuilder.MergeAttribute("data-dojo-type", "dijit/form/FilteringSelect");
                    break;
                case SelectType.MultiSelect:
                    tagBuilder.MergeAttribute("data-dojo-type", "dijit/form/MultiSelect");           
                    break;
                default:
                    tagBuilder.MergeAttribute("data-dojo-type", "dijit/form/FilteringSelect");
                    break;
            }
            tagBuilder.MergeAttribute("name", name, true);
            tagBuilder.GenerateId(ID);
            return tagBuilder;
        }

        public enum SelectType
        {
            DropDownList = 1,
            MultiSelect = 2,
        }
    }
}

在.NET MVC中,控件返回類型都是MvcHtmlString,但是這樣就不方便添加JS事件了。所以我這里把所有返回類型改為TagBuilder,在所有事件添加完成之后,再通過ToHtml方法轉換成MvcHtmlString

添加JS事件的代碼如下:

//=======================================
//Create By Jinn 2012.12.5  最基礎的版本,完成了OnClick、OnBlur等5個常用事件;
//                          我知道其實這5個都可以合起來寫,但是為了可拓展性與頁面代碼量盡可能少的原則,我選擇了這種寫法
//Edit By  請完善這個注釋
//=======================================

namespace System.Web.Mvc
{
    public static class Function
    {
        public static TagBuilder OnClick(this TagBuilder tagBuilder, string functionName)
        {
            tagBuilder.MergeAttribute("onclick", functionName);
            return tagBuilder;
        }

        public static TagBuilder OnMouseMove(this TagBuilder tagBuilder, string functionName)
        {
            tagBuilder.MergeAttribute("onmousemove", functionName);
            return tagBuilder;
        }

        public static TagBuilder OnFocus(this TagBuilder tagBuilder, string functionName)
        {
            tagBuilder.MergeAttribute("onfocus", functionName);
            return tagBuilder;
        }

        public static TagBuilder OnChange(this TagBuilder tagBuilder, string functionName)
        {
            tagBuilder.MergeAttribute("onchange", functionName);
            return tagBuilder;
        }

        public static TagBuilder OnBlur(this TagBuilder tagBuilder, string functionName)
        {
            tagBuilder.MergeAttribute("onblur", functionName);
            return tagBuilder;
        }

        //自閉和與默認閉和的方式都能解析
        //如<asd/>與<asd></asd>是一樣的
        //所以這里默認閉合的形式
        //By Jinn 2012.12.8
        /// <summary>
        /// 默認閉和的方式
        /// </summary>
        public static MvcHtmlString ToHtml(this TagBuilder tagBuilder)
        {
            return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal));
        }
    }
}

 

這樣寫完,在CShtml中添加

@Html.MyDropDownListFor(o => o.c, a => a.b, a => a.c.ToString(), Model.d).OnChange("show(this.value)").ToHtml()

就可以生成一個很Coooooooooooool的DropDownList控件了

 

就此擱筆

 

PS:為了偷懶,我的控件的NameSpace都是System.Web.Mvc

再PS:博客園隨筆的代碼着色功能貌似掛了,按一下Backspace會去掉文章中所有的顏色


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM