创建同时包含模型和视图数据项的ViewDataDictionary的速记方式是什么?

26
有没有一种方法可以用单行代码创建一个带有模型和额外属性的 ViewDataDictionary。我正试图在不需要明确组装多行 ViewDataDictionary 的情况下,对强类型视图进行 RenderPartial 调用,并同时组装模型和一些额外的显示配置属性。根据 RenderPartial 接受一个模型对象和 ViewDataDictionary 的重载,似乎可能实现,但它似乎只是忽略了 ViewDataDictionary,无论何时当它们都被填充时。
// FAIL: This will result in ViewData being a ViewDataDictionary
// where Model = MyModelObject and there are no other parameters available.
this.Html.RenderPartial("SomePartialView", MyModelObject, new ViewDataDictionary(new { SomeDisplayParameter = true }));

我发现还有其他人也遇到了相同的问题,但他们的解决方案与我找到的多行概念相同:创建一个离散的ViewDataDictionary与模型一起,添加新的参数并在RenderPartial调用中使用它。

var SomeViewData = new ViewDataDictionary(MyModelObject);
SomeViewData.Add("SomeDisplayParameter", true);
this.Html.RenderPartial("SomePartialView", SomeViewData);

我总是可以将那个逻辑封装到一个ChainedAdd方法中,该方法返回一个加入了新元素的重复字典,但似乎我错过了一种创建ViewDataDictionary的方式,可以为我完成这个操作 (而且这比我希望的要多一些开销)。

this.Html.RenderPartial("SomePartialView", new ViewDataDictionary(MyModelObject).ChainedAdd("SomeDisplayParameter", true));

public static ViewDataDictionaryExtensions {
    public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, string key, object value) {
        return source.ChainedAdd(new KeyValuePair<string,object>(key, value));
    }
    public static ViewDataDictionary ChainedAdd(this ViewDataDictionary source, KeyValuePair<string, object> keyAndValue) {
        ViewDataDictionary NewDictionary = new ViewDataDictionary(source);
        NewDictionary.Add(keyAndValue);
        return NewDictionary;
    }
}

此外,尝试使用显式的 ModelModelState 组装 ViewDataDictionary 只会导致编译错误,因为 ModelState 是只读的。

// FAIL: Compilation error
this.Html.RenderPartial("SomePartialView", new ViewDataDictionary { Model = MyModelObject, ModelState = new ViewDataDictionary( new { SomeDisplayParameter = true }});

回答:看起来 Craig 和我找到了两种不同的语法来完成这项工作。在这种情况下,我肯定有偏见,但我喜欢先设置模型,然后再“装饰”它的想法。

new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };

new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
    { Model = MyModelObject };
当然,如果没有他最终精准的答案,我仍将一直在原地打转,所以成功归功于他。
5个回答

26

使用对象初始化器和集合初始化器:

new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
    {
        Model = MyModelObject
    }

内部的ViewDataDictionary将其集合初始化,然后使用构造函数重载填充“真正”的ViewDataDictionary,该构造函数需要ViewDataDictionary而不是对象。最后,对象初始化程序设置模型。

然后只需传递整个内容,无需单独设置MyModelObject:

this.Html.RenderPartial("SomePartialView", null, 
    new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
        { Model = MyModelObject });

这似乎创建了一个ViewDataDictionary,其中Model等于匿名对象,然后在之后用MyModelObject替换了Model属性,没有添加任何附加属性。它确实教会了我如何将构造函数与其他初始化程序结合使用;这是我不知道的东西--谢谢。 - patridge
我刚刚检查了源代码。设置模型不会清除字典。 - Craig Stuntz
我同意,设置模型不会清除字典。但是在这段代码中,您没有设置字典。您创建了一个ViewDataDictionary,并将其模型设置为包含SomeDisplayParameter属性的匿名对象。然后,使用初始化程序,您将模型更改为MyModelObject。 - patridge
我明白你的意思。ViewDataDictionary(ViewDataDictionary)和ViewDataDictionary(object)构造函数的工作方式不同。我会更新示例。 - Craig Stuntz
我找到了一个可用的语法(下一个答案),但我不知道它背后发生了什么来使其工作。我想,如果我深入研究MVC源代码,我可能会能够理解这个特定初始化程序语法中使用的逻辑。 - patridge
我的更新答案已经解释了为什么它有效;它只是使用了相反的操作顺序。任何一种方式都可以。 - Craig Stuntz

12

以Craig的答案为起点——我甚至不知道你可以同时组合构造函数调用和对象初始化器——我偶然发现了这个片段,来自Palermo,它导致了一个可行的组合。他使用了一些字典简写,不知何故最终在被ViewDataDictionary对象初始化器使用时填充了ModelState

new ViewDataDictionary(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };
// Of course, this also works with typed ViewDataDictionary objects (what I ended up using)
new ViewDataDictionary<SomeType>(MyModelObject) { { "SomeDisplayParameter", true }, { "SomeOtherParameter", 3 }, { "SomeThirdParameter", "red" } };

我依然不太明白如何使其工作,因为你无法在初始化代码块中显式设置ModelState,但似乎它确实保持了原始模型对象和视图的“附加”参数。这种语法还有许多其他的变化形式是行不通的——你不能将模型与字典结合到一个单一对象中,也不能使用对象初始化器语法来设置字典值——但上面的版本似乎可行。


2

我创建了一个HtmlHelper的扩展方法,用于将匿名对象中的属性名称和值复制到ViewDataDictionary中。

示例

Html.RenderPartial("SomePartialView", MyModelObject, new { SomeDisplayParameter = true })

HtmlHelper Extension

public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, object viewData)
{
    var vdd = new ViewDataDictionary(model);
    foreach (var property in viewData.GetType().GetProperties()) {
        vdd[property.Name] = property.GetValue(viewData);
    }
    htmlHelper.RenderPartial(partialViewName, vdd);
}

正是我所需要的!谢谢 - Dirk Boer

1

这是我在旧版MVC ASPX视图中使用的方法:

<% Html.RenderPartial("ContactPartial", Model.ContactFactuur, new ViewDataDictionary(this.ViewData ) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "Factuur" } }); %>

这里的问题在于构造函数中使用了当前视图数据 "new ViewDataDictionary(this.ViewData)",该视图数据字典包含了我需要用于验证消息的模型状态。

0

我也有同样的问题。

我认为可能会起作用的是这个(请原谅VB Razor语法)

 @Code Html.RenderPartial("Address", Model.MailingAddress, New ViewDataDictionary(New With {.AddressType = "Mailing Address"}))End Code

但是当然你会在运行时得到这个错误:

System.InvalidOperationException被用户代码未处理

消息=传递给字典的模型项的类型为“VB$AnonymousType_1`1[System.String]”,但此字典需要类型为“ViewModel.Address”的模型项。

但我发现,我真正想要使用的是编辑器模板。

不要使用RenderPartial,而要使用:

@Html.EditorFor(Function(model) model.MailingAddress, "Address",  New With {.AddressType = "Mailing Address"})

编辑器模板只是一个部分视图,它存在于

~/Views/{Model|Shared}/EditorTemplates/templatename.vbhtml

我的地址模板是一个强类型的部分视图,但是EditorFor方法可以使用匿名对象轻松添加其他视图数据项。

在上面的示例中,我不需要包含模板名称“Address”,因为MVC会查找与模型类型相同名称的模板。

您也可以以相同的方式覆盖显示模板。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接