Shorthand for creating a ViewDataDictionary with b

2019-01-21 19:54发布

Is there any way to create a ViewDataDictionary with a model and additional properties with a single line of code. I am trying to make a RenderPartial call to a strongly-typed view while assembling both the model and some extra display configuration properties without explicitly assembling the ViewDataDictionary across multiple lines. It seems like it would be possible given the RenderPartial overload taking both a model object and a ViewDataDictionary but it looks like it simply ignores the ViewDataDictionary whenever they are both populated.

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

I found someone else with the same problem, but their solution is the same multi-line concept I found: create a discrete ViewDataDictionary with the model, add the new parameter(s) and use it in the RenderPartial call.

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

I can always wrap that logic into a ChainedAdd method that returns a duplicate dictionary with the new element added but it just seems like I am missing some way of creating a ViewDataDictionary that would do this for me (and that is a bit more overhead than I was hoping for).

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

As well, trying to assemble a ViewDataDictionary with an explicit Model and ModelState simply causes a compilation error because the ModelState is read-only.

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

ANSWER(S): It looks like Craig and I ended up finding two separate syntaxes that will get the job done. I am definitely biased in this case, but I like the idea of setting the model first and "decorating" it afterwards.

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

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

Of course, I would still be spinning my wheels without his [eventually spot-on] answer, so, circle gets the square.

5条回答
混吃等死
2楼-- · 2019-01-21 19:59

Use an object initializer and collection initializers:

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

The inner ViewDataDictionary gets its collection initialized, then this populates the "real" ViewDataDictionary using the constructor overload which takes ViewDataDictionary instead of object. Finally, the object initializer sets the model.

Then just pass the whole thing without setting MyModelObject separately:

this.Html.RenderPartial("SomePartialView", null, 
    new ViewDataDictionary(new ViewDataDictionary() { {"SomeDisplayParameter", true }})
        { Model = MyModelObject });
查看更多
Melony?
3楼-- · 2019-01-21 20:00

I created an extension method on HtmlHelper to copy the property names and values from an anonymous object to a ViewDataDictionary.

Sample

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);
}
查看更多
ゆ 、 Hurt°
4楼-- · 2019-01-21 20:17

This is what worked for me in old style mvc aspx view:

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

the thing here is that in the constructor I use the current viewdata "new ViewDataDictionary(this.ViewData)" which is a viewdatadictionary containing the modelstate that I need for the validationmessages.

查看更多
We Are One
5楼-- · 2019-01-21 20:18

Using Craig's answer as a starting point--I didn't even know you could combine both a constructor call and an object initializer--I stumbled on this snippet from Palermo that leads to a combination that works. He uses some sort of dictionary shorthand that somehow ends up populating the ModelState when consumed by the ViewDataDictionary object initializer.

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

I still don't see how this ends up working given that you cannot set the ModelState explicitly in an initializer, but it does seem to maintain both the original model object and the "appended" parameters for the view. There are definitely a number of other permutations of this syntax that do not work--you cannot combine the model with the dictionary in a single object or use object-initializer syntax for the dictionary values--but the above version seems to work.

查看更多
神经病院院长
6楼-- · 2019-01-21 20:21

I came here with the exact same question.

What I thought might work was this (pardon the VB Razor syntax)

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

But of course you get this error at run-time:

System.InvalidOperationException was unhandled by user code

Message=The model item passed into the dictionary is of type 'VB$AnonymousType_1`1[System.String]', but this dictionary requires a model item of type 'ViewModel.Address'.

But what I found, is that what I really wanted was to use was Editor Template anyway.

Instead of RenderPartial use:

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

An editor template is just a partial view that lives

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

My template for Address is a strongly-typed partial view, but the EditorFor method gives the ability to add additional view data items easily with an anon object.

In the example above I didn't need to include the template name "Address", since MVC would would look for a template with the same name as the model type.

You can also override the display template in the same way.

查看更多
登录 后发表回答