Как настроить EditorFor CSS с помощью бритвы

У меня есть этот класс

public class Contact
{
    public int Id { get; set; }
    public string ContaSurname { get; set; }
    public string ContaFirstname { get; set; }
    // and other properties...
}

И я хочу создать форму, которая позволит мне редактировать все эти поля. Итак, я использовал этот код

<h2>Contact Record</h2>

@Html.EditorFor(c => Model.Contact)

Это нормально работает, но я хочу настроить отображение элементов. Например, я хочу, чтобы каждое поле отображалось в той же строке, что и его метка. Потому что теперь сгенерированный html выглядит так:

<div class="editor-label">
  <label for="Contact_ContaId">ContaId</label>
</div>
<div class="editor-field">
  <input id="Contact_ContaId" class="text-box single-line" type="text" value="108" name="Contact.ContaId">
</div>

person kbaccouche    schedule 01.10.2012    source источник


Ответы (2)


Создайте частичное представление под названием Contact.cshtml с вашей пользовательской разметкой в ​​Views/Shared/EditorTemplates. Это заменит редактор по умолчанию.

Как отмечает @smartcavemen, см. Блог Брэда Уилсона для введения в шаблоны.

person jrummell    schedule 01.10.2012
comment
+1, я также хотел бы упомянуть: серию блогов Брэда Уилсона по теме bradwilson.typepad.com/blog/2009/10/ - person smartcaveman; 01.10.2012
comment
@smartcaveman спасибо за ссылку, не помню, кто написал ту серию. Почему-то я подумал, что это Скоттгу! - person jrummell; 01.10.2012

Я согласен с решением 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