原文:https://blog.csdn.net/a497785609/article/details/50184779
在ASP.NET MVC中,Html辅助方法给我们程序员带来很多方便,其重要性也就不言自明。有时候,我们不想重复地写一些HTML代码,或者MS没有提供我们想要的那个HTML标签的Html辅助方法,那么,我们就可以通过自己定义一个Html扩展方法来达到这个目的。
比如,到目前为止,Html扩展方法中没有关于<input type="file" />这类标签的辅助方法,那么我们就可以自已实现一个。本文以实现<input type="file" />标签为例,演示如何实现自定义Html扩展方法。
一、实现自定义弱类型Html扩展方法
其实实现自定义Html扩展方法并不难,有兴趣的同学可以去看下MVC源代码,关于Html扩展方法部分。要用到System.Web.Mvc命名空间下的TagBuilder类,MvcHtmlString类。TagBuilder类为Html辅助方法生成HTML标签,MvcHtmlString代表HTML编码的字符串。扩展方法代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public static class MyInputExtensions { public static MvcHtmlString Input( this HtmlHelper htmlHelper, string name) { TagBuilder tagBuilder = new TagBuilder( "input" ); //设置标签类型为input tagBuilder.Attributes.Add( "type" , "file" ); //为标签添加type属性及值 tagBuilder.Attributes.Add( "name" , name); //为标签添加name属性及值 tagBuilder.GenerateId(name); //为标签生成Id,name参数代码Id的值 //创建经过HTML加密的字符串 //TagRenderMode.SelfClosing枚举值代表当前标签是自动关闭的 return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing)); } } |
需要特别提醒的是,扩展方法类所在的命名空间最好设置为System.Web.Mvc,这样,我们在View中可以通过智能感知轻易找到,也不容易出错或者无法通过VS智能感知找到我们自定义的Html辅助方法,可以为我们省去很多不必要的麻烦。将上面代码编译,我们即可在View中通过智能感知看到我们自定义的Html辅助方法。如下图所示:
使用方法和其它Html辅助方法一样,如下代码所示:
<%: Html.Input("Path") %> //字符串参数Path代表生成标签的name属性和id属性的值
需要说明的是,本例所示是为了生成<input type="file">标签,是不用设置值的,读者可以通过自身情况定义扩展方法。然后运行,通过浏览器查看HTML源代码,如下图所示:
二、实现自定义强类型Html辅助方法
强类型辅助方法的一个好处是,我们可以通过编译器为我们检测一些错误,为我们节省一些排错的时间与精力。所以,强类型Html辅助方法是不可缺少的。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public static class MyInputExtensions { public static MvcHtmlString Input<TModel, TValue>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression) { string modelName = ExpressionHelper.GetExpressionText(expression); //从Lambda表达式中获取模型对应属性的名称 TagBuilder tagBuilder = new TagBuilder( "input" ); //设置标签类型为input tagBuilder.Attributes.Add( "type" , "file" ); //为标签添加type属性及值 tagBuilder.Attributes.Add( "name" , modelName); //为标签添加name属性及值 tagBuilder.GenerateId(modelName); //为标签生成Id,name参数代码Id的值 //创建经过HTML加密的字符串 //TagRenderMode.SelfClosing枚举值代表当前标签是自动关闭的 return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing)); } } |
然后我们编译,在View中,我们就可以通过智能感知看到我们新扩展的强类型Html辅助方法了。如下图所示:
我们可以通过如下代码使用新扩展的Html辅助方法:
<%: Html.Input(model => model.Path) %>//Path代表model的Path属性,生成标签的name和id的属性值均会是Path
运行,我们通过浏览器查看生成的Html源代码如下图所示:
三、为标签错误输入添加CSS支持
对于要求输入的标签,如Text,如果用户输入错误内容,我们可以为当前标签添加CSS错误提示,为用户提供一个更加友好、人性化的界面。代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ModelState modelState; if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState)) { if (modelState.Errors.Count > 0) { //添加错误提示CSS tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); } } |
将以上代码复制到我们自定义的扩展方法的返回MvcHtmlString字符串之前即可。
四、总结
本文通过演示如果实现自定义<input type="file" />标签的Html辅助方法,展示了如何在ASP.NET MVC中实现自定义Html辅助方法。对于ASP.NET MVC程序员来说,这是非常实用的。
五、补充
一个完整的扩展示例方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public static MvcHtmlString InputText<TModel, TValue>( this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, IDictionary< string , object > htmlAttributes) { TagBuilder tagBuilder = new TagBuilder( "input" ); var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); //第1步:获取模型字段的显示文本 string text = metadata.DisplayName ?? metadata.PropertyName; //第2步:获取模型字段的字段名 var name = ExpressionHelper.GetExpressionText(expression); //第3步:获取模型字段的值 #region 模型字段的值 string value; ModelState modelState; string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name); if (html.ViewData.ModelState.TryGetValue(fullName, out modelState) && modelState.Errors.Count > 0) { tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName); } if (modelState != null && modelState.Value != null ) { value = modelState.Value.AttemptedValue; } else if (metadata.Model != null ) { value = metadata.Model.ToString(); } else { value = String.Empty; } #endregion tagBuilder.Attributes.Add( "type" , "text" ); tagBuilder.Attributes.Add( "name" , name); tagBuilder.Attributes.Add( "value" , value); //第4步:填充其他自定义属性 tagBuilder.MergeAttributes(htmlAttributes, true ); tagBuilder.GenerateId(name); return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing)); } |
另外一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | public static class FileUploadExtension { private const string CanReadExts = ".doc;.docx;.rtf;.xml;.html;.htm;.odt;.xls;.xlsx;.ppt;.pptx;.pdf;.txt;.mpp" ; private const string ImgExts = ".jpg;.jpeg;.png;.bmp;.gif" ; private const string CanEditExts = ".doc;.docx" ; /// <summary> /// 上传文件扩展,绑定到模型字段 /// </summary> /// <typeparam name="TModel">模型</typeparam> /// <typeparam name="TProperty">模型对应属性</typeparam> /// <param name="helper"></param> /// <param name="pathsExpr">保存上传文件路径的属性表达式</param> /// <param name="multi">是否多文件上传(false时为单文件)</param> /// <param name="fileTypeExts">上传文件扩展名,用“分号”分割不同扩展名(如: '.jpg','.png')</param> /// <param name="htmlAttributes">html属性集合对象</param> /// <param name="editMode">是否为编辑模式</param> /// <param name="onlineView">是否可以在线阅读</param> /// <param name="onlineEdit">是否可以在线编辑</param> /// <param name="callBack">上传成功的回掉函数</param> /// <returns></returns> public static MvcHtmlString FileUploadFor<TModel, TProperty> ( this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> pathsExpr, bool multi = true , string fileTypeExts = null , object htmlAttributes = null , bool editMode = true , bool onlineView = true , bool onlineEdit = false , string callBack = "" ) { //检查model是否存在 if (helper.ViewData != null && null == helper.ViewData.Model) throw new NoNullAllowedException( "View Model 不能为空! 请确保 View Model 被创建了并且传输到当前视图." ); //从model中获得数据 if (helper.ViewData != null ) { var model = helper.ViewData.Model; //检查是否绑定到了model的属性上 var name = ExpressionHelper.GetExpressionText(pathsExpr); if (String.IsNullOrEmpty(name)) throw new ArgumentException( "名称不能为空!" , "pathsExpr" ); //获得已经上传文件名称列表 var pathsFunc = pathsExpr.Compile(); var nameStr = "" ; if (pathsFunc(model) != null ) { nameStr = pathsFunc(model).ToString(); } var metadata = ModelMetadata.FromLambdaExpression(pathsExpr, helper.ViewData); var isRequired = metadata.IsRequired; var displayName = isRequired ? metadata.DisplayName : "" ; return FileUpload(helper, name, nameStr, multi, fileTypeExts, htmlAttributes, editMode, onlineView, onlineEdit, isRequired, displayName, callBack); } else { return new MvcHtmlString( "" ); } } /// <summary> /// 上传文件,指定name和value不需要绑定到字段 /// </summary> /// <param name="helper">上下文</param> /// <param name="name">后台接收时使用的名称</param> /// <param name="value">值,上传文件的标识串</param> /// <param name="multi">是否可以上传多个</param> /// <param name="fileTypeExts">文件类型扩展名列表</param> /// <param name="htmlAttributes">附加的html属性</param> /// <param name="editMode">是否为编辑模式</param> /// <param name="onlineView">是否可以在线阅读</param> /// <param name="onlineEdit">是否允许在线编辑</param> /// <param name="isRequired">是否非空验证</param> /// <param name="displayName">非空时提示名称</param> /// <param name="callBack">上传成功的回掉函数</param> /// <returns></returns> public static MvcHtmlString FileUpload<TModel> ( this HtmlHelper<TModel> helper, string name, string value, bool multi = true , string fileTypeExts = null , object htmlAttributes = null , bool editMode = true , bool onlineView = true , bool onlineEdit = false , bool isRequired = false , string displayName = "" , string callBack = "" ) { var id = Utils.CreateTagId( "fine-uploader" ); return FileUpload(helper, id, name, value, multi, fileTypeExts, htmlAttributes, editMode, onlineView, onlineEdit, isRequired: isRequired, displayName: displayName, callBack: callBack); } /// <summary> /// 上传文件,指定name和value不需要绑定到字段 /// </summary> /// <param name="helper">上下文</param> /// <param name="id">容器ID</param> /// <param name="name">后台接收时使用的名称</param> /// <param name="value">值,上传文件的标识串</param> /// <param name="multi">是否可以上传多个</param> /// <param name="fileTypeExts">文件类型扩展名列表</param> /// <param name="htmlAttributes">附加的html属性</param> /// <param name="editMode">是否为编辑模式</param> /// <param name="onlineView">是否可以在线阅读</param> /// <param name="onlineEdit">是否允许在线编辑</param> /// <param name="width">上传文件内容显示宽度</param> /// <param name="isRequired">是否非空验证</param> /// <param name="displayName">非空时提示名称</param> /// <param name="callBack">上传成功的回掉函数</param> /// <returns></returns> public static MvcHtmlString FileUpload<TModel> ( this HtmlHelper<TModel> helper, string id, string name, string value, bool multi = true , string fileTypeExts = null , object htmlAttributes = null , bool editMode = true , bool onlineView = true , bool onlineEdit = false , int width = 722, bool isRequired = false , string displayName = "" , string callBack = "" ) { //内容容器 var div = new TagBuilder( "div" ); div.MergeAttribute( "id" , id); div.MergeAttribute( "data-multi" , multi.ToString().ToLower()); div.MergeAttribute( "data-filetypeexts" , fileTypeExts); div.MergeAttribute( "data-editmode" , editMode.ToString().ToLower()); div.MergeAttribute( "data-onlineView" , onlineView.ToString().ToLower()); div.MergeAttribute( "data-onlineEdit" , onlineEdit.ToString().ToLower()); div.MergeAttribute( "data-canReadExts" , CanReadExts.ToLower()); div.MergeAttribute( "data-canEditExts" , CanEditExts.ToLower()); div.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); if (width <= 0) width = 692; var widthStr = "width:" + width + "px" ; div.MergeAttribute( "style" , widthStr); //值保存位置 var input = new TagBuilder( "input" ); input.MergeAttribute( "type" , "text" ); input.MergeAttribute( "id" , id + "-hidden" ); input.MergeAttribute( "name" , name); input.MergeAttribute( "value" , value); input.MergeAttribute( "style" , "visibility: hidden;height: 0;width: 0;position:absolute;" ); if (isRequired) { input.MergeAttribute( "data-val" , "true" ); input.MergeAttribute( "data-val-required" , displayName + "不能为空" ); } //产生上传文件的区域代码 var uploadedFileStr = CreateUploadedTag(helper, value, editMode, onlineView, onlineEdit); //隐藏已上传文件的html内容 var hidDiv = new TagBuilder( "div" ); hidDiv.AddCssClass( "fileUploaderedHidDiv" ); hidDiv.MergeAttribute( "style" , "display:none" ); hidDiv.InnerHtml = uploadedFileStr; div.InnerHtml += hidDiv.ToString(); //初始化脚本 var script = new TagBuilder( "script" ); script.InnerHtml += "jQuery(function(){createUploader('" + id + "'," + editMode.ToString().ToLower() + ",'" + uploadedFileStr + "'," + multi.ToString().ToLower() + ",'" + fileTypeExts + "','" + callBack + "');});" ; return MvcHtmlString.Create(div + input.ToString() + script); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理