在Asp.net MVC中定义模型的时候,DataType有DataType.ImageUrl这个类型,但htmlhelper却无法输出一个img,当用脚手架自动生成一些form或表格的时候,这些Url字段总是需要再手动改一次,特别是我想在img上面包裹一个a标签。并限定大小,比如:
方法1:分部视图
在做后台表格的时候经常要修改这样的问题,于是首先想到的就是做一个分部视图,叫tableimg。
@model string @if (!string.IsNullOrEmpty(Model)) { }
使用的时候:
@Html.Partial("tableimg",Model.Img)
方便是方便了些,但还是不够灵活。宽度是写死的;而且还要记住这个视图,如果这样的片段多了都不知道谁是谁了;和脚手架生成的代码TextBoxFor,DisplayFor等风格也不一样;如果要增加参数呢,还得去改模型。
方法2:UIHint
这个方法和分部视图相似,也是使用模板,需要先在shared文件夹下创建一个EditorTemplates文件夹,然后新建一个视图。这里命名为ImageLink。内容和上面一样。
@model string @if (!string.IsNullOrEmpty(Model)) { }
只是调用方法不一样:
[DataType(DataType.ImageUrl)] )] public string Img { get; set; }
在视图里面通过EditorFor调用:
@Html.EditorFor(model => model.Img)
这修改的地方比较多,感觉不太舒服。能不能一劳永逸呢?当然是可以的,这需要自定义一个ModelMetadataProvider,来告诉MVC这个数据类型的属性就用这个模板显示。
public class ImageModelMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var meta= base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); if (meta.DataTypeName==DataType.ImageUrl.ToString() && string.IsNullOrEmpty(meta.TemplateHint)) { ; } return meta; } }
ModelMetadata是用来描述模型数据结构的数据,比如数据类型、约束、显示名称等,而ModelMetadataProvider就是用来提供Model的模型元数据的。
然后全局注册:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); ModelMetadataProviders.Current = new ImageModelMetadataProvider(); }
模型的定义里面,不再需要加UiHint了
[DataType(DataType.ImageUrl)] public string Img { get; set; }
视图里面调用的时候,需要用EditorFor。回头看一下,这种方式还是不够灵活,要实现一个效果,首先要增加一个模板,然后注册模型元数据提供器,然后每一个要显示计划效果的模型还要强制的使用DataType特性以及Html.EditorFor输出,这让人有点束缚的感觉。
可不可以只改一个地方呢?于是想到扩展htmlhelper
方法3:Html.Image
新建一个静态类,Htmlhelpers,增加一个Image的扩展方法,有url和length两个参数。用tagbuilder创建标签,增加属性。
public static MvcHtmlString Image(this HtmlHelper helper, string url, int length) { ); tagA.MergeAttribute(, url); tagA.MergeAttribute(, ); ); img.MergeAttribute(, url); img.MergeAttribute(, , length)); tagA.InnerHtml = img.ToString(); return MvcHtmlString.Create(tagA.ToString()); }
最后返回MvcHtmlString ,但上面体现不了tagbuilder的好处。如果觉得写tag比较麻烦,可以这样:
, url, length); return MvcHtmlString.Create(str);
调用的时候传入参数:
@Html.Image(Model.Img,100)
结果显示ok:
但如果要增加宽度以及更多的样式,想将这个img的id指定为模型属性的名字呢 ,那就得用ImageFor了。
方法4:Html.ImageFor
开始不会写,就想到参考MVC源码,于是用强大的ILSpy(直接把System.Web.MVC.dll拖进来)找到了System.Web.MVC.HTML中的源码,直接可以看到LabelExtension和DisplayExtension等,常用的TextBoxFor位于InputExtension。
所以这里我借鉴了上面的方法,先产生一个img,在用a表情包裹着。这里如果还用string.Format那就太糟糕了。
internal static MvcHtmlString ImageHelper(HtmlHelper html, ModelMetadata metadata, IDictionary<string, object> htmlAttributes = null)
{
value = metadata.Model.ToString();
(string.IsNullOrEmpty(value))
{
return MvcHtmlString.Empty;
}
);
, value);
img.Attributes.Add("id", metadata.PropertyName);
img.MergeAttributes(htmlAttributes, true);
);
tagA.MergeAttribute(,value);
tagA.MergeAttribute(, );
tagA.InnerHtml = img.ToString();
return MvcHtmlString.Create(tagA.ToString());
}
public static MvcHtmlString ImageFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
//var propertyName = ExpressionHelper.GetExpressionText(expression); //也能获取到属性名
var htmlAttributes2 = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
return ImageHelper(html, modelMetadata, htmlAttributes2);
}
public static MvcHtmlString ImageFor<TModel, TProperty>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TProperty>> expression)
{
return ImageFor(html, expression, null);
}