• 实现ASP.NET MVC3 HtmlHelper 的 RadioButtonList 与CheckBoxList 扩展


    ASP.NET MVC3也出来有一段时间了,对于没有RadioButtonList 与CheckBoxList的问题,网上也已经有很多解决方案了,可以for循环拼接出来,也可以引用ASP.NET MVC Toolkit,等等方法。其实本没有必要写出来的,不过看了WebGird中队format的实现方式,一时来了兴趣,就尝试这实现了一下,发现还是有不少机关的,于是就拿出来和大家分享一下。

    首先清楚下要实现什么,For<TModel, TProperty> 这样的重载时必须的了,然后还要实现WebGrid的format类似的支持,可以自定义输出格式。当然输出正确的列表HTML是必须的,不过这个不难。

    那么就先实现一个最基本的核心函数

    using System;
    
    using System.Text;
    
    using System.Collections;
    
    using System.Collections.Generic;
    
    using System.Linq;
    
    using System.Linq.Expressions;
    
    using System.Web;
    
    using System.Web.Mvc;
    
    namespace SymApplauseAward.MvcExtention
    
    {
    
        public static class CheckBoxListExtension
    
        {  
    
          public static MvcHtmlString InputListInternal(
    
                this HtmlHelper html,
    
                string name,
    
                IEnumerable<SelectListItem> selectList,
    
                bool allowMultiple,     
    
               )
    
            {
    
                string fullHtmlFieldName = 
    
                          html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    
                if (string.IsNullOrEmpty(fullHtmlFieldName))
    
                {
    
                    throw new ArgumentException("filed can't be null or empty !", 
    
                                                "name");
    
                }
    
                StringBuilder strBuilder = new StringBuilder();
    
                TagBuilder tagBuilder = new TagBuilder("input");
    
                foreach (var item in selectList)
    
                {   //Clear first
    
                    tagBuilder.InnerHtml = string.Empty;
    
                    if (allowMultiple)
    
                    {
    
                        tagBuilder.MergeAttribute("type", "checkbox", true);
    
                    }
    
                    else
    
                    {
    
                        tagBuilder.MergeAttribute("type", "radio", true);
    
                    }
    
                    tagBuilder.MergeAttribute("value", item.Value, true);
    
                    if (item.Selected)
    
                        tagBuilder.MergeAttribute("selected", "selected", true);
    
                 
    
                    tagBuilder.MergeAttribute("name", fullHtmlFieldName, true);
    
                    var s = tagBuilder.ToString()+item.Text+"<br/>"
    
                    strBuilder.Append(s);
    
                }
    
                return new MvcHtmlString(strBuilder.ToString());
    
            }
    
    }

    这里通过TagBuilder创建Html,虽然也可以直接拼字符串,不过既然微软提供了相应的类,就直接用吧。 并且通过html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);获得了合法的name值,这步很重要,它会把“.”转化为“_”从而使name合法。

    好了,基本功能实现了,如果用@Html.InputListInternal去调用,就可以显示相应的List了。

    不过,这样还不太灵活,我们无法自定义格式,那么下面就实现自定义格式的功能。查看一下WebGrid的文档,原来format参数的类型是Func<dynamic,object>类型,那简单我们加一个好了。加完后代码如下:

     public static MvcHtmlString InputListInternal(
    
                this HtmlHelper html,
    
                string name,
    
                IEnumerable<SelectListItem> selectList,
    
                bool allowMultiple,
    
                Func<dynamic, object> format
    
               )
    
            {
    
               //...
    
                if (format == null)
    
                    format = i => i.Button + i.Text + "<br/>";
    
                StringBuilder strBuilder = new StringBuilder();
    
                TagBuilder tagBuilder = new TagBuilder("input");
    
                foreach (var item in selectList)
    
                {   
    
                    //...
    
                    var inputItem = new { Button = tagBuilder.ToString(),Text = item.Text };
    
                    var s = format(inputItem).ToString();
    
                    strBuilder.Append(s);
    
                }
    
                return new MvcHtmlString(strBuilder.ToString());
    
    }

    赶快用一下@Html.InputListInternal(“Score”,scores,false,item=>item.Button+”>”+item.Text),事与愿违,报错了“object 不包含Button的定义”。奇怪了,明明传入的是new {Button=tagBuilder.ToString(), Text=item.Text},有Button属性啊!

    于是开始查技术文档,原来是dynamic的循环不会涉及扩展方法,换句话说,声明的匿名对象对于dynamic来说是不知道的,于是就强制转换为Object了,Object当然没有Button属性,于是报错了。对于这种问题,有两种解决办法,方法一:不用扩展方法的调用方式,而是回归静态类静态方法调用的方式。方法二:在dynamic的上下文中引用类型。第一种方法不说了,要那样就不做这个扩展了,于是尝试第二种解决办法。

    由于是在页面定义的Lamda表达式,因此Context是Page。想想的话,要用这个扩展必然要引用这个命名空间,那么只要传回的类型是这个空间的public类,那么dynamic就能找到相应的类型了。于是定义了一个新类InputListItem,将代码稍作改动:

       var btnHtmlString = new MvcHtmlString(tagBuilder.ToString());
    
       var inputItem = new InputListItem { Button = btnHtmlString, Text = item.Text };
    
       var s = format(inputItem).ToString();
    
       strBuilder.Append(s);

    再试下,OK,显示正常了,而且也是自定义的格式,至此format事情是搞定了。

    最后实现一下For<TModel, TProperty>的重载,这个比较简单,就是传入一个Expression<Func<TModel, TProperty>>的参数,这里多提示一下可以用ExpressionHelper.GetExpressionText来直接提取参数的内容。至于匿名类型的htmlAttributes的处理也可以用HtmlHelper.AnonymousObjectToHtmlAttributes简单完成。在经过进一步封装后,最终代码如下:

    using System;
    
    using System.Text;
    
    using System.Collections;
    
    using System.Collections.Generic;
    
    using System.Linq;
    
    using System.Linq.Expressions;
    
    using System.Web;
    
    using System.Web.Mvc;
    
    namespace Pride_Zhou.MvcExtention
    
    {
    
        public class InputListItem
    
        {
    
            public MvcHtmlString Button { get; set; }
    
            public string Text { get; set; }
    
        }
    
        public static class CheckBoxListExtension
    
        {
    
            #region CheckBoxList
    
            public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(
    
                this HtmlHelper<TModel> html,
    
                Expression<Func<TModel, TProperty>> expression,
    
                IEnumerable<SelectListItem> selectList,
    
                Func<dynamic, object> format,
    
                object htmlAttributes)
    
            {
    
                return CheckBoxListFor(html, expression, selectList, format, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    
            }
    
            public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(
    
                this HtmlHelper<TModel> html,
    
                Expression<Func<TModel, TProperty>> expression,
    
                IEnumerable<SelectListItem> selectList,
    
                Func<dynamic, object> format = null,
    
                IDictionary<string, object> htmlAttributes = null)
    
            {
    
                return CheckBoxList(html, GetName(expression), selectList, format, htmlAttributes);
    
            }
    
            public static MvcHtmlString CheckBoxList(
    
              this HtmlHelper html,
    
              string name,
    
              IEnumerable<SelectListItem> selectList,
    
              Func<dynamic, object> format,
    
              object htmlAttributes)
    
            {
    
                return CheckBoxList(html, name, selectList, format, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    
            }
    
            public static MvcHtmlString CheckBoxList(
    
               this HtmlHelper html,
    
               string name,
    
               IEnumerable<SelectListItem> selectList,
    
               Func<dynamic, object> format = null,
    
               IDictionary<string, object> htmlAttributes = null)
    
            {
    
                return InputListInternal(html, name, selectList, true, format, htmlAttributes);
    
            }
    
            #endregion
    
            #region RadioButtonList
    
            public static MvcHtmlString RadioButtonList(
    
             this HtmlHelper html,
    
             string name,
    
             IEnumerable<SelectListItem> selectList,
    
             Func<dynamic, object> format,
    
             object htmlAttributes)
    
            {
    
                return RadioButtonList(html, name, selectList, format, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    
            }
    
            public static MvcHtmlString RadioButtonList(
    
             this HtmlHelper html,
    
             string name,
    
             IEnumerable<SelectListItem> selectList,
    
             Func<dynamic, object> format = null,
    
             IDictionary<string, object> htmlAttributes = null)
    
            {
    
                return InputListInternal(html, name, selectList, false, format, htmlAttributes);
    
            }
    
            public static MvcHtmlString RadioButtonListFor<TModel, TProperty>(
    
              this HtmlHelper<TModel> html,
    
              Expression<Func<TModel, TProperty>> expression,
    
              IEnumerable<SelectListItem> selectList,
    
              Func<dynamic, object> format,
    
              object htmlAttributes)
    
            {
    
                return RadioButtonList(html, GetName(expression), selectList, format, htmlAttributes);
    
            }
    
            public static MvcHtmlString RadioButtonListFor<TModel, TProperty>(
    
                this HtmlHelper<TModel> html,
    
                Expression<Func<TModel, TProperty>> expression,
    
                IEnumerable<SelectListItem> selectList,
    
                Func<dynamic, object> format = null,
    
                IDictionary<string, object> htmlAttributes = null)
    
            {
    
                return RadioButtonList(html, GetName(expression), selectList, format, htmlAttributes);
    
            }
    
            #endregion
    
            /*-------------------------------------
    
             * Core Function
    
             --------------------------------------*/
    
            private static MvcHtmlString InputListInternal(
    
                this HtmlHelper html,
    
                string name,
    
                IEnumerable<SelectListItem> selectList,
    
                bool allowMultiple,
    
                Func<dynamic, object> format,
    
                IDictionary<string, object> htmlAttributes
    
               )
    
            {
    
                string fullHtmlFieldName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    
                if (string.IsNullOrEmpty(fullHtmlFieldName))
    
                {
    
                    throw new ArgumentException("filed can't be null or empty !", "name");
    
                }
    
                if (format == null)
    
                    format = i => i.Button + i.Text + "<br/>";
    
                StringBuilder strBuilder = new StringBuilder();
    
                TagBuilder tagBuilder = new TagBuilder("input");
    
                foreach (var item in selectList)
    
                {   //Clear first
    
                    tagBuilder.InnerHtml = string.Empty;
    
                    if (allowMultiple)
    
                    {
    
                        tagBuilder.MergeAttribute("type", "checkbox", true);
    
                    }
    
                    else
    
                    {
    
                        tagBuilder.MergeAttribute("type", "radio", true);
    
                    }
    
                    tagBuilder.MergeAttribute("value", item.Value, true);
    
                    if (item.Selected)
    
                        tagBuilder.MergeAttribute("selected", "selected", true);
    
                    tagBuilder.MergeAttributes<string, object>(htmlAttributes);
    
                    tagBuilder.MergeAttribute("name", fullHtmlFieldName, true);
    
                    var btnHtmlString = new MvcHtmlString(tagBuilder.ToString());
    
                    var inputItem = new InputListItem { Button = btnHtmlString, Text = item.Text };
    
                    var s = format(inputItem).ToString();
    
                    strBuilder.Append(s);
    
                }
    
                return new MvcHtmlString(strBuilder.ToString());
    
            }
    
            private static string GetName(LambdaExpression expression)
    
            {
    
                if (expression == null)
    
                {
    
                    throw new ArgumentNullException("expression");
    
                }
    
                return ExpressionHelper.GetExpressionText(expression);
    
            }
    
        }
    
    }

    好了,现在可以像WebGrid一样生成批量Html了,例如:

       <table style="border:0 solid #000;">
    
            <tbody>
    
            @Html.RadioButtonList(String.Format("{0}{1}", "S_", gId),
    
                                  Model.GetQuestions(gId).ToList().Select(q => new SelectListItem { Value = q.Id.ToString(), Text = q.Content }),
    
                                 @<tr style="border:0 solid #000;">
    
                                   <td style="2em;border:0 solid #000;">@item.Button</td>
    
                                   <td style="border:0 solid #000;">@item.Text</td>
    
                                  </tr>, new { @class = "score"})
    
            </tbody>
    
       </table>

    怎样,和WebGird一样吧,如果觉得item参数过少,扩展InputListItem类就可以了。 总体来说实现不难,就是Dynamic比较坑爹,这里很多代码是通过查看MVC源码的实现来写的,当然检查还不够完美,如果有兴趣可以仿照微软,加上相应的代码,这样用起来也更稳定。


  • 相关阅读:
    xcode 定义自己的代码片段
    iOS 开发技巧
    iOS 上传自己的库到cocoapod
    制作正式版10.11 OS X El Capitan 安装U盘(优盘)
    查看 共享内存 的命令 ipcrm、ipcs
    批量kill 进程
    GROUP BY、HAVING、AS 的用法小例子
    C++多线程中调用python api函数
    C++ 查询某个变量的类型
    C++ 把枚举变量的名称,直接当字符串使用方法 字符串化符号 #
  • 原文地址:https://www.cnblogs.com/ebread/p/2104621.html
Copyright © 2020-2023  润新知