#c# #asp.net-mvc-3 #razor #data-annotations
#c# #asp.net-mvc-3 #razor #данные-аннотации
Вопрос:
Хорошо, я работаю над проблемой, которую я откладывал в течение трех месяцев. Я создал представление, которое повторяет все мои ViewModels, которые реализуют IStepViewModel
. Мне нужно отобразить заголовок формы в представлении, который простым английским языком указывает текущий шаг, над которым работает пользователь. Я хотел бы сделать это с DataAnnotations
, поэтому мне просто нужно украсить каждую ViewModel вот так [StepTitle("Ownership Information")]
. Я пытался это сделать, но не смог заставить это работать. Это означает, что мой ModelDataProvider
вызовется, он не загрузит информацию в metadata.AdditionalValues
, и когда мое представление загрузится, и я попытаюсь прочитать ViewData.ModelMetadata.AdditionalValues["WizardStep"]
, оно не существует.
Я включу свои пользовательские provider
и Attribute
классы внизу.
Index.cshtml
@using Microsoft.Web.Mvc;
@using Tangible.Models;
@model Tangible.Models.WizardViewModel
@{
var currentStep = Model.Steps[Model.CurrentStepIndex];
var progress = ((Double)(Model.CurrentStepIndex) / Model.Steps.Count) * 100;
}
<script type="text/javascript">
$(function () {
$("#progressbar").progressbar({
value: @progress
});
});
</script>
@Html.ValidationSummary()
@using (Html.BeginForm())
{
<div id="progressbar" style="height:20px;">
<span style="position:absolute;line-height:1.2em; margin-left:10px;">Step @(Model.CurrentStepIndex 1) out of @Model.Steps.Count</span>
</div>
<br />
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save amp;amp; Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
@*<input type="submit" value="Save" name="Save" />*@
@Html.Serialize("wizard", Model)
@Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
@Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save amp;amp; Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
@*<input type="submit" value="Save" name="Save" />*@
}
Пользовательский атрибут
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Web.Mvc;
namespace Tangible.Attributes
{
public enum HtmlTextLengthAttribute
{
Description=50,
Long = 35,
Default = 60,
Short = 10,
Email = 30
}
public interface ICustomModelMetaDataAttribute
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple= false, Inherited = true)]
public sealed class WizardStepAttribute : Attribute, ICustomModelMetaDataAttribute
{
public WizardStepAttribute() : base() { }
public String Name { get; set; }
//public virtual int? Order { get; set; }
public IDictionary<string, object> WizardStepAttributes()
{
IDictionary<string, object> attribs = new Dictionary<string, object>();
//attribs = this.GetType().GetProperties().ToDictionary(p => p.Name, p=> p.GetValue(this,null)) ;
attribs.Add("Name", Name);
return attribs;
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class HtmlPropertiesAttribute : Attribute, ICustomModelMetaDataAttribute
{
public HtmlPropertiesAttribute()
{
Size = (int) HtmlTextLengthAttribute.Defau<
}
public string CssClass
{
get;
set;
}
/// <summary>
/// Enter the actual number of characters you want to display in the field.
/// </summary>
public int Size
{
get;
set;
}
public IDictionary<string, object> HtmlAttributes()
{
//Todo: we could use TypeDescriptor to get the dictionary of properties and their values
IDictionary<string, object> htmlatts = new Dictionary<string, object>();
if (Size != 0)
{
htmlatts.Add("size", Size);
}
return htmlatts;
}
}
}
Пользовательский ModelMetadataProvider
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Tangible.Attributes;
namespace Tangible.Providers
{
public class ModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<System.Attribute> attributes, System.Type containerType, System.Func<object> modelAccessor, System.Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var customAttr = attributes.OfType<ICustomModelMetaDataAttribute>();
if (customAttr != null)
{
foreach (var itr in customAttr)
{
metadata.AdditionalValues.Add(itr.GetType().Name, itr);
}
}
return metadata;
}
}
}
Комментарии:
1. Пожалуйста, объясните, но я не смог заставить это работать
2. Я ненавижу задавать глупый вопрос, но вы зарегистрировали свой пользовательский
ModelMetadataProvider
в качестве текущего поставщика в Application_Start в global.asax.cs? Если да, то если вы установите точку останова в своемforeach
цикле в своемCreateMetadata
переопределении, выполняется ли она?3. Да, но, основываясь на приведенном ниже ответе Дарина, я могу фактически полностью избежать пользовательского ModelMetadataProvider, просто реализовав интерфейс IMetaDataAware в моем атрибуте. Но, чтобы вы могли спать сегодня вечером …. прямо из файла Global.asax.cs…
System.Web.Mvc.ModelMetadataProviders.Current = new Tangible.Providers.ModelMetadataProvider();
Ответ №1:
Вы могли бы попробовать создать атрибут, учитывающий метаданные:
public class StepTitleAttribute : Attribute, IMetadataAware
{
private readonly string _title;
public StepTitleAttribute(string title)
{
_title = title;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues["title"] = _title;
}
}
затем украсьте им свою модель представления:
[StepTitle("Ownership Information")]
public class MyViewModel
{
...
}
и в соответствующем представлении:
<h2>@ViewData.ModelMetadata.AdditionalValues["title"]</h2>
Комментарии:
1. @Doug Чемберлен, это можно использовать независимо от любого поставщика метаданных. Конечно, если ваш делает что-то, что удаляет эти значения, это может не сработать для вас.
2. Я не думаю, что это сработает, потому что мой индексный просмотр строго типизирован для WizardViewModel, который имеет свойство Steps, и IEnumerable список IStepViewModels… Итак, когда я обращаюсь к ViewData, это Viewdata для WizardViewModel:(
3. @DougChamberlain, насколько я вижу, вы используете шаблон редактора для текущего шага. Эта информация будет находиться внутри этого шаблона редактора.
4. До сих пор у меня вызывался OnMetaDataCreated. Вызывается Object.cshtml EditorTemplate, но
@ViewData.ModelMetadata.AdditionalValues["title"]
по-прежнему не существует. Похоже, что между OnMetaDataCreated и отображаемым представлением возникает другой запрос.
Ответ №2:
Мне пришлось жестко закодировать свой мой вид. Это был мой единственный вариант.
@switch (Model.CurrentStepIndex)
case 0:
<h2>Preparer's Information</h2>
break;
case 1:
<h2>Owner's Information</h2>
break;
case 2:
<h2>Physical Location</h2>
break;
case 3:
<h2>About this business</h2>
break;
case 4:
<h2>Describe Business</h2>
break;
case 6:
<h2>Sale or Change of Ownership</h2>
break;