Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/asp.net-mvc-3/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc EditorFor()和additionalViewData:如何在helper类中添加数据?_Asp.net Mvc_Asp.net Mvc 3_Views_Anonymous Types - Fatal编程技术网

Asp.net mvc EditorFor()和additionalViewData:如何在helper类中添加数据?

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

EditorFor()可以采用
对象additionalViewData
参数,典型的填充方法如下:

EditorFor(model=>model.PropertyName,new{myKey=“myValue”})

如何在自定义HTML帮助程序中检查additionalViewData的内容、添加或追加键的现有值等

我尝试过以下方法:

  • 转换为
    Dictionary()
    并添加/附加值:不起作用,因为MVC中的EditorFor实现似乎使用了新的RouteValueDictionary(additionalViewData),它将字典嵌入到字典中
  • 使用
    新的RouteValueDictionary(additionalViewData)
    转换为RouteValueDictionary,但问题与上述相同(或非常类似)
我也对“你做错了”持开放态度——也许我错过了一个更简单的方法。请记住,我要做的是编写一个可重用的HTML帮助程序,并向附加视图数据添加一些值,以供自定义视图使用。有些值依赖于属性中的元数据,因此它不像使用一堆不同的模板那么简单

更新我正在做的事情的示例:

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