ModelMetaData,自定义类属性和一种说不出的问题 [英] ModelMetaData, Custom Class Attributes and an indescribable question

查看:915
本文介绍了ModelMetaData,自定义类属性和一种说不出的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我index.cshtml我要显示的 WizardStepAttribute

In my index.cshtml I want to display the WizardStepAttribute Value

所以,用户在每个页面的顶部看到,第1步:输入用户信息

So, a user will see at the top of each page, Step 1: Enter User Information

我有一个名为视图模型 WizardViewModel 。该视图模型有一个属性是的IList< IStepViewModel>步骤

I have a ViewModel called WizardViewModel. This ViewModel has a property that is IList<IStepViewModel> Steps

每个步骤实现了接口IStepViewModel,这是一个空的接口

each "step" implements the Interface IStepViewModel, which is an empty interface.

我有一个名为Index.cshtml视图。此视图显示 EditorFor()当前步骤。

I have a view called Index.cshtml. This view displays EditorFor() the current step.

我有一个自定义的模型绑定器,结合了以落实 IStepViewModel 根据 WizardViewModel.CurrentStepIndex 属性

I have a custom ModelBinder, that binds the View to an new instance of the concrete class implementing IStepViewModel based on the WizardViewModel.CurrentStepIndex property

我创建了一个自定义属性 WizardStepAttribute

I have created a custom attribute WizardStepAttribute.

我的脚步类中的每一个定义是这样的。

Each of my Steps classes are defined like this.

[WizardStepAttribute(Name="Enter User Information")] 
[Serializable]
public class Step1 : IStepViewModel
....

我有几个问题,虽然。

我的看法是强类型为 WizardViewModel 不是每一个步骤。我不希望有创建视图对​​于每个具体的实施 IStepViewModel

I have several problems though.

My View is strongly typed to WizardViewModel not each step. I don't want to have to create a view for each concrete implementation of IStepViewModel

我以为我可以将属性添加到界面,但我必须明确地落实到每个班级。 (因此,这是没有任何好转)

I thought I could add a property to the interface, but then I have to explicitly implement it in each class. (So this isn't any better)

我想我可以在界面中使用反射实现它,但是,你不能引用实例方法的接口。

I'm thinking I could implement it using reflection in the interface but, you can't refer to instances in methods in an interface.

推荐答案

这是可以做到的,但它既不容易,也不pretty。

It can be done, but it is neither easy nor pretty.

首先,我建议增加第二个字符串属性您WizardStepAttribute类,STEPNUMBER,让你的WizardStepAttribute类看起来是这样的:

First, I would suggest adding a second string property to your WizardStepAttribute class, StepNumber, so that your WizardStepAttribute class looks like this:

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class WizardStepAttribute : Attribute
{
    public string StepNumber { get; set; }
    public string Name { get; set; }
}

然后,每个班级必须饰:

Then, each class must be decorated:

[WizardAttribute(Name = "Enter User Information", StepNumber = "1")]
public class Step1 : IStepViewModel
{
    ...
}

接下来,你需要创建一个自定义DataAnnotationsModelMetadataProvider,把你的自定义属性的值,并将其插入到第一步模型的元数据:

Next, you need to create a custom DataAnnotationsModelMetadataProvider, to take the values of your custom attribute and insert them into the Step1 model's metadata:

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;
    }
}

然后,present您的自定义元数据,我建议创建一个自定义的HtmlHelper创建为每个视图标签:

Then, to present your custom metadata, I suggest creating a custom HtmlHelper to create your label for each view:

    [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

As you can see, the custom helper creates an h3 tag with your custom metadata.

然后,终于,在您看来,放于以下内容:

Then, finally, in your view, put in the following:

@Html.WizardStepLabelFor(model => model)

有两点需要注意:首先,在你的Global.asax.cs文件,添加以下的Application_Start():

Two notes: first, in your Global.asax.cs file, add the following to Application_Start():

        ModelMetadataProviders.Current = new MyModelMetadataProvider();

二,在浏览文件夹中的web.config文件时,请务必添加命名空间为您的自定义HtmlHelper类:

Second, in the web.config in the Views folder, make sure to add the namespace for your custom HtmlHelper class:

<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

counsellorben

这篇关于ModelMetaData,自定义类属性和一种说不出的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆