Możesz zapoznać się z następującym artykułem, aby napisać niestandardowy DataAnnotationsModelMetadataProvider
.
A oto inny, bardziej ASP.NET MVC 3ish sposób postępowania z wykorzystaniem nowo wprowadzonego IMetadataAware interfejs .
Zacznij od utworzenia niestandardowego atrybutu implementującego ten interfejs:
public class PlaceHolderAttribute : Attribute, IMetadataAware
{
private readonly string _placeholder;
public PlaceHolderAttribute(string placeholder)
{
_placeholder = placeholder;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["placeholder"] = _placeholder;
}
}
A następnie udekoruj nim swój model:
public class MyViewModel
{
[PlaceHolder("Enter title here")]
public string Title { get; set; }
}
Następnie zdefiniuj kontroler:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
}
Odpowiedni widok:
@model MyViewModel
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.Title)
<input type="submit" value="OK" />
}
I na koniec szablon edytora ( ~/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
pracować.Jak komentuje smnbss w odpowiedzi Darina Dimitrova,
Prompt
istnieje dokładnie w tym celu, więc nie ma potrzeby tworzenia niestandardowego atrybutu . Z dokumentacji:Aby go użyć, po prostu udekoruj właściwość modelu widoku w następujący sposób:
[Display(Prompt = "numbers only")] public int Age { get; set; }
Ten tekst jest następnie wygodnie umieszczony w
ModelMetadata.Watermark
. Po wyjęciu z pudełka domyślny szablon w MVC 3 ignoruje tęWatermark
właściwość, ale sprawienie, by działało, jest naprawdę proste. Wszystko, co musisz zrobić, to dostosować domyślny szablon ciągu, aby powiedzieć MVC, jak go renderować. Po prostu edytuj String.cshtml, tak jak robi to Darin, z tym wyjątkiem, że zamiastModelMetadata.AdditionalValues
pobierać znak wodny z , otrzymasz go bezpośrednio zModelMetadata.Watermark
:~ / Views / Shared / EditorTemplates / String.cshtml:
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", placeholder = ViewData.ModelMetadata.Watermark })
I to wszystko.
Jak widać, kluczem do działania jest plik
placeholder = ViewData.ModelMetadata.Watermark
bit.Jeśli chcesz również włączyć znak wodny dla wielowierszowych pól tekstowych (obszarów tekstowych), zrób to samo dla 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 })
źródło
Prompt
atrybutem. Więcej informacji na temat tych szablonów można znaleźć w tym świetnym poście autorstwa Brada Wilsona: bradwilson.typepad.com/blog/2009/10/…DisplayAttribute
(w tym Prompt) są lokalizowalne. Trzeba tylko określić ResourceType w swojej adnotacji:[Display(ResourceType = typeof(PeopleResources), Prompt = "AgePrompt")]
. I to wszystko. Tekst znaku wodnego pochodzi teraz z klucza AgeGroup w zasobie PeopleResources .EditorTemplates
Folder @FrancisRodgers domyślnie nie istnieje; po prostu tworzysz w swoimViews\Shared
folderze (lubViews\{ControllerName}
jeśli chcesz, aby był on specyficzny dla określonego kontrolera). Następnie umieszczasz szablony .cshtml w tym folderze i powinieneś być gotowy.W rzeczywistości wolę używać nazwy wyświetlanej dla tekstu zastępczego przez większość czasu. Oto przykład użycia DisplayName:
@Html.TextBoxFor(x => x.FirstName, true, null, new { @class = "form-control", placeholder = Html.DisplayNameFor(x => x.FirstName) })
źródło
Napisałem takie proste zajęcia:
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); } }
Zastosowanie jako takie:
@Html.TextBoxFor(model => model.AddressSuffix, new {placeholder = Html.WatermarkFor(model => model.AddressSuffix)})
I właściwość w modelu widoku:
[Display(ResourceType = typeof (Resources), Name = "AddressSuffixLabel", Prompt = "AddressSuffixPlaceholder")] public string AddressSuffix { get { return _album.AddressSuffix; } set { _album.AddressSuffix = value; } }
Uwaga parametr Monit. W tym przypadku używam ciągów z zasobów do lokalizacji, ale możesz użyć tylko ciągów, po prostu unikaj parametru ResourceType.
źródło
Używam tego w przypadku pliku zasobów (nie potrzebuję już monitu!)
@Html.TextBoxFor(m => m.Name, new { @class = "form-control", placeholder = @Html.DisplayName(@Resource.PleaseTypeName), autofocus = "autofocus", required = "required" })
źródło
Oto rozwiązanie, które stworzyłem, korzystając z powyższych pomysłów, które można wykorzystać dla TextBoxFor i 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)); } }
źródło
Myślę, że utworzenie niestandardowego szablonu EditorTemplate nie jest dobrym rozwiązaniem, ponieważ musisz zadbać o wiele możliwych tepmlatów dla różnych przypadków: ciągi, liczby, kombinacje i tak dalej. Innym rozwiązaniem jest niestandardowe rozszerzenie do HtmlHelper.
Model:
public class MyViewModel { [PlaceHolder("Enter title here")] public string Title { get; set; } }
Rozszerzenie pomocnika HTML:
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);
}
Odpowiedni widok:
@Html.BsEditorFor(x => x.Title)
źródło