Answers:
您可以查看以下有关编写自定义的文章DataAnnotationsModelMetadataProvider
。
这是涉及新引入的IMetadataAware接口的另一种ASP.NET MVC 3ish继续方法。
首先创建实现此接口的自定义属性:
public class PlaceHolderAttribute : Attribute, IMetadataAware
{
private readonly string _placeholder;
public PlaceHolderAttribute(string placeholder)
{
_placeholder = placeholder;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["placeholder"] = _placeholder;
}
}
然后用它来装饰模型:
public class MyViewModel
{
[PlaceHolder("Enter title here")]
public string Title { get; set; }
}
接下来定义一个控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
}
相应的视图:
@model MyViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Title)
<input type="submit" value="OK" />
}
最后是编辑器模板(~/Views/Shared/EditorTemplates/string.cshtml
):
@{
var placeholder = string.Empty;
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("placeholder"))
{
placeholder = ViewData.ModelMetadata.AdditionalValues["placeholder"] as string;
}
}
<span>
@Html.Label(ViewData.ModelMetadata.PropertyName)
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { placeholder = placeholder })
</span>
Prompt
工作。
正如达林·迪米特洛夫(Darin Dimitrov)回答中的smnbss注释Prompt
正是出于此目的而存在的,因此无需创建自定义属性。从文档中:
获取或设置一个值,该值将用于为UI中的提示设置水印。
要使用它,只需装饰视图模型的属性,如下所示:
[Display(Prompt = "numbers only")]
public int Age { get; set; }
然后将该文本方便地放入中ModelMetadata.Watermark
。开箱即用,MVC 3中的默认模板会忽略该Watermark
属性,但是使其工作起来确实非常简单。您需要做的就是调整默认的字符串模板,以告诉MVC如何呈现它。只需像Darin一样编辑String.cshtml,除了除了ModelMetadata.AdditionalValues
直接从中获取水印外,还可以直接从中获取水印ModelMetadata.Watermark
:
〜/ Views / Shared / EditorTemplates / String.cshtml:
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", placeholder = ViewData.ModelMetadata.Watermark })
就是这样。
如您所见,使一切正常工作的关键是placeholder = ViewData.ModelMetadata.Watermark
一点。
如果您还想为多行文本框(文本区域)启用水印功能,请对MultilineText.cshtml进行相同的操作:
〜/ Views / Shared / EditorTemplates / MultilineText.cshtml:
@Html.TextArea("", ViewData.TemplateInfo.FormattedModelValue.ToString(), 0, 0, new { @class = "text-box multi-line", placeholder = ViewData.ModelMetadata.Watermark })
Prompt
属性。有关这些模板的更多信息,请参考Brad Wilson的精彩文章:bradwilson.typepad.com/blog/2009/10/…–
DisplayAttribute
(包括Prompt)的所有字符串参数都是可本地化的。您只需要在注释中指定ResourceType即可[Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]
。就是这样。水印的文字现在来自关键AgeGroup资源PeopleResources。
EditorTemplates
文件夹默认情况下不存在;您只需在Views\Shared
文件夹中创建(或者Views\{ControllerName}
如果您希望它特定于某个控制器)。然后,将.cshtml模板放置在此文件夹中,您应该会很好。
实际上,我大部分时间更喜欢使用占位符文本的显示名称。这是使用DisplayName的示例:
@Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })
我写了一个简单的类:
public static class WatermarkExtension
{
public static MvcHtmlString WatermarkFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var watermark = ModelMetadata.FromLambdaExpression(expression, html.ViewData).Watermark;
var htmlEncoded = HttpUtility.HtmlEncode(watermark);
return new MvcHtmlString(htmlEncoded);
}
}
用法如下:
@Html.TextBoxFor(model => model.AddressSuffix, new {placeholder = Html.WatermarkFor(model => model.AddressSuffix)})
在视图模型中的属性:
[Display(ResourceType = typeof (Resources), Name = "AddressSuffixLabel", Prompt = "AddressSuffixPlaceholder")]
public string AddressSuffix
{
get { return _album.AddressSuffix; }
set { _album.AddressSuffix = value; }
}
注意提示参数。在这种情况下,我使用资源中的字符串进行本地化,但是您只能使用字符串,而应避免使用ResourceType参数。
这是我使用上述想法制作的解决方案,可用于TextBoxFor和PasswordFor:
public static class HtmlHelperEx
{
public static MvcHtmlString TextBoxWithPlaceholderFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return htmlHelper.TextBoxFor(expression, htmlAttributes.AddAttribute("placeholder", metadata.Watermark));
}
public static MvcHtmlString PasswordWithPlaceholderFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
return htmlHelper.PasswordFor(expression, htmlAttributes.AddAttribute("placeholder", metadata.Watermark));
}
}
public static class HtmlAttributesHelper
{
public static IDictionary<string, object> AddAttribute(this object htmlAttributes, string name, object value)
{
var dictionary = htmlAttributes == null ? new Dictionary<string, object>() : htmlAttributes.ToDictionary();
if (!String.IsNullOrWhiteSpace(name) && value != null && !String.IsNullOrWhiteSpace(value.ToString()))
dictionary.Add(name, value);
return dictionary;
}
public static IDictionary<string, object> ToDictionary(this object obj)
{
return TypeDescriptor.GetProperties(obj)
.Cast<PropertyDescriptor>()
.ToDictionary(property => property.Name, property => property.GetValue(obj));
}
}
我认为创建自定义EditorTemplate并不是一个好的解决方案,因为您需要关心许多不同情况下的提示:字符串,数字,组合框等。其他解决方案是自定义扩展到HtmlHelper。
模型:
public class MyViewModel
{
[PlaceHolder("Enter title here")]
public string Title { get; set; }
}
HTML Helper扩展:
public static MvcHtmlString BsEditorFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TValue>> expression, string htmlClass = "")
{
var modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var metadata = modelMetadata;
var viewData = new
{
HtmlAttributes = new
{
@class = htmlClass,
placeholder = metadata.Watermark,
}
};
return htmlHelper.EditorFor(expression, viewData);
}
相应的视图:
@Html.BsEditorFor(x => x.Title)