Asp.net mvc EditorFor()和additionalViewData:如何在helper类中添加数据?
EditorFor()可以采用Asp.net mvc EditorFor()和additionalViewData:如何在helper类中添加数据?,asp.net-mvc,asp.net-mvc-3,views,anonymous-types,Asp.net Mvc,Asp.net Mvc 3,Views,Anonymous Types,EditorFor()可以采用对象additionalViewData参数,典型的填充方法如下: EditorFor(model=>model.PropertyName,new{myKey=“myValue”}) 如何在自定义HTML帮助程序中检查additionalViewData的内容、添加或追加键的现有值等 我尝试过以下方法: 转换为Dictionary()并添加/附加值:不起作用,因为MVC中的EditorFor实现似乎使用了新的RouteValueDictionary(additio
对象additionalViewData
参数,典型的填充方法如下:
EditorFor(model=>model.PropertyName,new{myKey=“myValue”})
如何在自定义HTML帮助程序中检查additionalViewData的内容、添加或追加键的现有值等
我尝试过以下方法:
- 转换为
并添加/附加值:不起作用,因为MVC中的EditorFor实现似乎使用了新的RouteValueDictionary(additionalViewData),它将字典嵌入到字典中Dictionary()
- 使用
转换为RouteValueDictionary,但问题与上述相同(或非常类似)新的RouteValueDictionary(additionalViewData)
public static MvcHtmlString myNullableBooleanFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> choice, string templateName, object additionalViewData)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(choice, htmlHelper.ViewData);
/*
here need to add to additionalViewData some key values among them:
{ propertyName, metadata.PropertyName }
*/
StringBuilder sb = new StringBuilder();
sb.AppendLine(htmlHelper.EditorFor(choice, templateName, additionalViewData).ToString());
MvcHtmlString validation = htmlHelper.ValidationMessageFor(choice);
if (validation != null)
sb.AppendLine(validation.ToString());
return new MvcHtmlString(sb.ToString());
}
看看字典里是如何包含字典的内容的?是的,实际数据在内部字典中,但毫不奇怪,这不起作用
添加了赏金。如果我理解正确,您正在尝试迭代匿名类型的属性。如果是: [编辑] 好吧,不止这些。这真的让我很困扰,因为我喜欢C#,它不会让我做Python做的事情,我也喜欢它。因此,这里有一个解决方案,如果您使用的是C#4,但它很混乱,可以解决我遇到的类似问题,但可能不完全适合您:
// Assume you've created a class to hold commonly used additional view data
// called MyAdditionalViewData. I would create an inheritance hierarchy to
// contain situation-specific (area-specific, in my case) additionalViewData.
class MyAdditionalViewData
{
public string Name { get; set; }
public string Sound { get; set; }
}
/* In your views */
// Pass an instance of this class in the call to your HTML Helper
EditorFor(model => model.PropertyName,
new { adv = new MyAdditionalViewData { Name = "Cow", Sound = "Moo" } }) ;
/* In your HTML helper */
// Here's the C# 4 part that makes this work.
dynamic bovine = new ExpandoObject();
// Copy values
var adv = x.adv;
if (adv.Name != null) bovine.Name = adv.Name;
if (adv.Sound != null) bovine.Sound = adv.Sound;
// Additional stuff
bovine.Food = "Grass";
bovine.Contents = "Burgers";
// When the MVC framework converts this to a route value dictionary
// you'll get a proper object, not a dictionary within a dictionary
var dict = new RouteValueDictionary(bovine);
必须有更好的方法。RouteValueDictionary使用TypeDescriptor基础结构(
TypeDescriptor.GetProperties(obj)
)来反映额外的ViewData对象。由于此基础结构是可扩展的,因此您可以创建一个类,该类实现ICustomTypeDescriptor并提供您选择的伪属性,例如基于内部字典。
在下面的文章中,您将看到一个实现:
使用此实现,您可以轻松添加具有任意名称和值的“属性”,并将其作为AddationalViewData对象传递。要从初始对象获取现有属性,您可以使用MVC稍后将使用的相同方法,调用TypeDescriptor.GetProperties,枚举属性,获取属性的名称和值,并将它们添加到新的“对象”中 您是否尝试将数据添加到
helper.ViewData[xxx] = yyy;
ViewData集合是ViewContext的全局集合,因此我认为只要将其添加到全局ViewData中,就可以在呈现EditorTemplate时使用它
更多信息:据我所知,additionalViewdata属性只是在决定渲染哪个控件后,向全局ViewData动态添加集合/任何内容的一种简单方法。仍然使用相同的上下文集合,与其说它是一个不同的对象,不如说它是一种添加到相同上下文字典的新方法
我还没有尝试过这个,所以如果我没有抓住要点,那么说出来,我就删除答案。在我的例子中,我有一个布尔字段编辑器,我希望它是一个yes/no收音机。我使用additionalViewData属性设置要本地化的是/否文本。对我来说似乎很有用 以下是我的自定义编辑器的代码:
@model bool?
@{
var yes = ViewBag.TrueText ?? @Resources.WebSiteLocalizations.Yes;
var no = ViewBag.FalseText ?? @Resources.WebSiteLocalizations.No;
}
<div class="title">
@Html.LabelFor(model => model)
@Html.RequiredFor(model => model)
</div>
<div class="editor-field">
@Html.RadioButton("", "True", (Model.HasValue && Model.Value))@yes
<br />
@Html.RadioButton("", "False", (Model.HasValue && Model.Value == false))@no
<br />
@Html.ValidationMessageFor(model => model)
</div>
@Html.DescriptionFor(model => model)
有点晚了,但是有一个使用CodeDom的有效解决方案
感谢原作者安东尼·约翰斯顿 听起来你根本不是在写HtmlHelper。你能解释一下你的目标是什么吗?我添加了一个例子——它是一个HTML帮助程序,它使用EditorFor,以便调用者可以指定模板。类似于TextBoxFor()或其他HTML帮助程序的源代码。我已经找到了另一种方法(返回RadioButtonFor()),因为我不再需要覆盖某些HTML输出。所以我想说这个例子是一个例子,但不是我要用的东西。我仍然很好奇,在HTML helpers中添加/删除/替换additionalViewData中的值是否是一种现实的方法。您是否能够将该对象转换为
字典
?我不太清楚tell@David我能够毫无问题地将对象转换为Dictionary
,但是当该对象被传递到EditorFor()
并发送到视图(使用Razor,放入一些代码并用调试器捕获)时,它被错误地发送:它是Dictionary()
这是正确的,但是该词典中有我们想要的实际词典。我的猜测是EditorFor中的代码无法判断它已经是一个字典()
,并再次对其进行重新字典化。我在问题的底部添加了一个更新,提供了关于这个案例的更多细节。实际上,这是在为匿名类型添加值。或者在ASP.NET MVC 3中这样做的正确方法,它不使用匿名类型。我同意——必须有更好的方法。我猜这是一个“不要这样做,希望我们能实现一些有意义的东西”,尽管其他答案之一看起来也很有希望。接受你的答案,因为你显然花了一些时间研究这个问题,这是一个很好的信息。我将坚持我的另一种方法,希望ASP.NET MVC团队能够解决这个问题。您可以将ExpandoObject转换为IDictionary并进行迭代。
@model bool?
@{
var yes = ViewBag.TrueText ?? @Resources.WebSiteLocalizations.Yes;
var no = ViewBag.FalseText ?? @Resources.WebSiteLocalizations.No;
}
<div class="title">
@Html.LabelFor(model => model)
@Html.RequiredFor(model => model)
</div>
<div class="editor-field">
@Html.RadioButton("", "True", (Model.HasValue && Model.Value))@yes
<br />
@Html.RadioButton("", "False", (Model.HasValue && Model.Value == false))@no
<br />
@Html.ValidationMessageFor(model => model)
</div>
@Html.DescriptionFor(model => model)
@Html.EditorFor(model => model.IsActive, new {TrueText = @Resources.WebSiteLocalizations.Active, FalseText = @Resources.WebSiteLocalizations.InActive})
public interface IObjectExtender
{
object Extend(object obj1, object obj2);
}
public class ObjectExtender : IObjectExtender
{
private readonly IDictionary<Tuple<Type, Type>, Assembly>
_cache = new Dictionary<Tuple<Type, Type>, Assembly>();
public object Extend(object obj1, object obj2)
{
if (obj1 == null) return obj2;
if (obj2 == null) return obj1;
var obj1Type = obj1.GetType();
var obj2Type = obj2.GetType();
var values = obj1Type.GetProperties()
.ToDictionary(
property => property.Name,
property => property.GetValue(obj1, null));
foreach (var property in obj2Type.GetProperties()
.Where(pi => !values.ContainsKey(pi.Name)))
values.Add(property.Name, property.GetValue(obj2, null));
// check for cached
var key = Tuple.Create(obj1Type, obj2Type);
if (!_cache.ContainsKey(key))
{
// create assembly containing merged type
var codeProvider = new CSharpCodeProvider();
var code = new StringBuilder();
code.Append("public class mergedType{ \n");
foreach (var propertyName in values.Keys)
{
// use object for property type, avoids assembly references
code.Append(
string.Format(
"public object @{0}{{ get; set;}}\n",
propertyName));
}
code.Append("}");
var compilerResults = codeProvider.CompileAssemblyFromSource(
new CompilerParameters
{
CompilerOptions = "/optimize /t:library",
GenerateInMemory = true
},
code.ToString());
_cache.Add(key, compilerResults.CompiledAssembly);
}
var merged = _cache[key].CreateInstance("mergedType");
Debug.Assert(merged != null, "merged != null");
// copy data
foreach (var propertyInfo in merged.GetType().GetProperties())
{
propertyInfo.SetValue(
merged,
values[propertyInfo.Name],
null);
}
return merged;
}
}
var merged = Extender.Extend(new { @class }, additionalViewData));