最近在使用MVC进行开发时,使用进行客户端的输入验证,加上使用MVC3的新视图引擎感觉还是挺方便的,不用自己去写很多js了,并且效果也能让人接受
可是遇上要向外输出比如一个CheckBox列表时就纠结了,验证代码还得自己去写,这样就造成了客户端采用了两套验证,感觉不统一也不优雅,于是就琢磨了一下,便有了如下实现方式。
HtmlHelper的扩展类主要包括CheckBoxList,CheckBoxListFor等方法,有了这些方法,你可以这样生成checkBox 列表
@Html.CheckBoxListFor(m=>m.RoleList,"li")
下来就是扩展的全部代码
HtmlHelperExtention.cs
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
namespace Newborn.BSCommon
{
/// <summary>
/// HtmlHelper扩展
/// </summary>
public static class HtmlHelperExtention
{
public static MvcHtmlString InputList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName, InputType inputType)
{
return InputList(helper, null, selectList, checkBoxName,splitTagName, inputType);
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="checkBoxName">name属性</param>
/// <param name="splitTagName">每个SelectList外层</param>
/// <param name="inputType">inputType</param>
/// <param name="selectList">selectList</param>
/// <param name="metadata">metadata</param>
/// <returns>MvcHtmlString</returns>
public static MvcHtmlString InputList(this HtmlHelper helper,ModelMetadata metadata, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName, InputType inputType)
{
if (helper == null) throw new ArgumentNullException("helper");
if (selectList == null) throw new ArgumentNullException("selectList");
if (string.IsNullOrEmpty(checkBoxName)) throw new ArgumentNullException("checkBoxName");
StringBuilder sb = new StringBuilder();
int idIndex = 0;
TagBuilder tagBuilder = new TagBuilder("span");
foreach (SelectListItem item in selectList)
{
TagBuilder splitTagBuilder = null;
if (!string.IsNullOrEmpty(splitTagName))
splitTagBuilder = new TagBuilder(splitTagName);
TagBuilder checkTagBuilder = new TagBuilder("input");
checkTagBuilder.Attributes["type"] = inputType.ToString();
checkTagBuilder.Attributes["name"] = checkBoxName;
checkTagBuilder.Attributes["value"] = item.Value;
string checkBoxId = checkBoxName + "_id_" + idIndex;
checkTagBuilder.Attributes["id"] = checkBoxId;
if (item.Selected)
checkTagBuilder.Attributes["checked"] = "checked";
TagBuilder labelTagBuilder = new TagBuilder("label") { InnerHtml = helper.Encode(item.Text) };
labelTagBuilder.Attributes["for"] = checkBoxId;
string checkHtml = checkTagBuilder.ToString() + labelTagBuilder.ToString();
if (splitTagBuilder != null)
{
splitTagBuilder.InnerHtml += checkHtml;
sb.AppendLine(splitTagBuilder.ToString());
}
else
sb.AppendLine(checkHtml);
idIndex++;
}
TagBuilder hiddenTagBuilder = new TagBuilder("input");
hiddenTagBuilder.Attributes["type"] = "hidden";
hiddenTagBuilder.MergeAttribute("name", checkBoxName);
hiddenTagBuilder.MergeAttribute("id", "hidden"+ checkBoxName);
hiddenTagBuilder.MergeAttributes<string, object>(helper.GetUnobtrusiveValidationAttributes(checkBoxName, metadata));
tagBuilder.InnerHtml = hiddenTagBuilder + sb.ToString();
return MvcHtmlString.Create(tagBuilder.ToString());
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="selectList"></param>
/// <param name="checkBoxName">name属性</param>
/// <returns>MvcHtmlString</returns>
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName)
{
return helper.InputList(selectList, checkBoxName, null, InputType.CheckBox);
}
public static MvcHtmlString InputListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,string splitTag,InputType inputType)
{
ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
List<SelectListItem> list = ((List<SelectListItem>)modelMetadata.Model);
return htmlHelper.InputList(modelMetadata, list, modelMetadata.PropertyName, splitTag, inputType);
}
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.InputListFor(expression, "span",InputType.CheckBox);
}
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,string splitTag)
{
return htmlHelper.InputListFor(expression, splitTag, InputType.CheckBox);
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <param name="splitTagName">每项分隔符的Tag名称</param>
/// <returns></returns>
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName)
{
return InputList(helper, selectList, checkBoxName, splitTagName, InputType.CheckBox);
}
public static MvcHtmlString RadioBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.InputListFor(expression, "span", InputType.Radio);
}
/// <summary>
/// 生成RadioButton列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <param name="splitTag"></param>
/// <returns></returns>
public static MvcHtmlString RadioButtonList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTag)
{
return InputList(helper, selectList, checkBoxName, splitTag, InputType.Radio);
}
/// <summary>
/// 生成RadioButton列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <returns></returns>
public static MvcHtmlString RadioButtonList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName)
{
return InputList(helper, selectList, checkBoxName, null, InputType.Radio);
}
}
}
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
namespace Newborn.BSCommon
{
/// <summary>
/// HtmlHelper扩展
/// </summary>
public static class HtmlHelperExtention
{
public static MvcHtmlString InputList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName, InputType inputType)
{
return InputList(helper, null, selectList, checkBoxName,splitTagName, inputType);
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="checkBoxName">name属性</param>
/// <param name="splitTagName">每个SelectList外层</param>
/// <param name="inputType">inputType</param>
/// <param name="selectList">selectList</param>
/// <param name="metadata">metadata</param>
/// <returns>MvcHtmlString</returns>
public static MvcHtmlString InputList(this HtmlHelper helper,ModelMetadata metadata, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName, InputType inputType)
{
if (helper == null) throw new ArgumentNullException("helper");
if (selectList == null) throw new ArgumentNullException("selectList");
if (string.IsNullOrEmpty(checkBoxName)) throw new ArgumentNullException("checkBoxName");
StringBuilder sb = new StringBuilder();
int idIndex = 0;
TagBuilder tagBuilder = new TagBuilder("span");
foreach (SelectListItem item in selectList)
{
TagBuilder splitTagBuilder = null;
if (!string.IsNullOrEmpty(splitTagName))
splitTagBuilder = new TagBuilder(splitTagName);
TagBuilder checkTagBuilder = new TagBuilder("input");
checkTagBuilder.Attributes["type"] = inputType.ToString();
checkTagBuilder.Attributes["name"] = checkBoxName;
checkTagBuilder.Attributes["value"] = item.Value;
string checkBoxId = checkBoxName + "_id_" + idIndex;
checkTagBuilder.Attributes["id"] = checkBoxId;
if (item.Selected)
checkTagBuilder.Attributes["checked"] = "checked";
TagBuilder labelTagBuilder = new TagBuilder("label") { InnerHtml = helper.Encode(item.Text) };
labelTagBuilder.Attributes["for"] = checkBoxId;
string checkHtml = checkTagBuilder.ToString() + labelTagBuilder.ToString();
if (splitTagBuilder != null)
{
splitTagBuilder.InnerHtml += checkHtml;
sb.AppendLine(splitTagBuilder.ToString());
}
else
sb.AppendLine(checkHtml);
idIndex++;
}
TagBuilder hiddenTagBuilder = new TagBuilder("input");
hiddenTagBuilder.Attributes["type"] = "hidden";
hiddenTagBuilder.MergeAttribute("name", checkBoxName);
hiddenTagBuilder.MergeAttribute("id", "hidden"+ checkBoxName);
hiddenTagBuilder.MergeAttributes<string, object>(helper.GetUnobtrusiveValidationAttributes(checkBoxName, metadata));
tagBuilder.InnerHtml = hiddenTagBuilder + sb.ToString();
return MvcHtmlString.Create(tagBuilder.ToString());
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper">HtmlHelper</param>
/// <param name="selectList"></param>
/// <param name="checkBoxName">name属性</param>
/// <returns>MvcHtmlString</returns>
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName)
{
return helper.InputList(selectList, checkBoxName, null, InputType.CheckBox);
}
public static MvcHtmlString InputListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,string splitTag,InputType inputType)
{
ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
List<SelectListItem> list = ((List<SelectListItem>)modelMetadata.Model);
return htmlHelper.InputList(modelMetadata, list, modelMetadata.PropertyName, splitTag, inputType);
}
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.InputListFor(expression, "span",InputType.CheckBox);
}
public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression,string splitTag)
{
return htmlHelper.InputListFor(expression, splitTag, InputType.CheckBox);
}
/// <summary>
/// 生成CheckBox列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <param name="splitTagName">每项分隔符的Tag名称</param>
/// <returns></returns>
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTagName)
{
return InputList(helper, selectList, checkBoxName, splitTagName, InputType.CheckBox);
}
public static MvcHtmlString RadioBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.InputListFor(expression, "span", InputType.Radio);
}
/// <summary>
/// 生成RadioButton列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <param name="splitTag"></param>
/// <returns></returns>
public static MvcHtmlString RadioButtonList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName, string splitTag)
{
return InputList(helper, selectList, checkBoxName, splitTag, InputType.Radio);
}
/// <summary>
/// 生成RadioButton列表
/// </summary>
/// <param name="helper"></param>
/// <param name="selectList"></param>
/// <param name="checkBoxName"></param>
/// <returns></returns>
public static MvcHtmlString RadioButtonList(this HtmlHelper helper, IEnumerable<SelectListItem> selectList, string checkBoxName)
{
return InputList(helper, selectList, checkBoxName, null, InputType.Radio);
}
}
}
由于使用到客户端验证所以要引用如下的js文件。
脚本部分
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/Custom/jquery.validate.unobtrusive.extension.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
new checkBoxList("RoleList", "hiddenRoleList");//初始验证
});
</script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/Custom/jquery.validate.unobtrusive.extension.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
new checkBoxList("RoleList", "hiddenRoleList");//初始验证
});
</script>
页面上还要写这个一句
$(function () {
new checkBoxList("RoleList", "hiddenRoleList");//初始验证
new checkBoxList("RoleList", "hiddenRoleList");//初始验证
});
是为了让脚本去绑定事件,可能还会有更好的方式,这里有待研究。
页面上代码如下:
HTML部分<div class="select">
@Html.CheckBoxListFor(m=>m.RoleList,"li")
@Html.ValidationMessageFor(m => m.RoleList)
</div>
@Html.CheckBoxListFor(m=>m.RoleList,"li")
@Html.ValidationMessageFor(m => m.RoleList)
</div>
ListSlectRangeAttribute.cs 文件
View Code
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Newborn.BSCommon
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class ListSlectRangeAttribute : ValidationAttribute, IClientValidatable
{
private const string errFormat = "该项最少选择项为{0}最多选择项为{1}";
public ListSlectRangeAttribute()
{
MinSelected = 0;
MaxSelected = -1;
}
public int MinSelected { get; set; }
public int MaxSelected { get; set; }
public override bool IsValid(object value)
{
return true;
}
public override string FormatErrorMessage(string name)
{
return string.Format(errFormat, MinSelected > 0 ? MinSelected.ToString() : "不限", MaxSelected > 0 ? MaxSelected.ToString() : "不限");
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule
{
ValidationType = "list",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters["min"] = MinSelected;
rule.ValidationParameters["max"] = MaxSelected;
yield return rule;
}
}
}
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Newborn.BSCommon
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class ListSlectRangeAttribute : ValidationAttribute, IClientValidatable
{
private const string errFormat = "该项最少选择项为{0}最多选择项为{1}";
public ListSlectRangeAttribute()
{
MinSelected = 0;
MaxSelected = -1;
}
public int MinSelected { get; set; }
public int MaxSelected { get; set; }
public override bool IsValid(object value)
{
return true;
}
public override string FormatErrorMessage(string name)
{
return string.Format(errFormat, MinSelected > 0 ? MinSelected.ToString() : "不限", MaxSelected > 0 ? MaxSelected.ToString() : "不限");
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule rule = new ModelClientValidationRule
{
ValidationType = "list",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters["min"] = MinSelected;
rule.ValidationParameters["max"] = MaxSelected;
yield return rule;
}
}
}
jquery.validate.unobtrusive.extension.js文件主要完成客户端如checkbox列表的验证,细心的朋友会发现因为checkbox列表是多个input所以我没有基于它来做,而是采用一个隐藏域还记录现在选择的项数目(目前只记录选择了多少项,没记录选择的项值之类的,不过目前暂无此类需求,如果有扩展起来也行方便)。
//by xianhong
//添加验证如验证框必须选择一定数量的验证
$.validator.addMethod("maxminselected", function (value, element, param) {
var min = param[0];
var max = param[1];
if (value >= min && (max <= 0 || value <= max))
return true;
return false;
});
$.validator.unobtrusive.adapters.addMinMax("list", "min", "max", "maxminselected");
var checkBoxList = function (name/*input name属性*/, hiddenId/*记录选择的隐藏域*/) {
this.checkedCount = function () {
var selected = $("input[name='" + name + "']:checked");
return selected.length;
};
this.All = function () {
return $("input[name='" + name + "']");
};
this.BindClick = function () {
var thisobj = this;
this.All().click(function () {
$("#" + hiddenId).val(thisobj.checkedCount());
});
};
this.BindClick();
};
//添加验证如验证框必须选择一定数量的验证
$.validator.addMethod("maxminselected", function (value, element, param) {
var min = param[0];
var max = param[1];
if (value >= min && (max <= 0 || value <= max))
return true;
return false;
});
$.validator.unobtrusive.adapters.addMinMax("list", "min", "max", "maxminselected");
var checkBoxList = function (name/*input name属性*/, hiddenId/*记录选择的隐藏域*/) {
this.checkedCount = function () {
var selected = $("input[name='" + name + "']:checked");
return selected.length;
};
this.All = function () {
return $("input[name='" + name + "']");
};
this.BindClick = function () {
var thisobj = this;
this.All().click(function () {
$("#" + hiddenId).val(thisobj.checkedCount());
});
};
this.BindClick();
};