Shorthand for creating a ViewDataDictionary with both a model and ViewData items?

≯℡__Kan透↙ 提交于 2019-11-27 10:51:51

问题


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.


回答1:


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



回答2:


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.




回答3:


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



回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/607985/shorthand-for-creating-a-viewdatadictionary-with-both-a-model-and-viewdata-items

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!