一般渲染局部视图 - 将 PropertyInfo 转换为具体对象

Generically Render Partial View - Convert PropertyInfo into Concrete Object

我目前正在使用 MVC (c#) 制作向导。但是我的向导视图中有一个 if 语句,如下所示:

if (Model.Wizard.ClientDetails.GetStep() == Model.Wizard.CurrentStep)
{
    @Html.PartialFor(x => x.Wizard.ClientDetails, "_Step");
}
else if (Model.Wizard.Preferences.GetStep() == Model.Wizard.CurrentStep)
{
    @Html.PartialFor(x => x.Wizard.ClientPreferences, "_Step")
}
else if (Model.Wizard.ClientQuestions.GetStep() == Model.Wizard.CurrentStep)
{
    @Html.PartialFor(x => x.Wizard.ClientQuestions, "_Step")
}

向导的设置非常一般,除了我选择要显示的部分视图的这一部分。从上面的代码可以看出,每个 if 都遵循相同的结构。唯一改变的部分是 Model.Wizard.**Property** 部分。

我想尝试删除此 if 语句,这样我就不必担心为添加到新向导的每个步骤编写 if 语句。

我想把代码改成这样:

@Html.PartialFor(x => x.ExampleWizardTransaction.GetStepObject(), "_Step");

我目前对GetStepObject方法的尝试如下:

public static T GetStepObject<T>(this IWizardTransaction wizardTransaction) 
     where T : class, new()
{
    var properties = wizardTransaction.GetType().GetProperties()
            .Where(x => x.PropertyType.GetCustomAttributes(typeof(StepAttribute), true).Any());

    PropertyInfo @object = properties.FirstOrDefault(x => ((StepAttribute)Attribute
            .GetCustomAttribute(x.PropertyType, typeof(StepAttribute))).Step == wizardTransaction.CurrentStep);

}

PropertyInfo @object 部分正确选择了向导中当前步骤的 属性 信息。我需要能够 return PropertyInfo @object PropertyInfo 作为其当前值的正确类型和 return 它以某种方式。

这可能吗?

编辑#1:

现有 PartialFor 在正常情况下有效。

public static MvcHtmlString PartialFor<TModel, TProperty>(
    this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
    var name = ExpressionHelper.GetExpressionText(expression);
    var model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
    var viewData = new ViewDataDictionary(helper.ViewData)
    {
        TemplateInfo = new TemplateInfo { HtmlFieldPrefix = name }
    };
    return helper.Partial(partialViewName, model, viewData);
}

编辑#2:

值未绑定的原因是 var name = ExpressionHelper.GetExpressionText(expression); 部分是 return 空字符串。如果我将 name 变量硬编码为实际的 属性,则绑定有效。例如:

public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
    var compiled = expression.Compile();
    var result = compiled.Invoke(helper.ViewData.Model);

    var name = ExpressionHelper.GetExpressionText(expression); 
    //Should be ExampleWizardTransaction.ClientDetails for this step but is blank

    var viewData = new ViewDataDictionary(helper.ViewData)
    {
        TemplateInfo = new TemplateInfo 
        { 
             //HtmlFieldPrefix = name
             HtmlFieldPrefix = "ExampleWizardTransaction.ClientDetails" 
        }
        //Hard coded this to ExampleWizardTransaction.ClientDetails and the bindings now work
    };

    return helper.Partial(partialViewName, result, viewData);
 }

看来我需要能够将向导对象的名称和当前步骤对象的名称作为字符串值传递给 TemplateInfo

我将对您的 class 结构进行大胆的猜测。假设你的 classes 是这样的:

[AttributeUsage(AttributeTargets.Property, AllowMultiple =false)]
public class StepAttribute: Attribute
{
    public StepEnum Step { get; set; }
}

public interface IWizardStep
{

}

public interface IWizardTransaction
{

}

public enum StepEnum
{
    Previous,
    CurrentStep
}

public class WizardStep: IWizardStep
{
    public string StepName { get; set; }

    public override string ToString()
    {
        return StepName;
    }
}

public class Wizard : IWizardTransaction
{
    [Step(Step = StepEnum.Previous)]
    public WizardStep ClientDetails => new WizardStep() { StepName = "ClientDetails" };
    [Step(Step = StepEnum.CurrentStep)]
    public WizardStep ClientQuestions => new WizardStep() { StepName = "ClientQuestions" };
}

假设还实现了 PartialFor 方法

    public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> html, 
            Expression<Func<TModel, TProperty>> expression, string partialViewName)
    {
        var compiled = expression.Compile();
        var result = compiled.Invoke(html.ViewData.Model);
        return html.Partial(partialViewName, result);
    }

那么 GetStepObject 的这个实现将起作用

    public static TProperty GetStepObject<TProperty>(this IWizardTransaction wizardTransaction)
        where TProperty : class
    {
        var properties = wizardTransaction.GetType().GetProperties()
                .Where(x => x.GetCustomAttributes(typeof(StepAttribute), true).Any());
        PropertyInfo @object = properties.FirstOrDefault(x => 
                    (x.GetCustomAttributes(typeof(StepAttribute), true).SingleOrDefault()
                                as StepAttribute).Step == StepEnum.CurrentStep);
        return @object.GetValue(wizardTransaction) as TProperty;
    }

有了这个名为 _Step.cshtml 的部分视图的实现,就像这样

@model PartialView.Models.WizardStep

@Model

你的视图可以这样调用

@model PartialView.Models.Wizard
@using PartialView.Models;
@{
    ViewBag.Title = "Partial view calling";
}

@Html.PartialFor(m=>m.GetStepObject<WizardStep>(), "_Step")

并且视觉结果将是一个空白页面,其中包含 html 文本 ClientQuestions