Я согласен с решением jrummell, приведенным выше: когда вы используете EditorFor
-Extension, вы должны написать собственный шаблон редактора для описания визуальных компонентов.
В некоторых случаях я думаю, что использовать шаблон редактора для нескольких свойств модели с одним и тем же типом данных немного сложно. В моем случае я хочу использовать в своей модели десятичные значения валюты, которые должны отображаться в виде форматированной строки. Я хочу стилизовать эти свойства, используя соответствующие классы CSS в своих представлениях.
Я видел другие реализации, в которых параметры HTML добавлялись к свойствам с помощью аннотаций в модели. На мой взгляд, это плохо, потому что информация о представлении, такая как определения CSS, должна быть установлена в представлении, а не в модели данных.
Поэтому я работаю над другим решением:
Моя модель содержит свойство decimal?
, которое я хочу использовать в качестве поля валюты. Проблема в том, что я хочу использовать тип данных decimal?
в модели, но отображать десятичное значение в представлении как отформатированную строку с использованием маски формата (например, «42,13 €»).
Вот определение моей модели:
[DataType(DataType.Currency), DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = true)]
public decimal? Price { get; set; }
Маска формата 0:C2
форматирует decimal
с двумя десятичными знаками. ApplyFormatInEditMode
важен, если вы хотите использовать это свойство для заполнения редактируемого текстового поля в представлении. Поэтому я установил его на true
, потому что в моем случае я хочу поместить его в текстовое поле.
Обычно вы должны использовать EditorFor
-Extension в представлении следующим образом:
<%: Html.EditorFor(x => x.Price) %>
Проблема:
Я не могу добавлять сюда классы CSS, например, используя Html.TextBoxFor
.
Чтобы предоставить собственные классы CSS (или другие атрибуты HTML, например tabindex
или readonly
) с EditorFor
-Extension, нужно написать собственный HTML-помощник, например Html.CurrencyEditorFor
. Вот реализация:
public static MvcHtmlString CurrencyEditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Object htmlAttributes)
{
TagBuilder tb = new TagBuilder("input");
// We invoke the original EditorFor-Helper
MvcHtmlString baseHtml = EditorExtensions.EditorFor<TModel, TValue>(html, expression);
// Parse the HTML base string, to refurbish the CSS classes
string basestring = baseHtml.ToHtmlString();
HtmlDocument document = new HtmlDocument();
document.LoadHtml(basestring);
HtmlAttributeCollection originalAttributes = document.DocumentNode.FirstChild.Attributes;
foreach(HtmlAttribute attr in originalAttributes) {
if(attr.Name != "class") {
tb.MergeAttribute(attr.Name, attr.Value);
}
}
// Add the HTML attributes and CSS class from the View
IDictionary<string, object> additionalAttributes = (IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
foreach(KeyValuePair<string, object> attribute in additionalAttributes) {
if(attribute.Key == "class") {
tb.AddCssClass(attribute.Value.ToString());
} else {
tb.MergeAttribute(attribute.Key, attribute.Value.ToString());
}
}
return MvcHtmlString.Create(HttpUtility.HtmlDecode(tb.ToString(TagRenderMode.SelfClosing)));
}
Идея состоит в том, чтобы использовать исходное EditorFor
-Extension для создания HTML-кода и синтаксического анализа этой выходной строки HTML для замены созданного CSS-атрибута Html нашими собственными классами CSS и добавления других дополнительных атрибутов HTML. Для разбора HTML я использую HtmlAgilityPack (используйте Google).
В представлении вы можете использовать этот помощник следующим образом (не забудьте поместить соответствующее пространство имен в web.config
в вашем каталоге просмотра!):
<%: Html.CurrencyEditorFor(x => x.Price, new { @class = "mypricestyles", @readonly = "readonly", @tabindex = "-1" }) %>
С помощью этого помощника стоимость вашей валюты должна хорошо отображаться в представлении.
Если вы хотите опубликовать свое представление (форму), то обычно все свойства модели будут отправлены в метод действия вашего контроллера. В нашем случае будет отправлено десятичное значение в строковом формате, которое будет обрабатываться внутренним классом привязки модели ASP.NET MVC.
Поскольку этот связыватель модели ожидает decimal?
-значение, но получает значение в строковом формате, будет создано исключение. Итак, мы должны преобразовать отформатированную строку обратно в decimal?
- представление. Следовательно, необходима собственная ModelBinder
-реализация, которая преобразует десятичные значения валюты обратно в десятичные значения по умолчанию ("42,13 €" => "42,13").
Вот реализация такой привязки моделей:
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object o = null;
decimal value;
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
try {
if(bindingContext.ModelMetadata.DataTypeName == DataType.Currency.ToString()) {
if(decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, null, out value)) {
o = value;
}
} else {
o = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
} catch(FormatException e) {
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return o;
}
}
Связующее должно быть зарегистрировано в global.asax
файле вашего приложения:
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
...
}
Может решение кому то поможет.
person
FunThom
schedule
04.02.2013