在我的index.cshtml中,我想显示WizardStepAttribute
值
因此,用户会在每个页面的顶部看到Step 1: Enter User Information
我有一个名为WizardViewModel
的ViewModel。此ViewModel具有IList<IStepViewModel> Steps
每个“步骤”实现接口IStepViewModel,这是一个空接口。
我有一个名为Index.cshtml的视图。此视图显示EditorFor()
当前步骤。
我有一个自定义的ModelBinder,它根据IStepViewModel
属性将View绑定到实现WizardViewModel.CurrentStepIndex
的具体类的新实例
我创建了自定义属性WizardStepAttribute
。
我的每个步骤类都是这样定义的。
[WizardStepAttribute(Name="Enter User Information")]
[Serializable]
public class Step1 : IStepViewModel
....
我的观点强烈输入WizardViewModel
而不是每一步。我不想为IStepViewModel
我以为我可以在界面中添加属性,但是我必须在每个类中明确地实现它。 (所以这不是更好)
我想我可以在界面中使用反射来实现它,但是你不能在界面中的方法中引用实例。
答案 0 :(得分:11)
可以做到,但既不容易也不漂亮。
首先,我建议在WizardStepAttribute类StepNumber中添加第二个字符串属性,以便您的WizardStepAttribute类看起来像这样:
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class WizardStepAttribute : Attribute
{
public string StepNumber { get; set; }
public string Name { get; set; }
}
然后,每个班级都必须装饰:
[WizardAttribute(Name = "Enter User Information", StepNumber = "1")]
public class Step1 : IStepViewModel
{
...
}
接下来,您需要创建自定义DataAnnotationsModelMetadataProvider,以获取自定义属性的值并将其插入Step1模型的元数据中:
public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var additionalValues = attributes.OfType<WizardStepAttribute>().FirstOrDefault();
if (additionalValues != null)
{
modelMetadata.AdditionalValues.Add("Name", additionalValues.Name);
modelMetadata.AdditionalValues.Add("StepNumber", additionalValues.StepNumber);
}
return modelMetadata;
}
}
然后,为了呈现您的自定义元数据,我建议您创建一个自定义HtmlHelper来为每个视图创建标签:
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
{
return WizardStepLabelFor(htmlHelper, expression, null /* htmlAttributes */);
}
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
return WizardStepLabelFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
[SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var values = metadata.AdditionalValues;
// build wizard step label
StringBuilder labelSb = new StringBuilder();
TagBuilder label = new TagBuilder("h3");
label.MergeAttributes(htmlAttributes);
label.InnerHtml = "Step " + values["StepNumber"] + ": " + values["Name"];
labelSb.Append(label.ToString(TagRenderMode.Normal));
return new MvcHtmlString(labelSb.ToString() + "\r");
}
如您所见,自定义帮助程序使用您的自定义元数据创建一个h3标记。
然后,最后,在您看来,请输入以下内容:
@Html.WizardStepLabelFor(model => model)
两个注意事项:首先,在Global.asax.cs文件中,将以下内容添加到Application_Start():
ModelMetadataProviders.Current = new MyModelMetadataProvider();
其次,在Views文件夹的web.config中,确保为自定义HtmlHelper类添加命名空间:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="YOUR NAMESPACE HERE"/>
</namespaces>
</pages>
</system.web.webPages.razor>
瞧。
counsellorben
答案 1 :(得分:3)
在我们的例子中,我们只需要一个实现IMetadataAware
接口的属性:
https://msdn.microsoft.com/en-us/library/system.web.mvc.imetadataaware(v=vs.118).aspx
在您的情况下,这可能是:
public class WizardStepAttribute : Attribute, IMetadataAware
{
public string Name;
public void OnMetadataCreated(ModelMetadata metadata)
{
if (!metadata.AdditionalValues.ContainsKey("WizardStep"))
{
metadata.AdditionalValues.Add("WizardStep", Name);
}
}
}