2009-08-19 13 views
11

Est-il possible de lier ce type de propriété?ASP MVC.NET - comment lier KeyValuePair?

public KeyValuePair<string, string> Stuff { get; set; } 

J'ai essayé d'utiliser le code suivant dans la vue, mais il ne fonctionne pas:

<%=Html.Text("Stuff", Model.Stuff.Value)%>  
<%=Html.Hidden("Model.Stuff.Key", Model.Stuff.Key)%> 
+0

Veuillez décrire ce que «cela ne fonctionne pas». –

+0

Exactement ce qui ne fonctionne pas? Pourriez-vous élaborer un peu? – Jimmeh

+0

J'essaye de traiter les données qui ont été mises à jour sur la vue, ainsi dans le contrôleur je reçois le modèle mis à jour correct, mais cette propriété particulière n'a pas de valeur. Dans mon cas, c'est [null, null]. –

Répondre

8

KeyValuePair<K,V> est une structure, pas une classe, de sorte que chaque appel à votre propriété Stuff renvoie une copie de l'original KeyValuePair. Ainsi, lorsque vous liez à Model.Stuff.Value et à Model.Stuff.Key, vous travaillez actuellement sur deux instances différentes de KeyValuePair<K,V>, dont aucune n'est celle de votre modèle. Donc, quand ils sont mis à jour, il ne met pas à jour la propriété Stuff dans votre modèle ... QED

Par ailleurs, les propriétés Key et Value sont en lecture seule, donc vous ne pouvez pas les modifier: vous devez remplacer le KeyValuePair par exemple

la solution suivante devrait fonctionner:

Modèle:

private KeyValuePair<string, string> _stuff; 
public KeyValuePair<string, string> Stuff 
{ 
    get { return _stuff; } 
    set { _stuff = value; } 
} 

public string StuffKey 
{ 
    get { return _stuff.Key; } 
    set { _stuff = new KeyValuePair<string, string>(value, _stuff.Value); } 
} 

public string StuffValue 
{ 
    get { return _stuff.Value; } 
    set { _stuff = new KeyValuePair<string, string>(_stuff.Key, value); } 
} 

Vue:

<%=Html.Text("Stuff", Model.StuffValue)%>  
<%=Html.Hidden("Model.StuffKey", Model.StuffKey)%> 
+1

Merci pour la solution de contournement, cela fonctionne. Ce n'est pas joli, mais c'est suffisant pour cette affaire. –

+0

On dirait qu'il est plus propre d'utiliser une classe pour cela. C'est frustrant - plus de code (comme ci-dessus) ou une autre classe. Hrm ... Merci d'avoir demandé ça. – Cymen

+0

Aussi [this] (http://khalidabuhakmeh.com/submitting-a-dictionary-to-an-asp-net-mvc-action), un post utilisant 'Dictionary' peut aider quelqu'un. – stom

0
<%=Html.Text("Stuff.Value", Model.Stuff.Value)%> 

pourrait fonctionner?

+0

J'ai essayé mais le résultat est le même. –

0

Si vous avez besoin de lier un dictionnaire, de sorte que chaque valeur possède une boîte texbox pour l'éditer, voici une façon de le faire fonctionner. Les parties les plus importantes qui affectent la génération de l'attribut name dans le code HTML sont l'expression du modèle, qui garantit que la liaison du modèle se produit lors de la publication. Cet exemple ne fonctionne que pour le dictionnaire.

L'article lié explique la syntaxe HTML qui fait fonctionner la liaison, mais il laisse la syntaxe Razor pour accomplir cela un mystère. En outre, l'article est assez différent en ce sens qu'il autorise l'édition des clés et des valeurs et utilise un index entier même si la clé du dictionnaire est une chaîne et non un nombre entier. Donc, si vous essayez de lier un dictionnaire, vous devrez d'abord évaluer si vous voulez simplement que les valeurs soient modifiables, ou les clés et les valeurs, avant de décider de l'approche à adopter, car ces scénarios sont très différents.

Si jamais vous avez besoin de lier un objet complexe, c'est-à-dire Dictionnaire, vous devriez juste avoir une zone de texte pour chaque propriété avec l'expression d'exploration dans la propriété, similaire à l'article.

http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

public class SomeVM 
    { 
     public Dictionary<string, string> Fields { get; set; } 
    } 

    public class HomeController : Controller 
    { 
     [HttpGet] 
     public ViewResult Edit() 
     { 
      SomeVM vm = new SomeVM 
      { 
      Fields = new Dictionary<string, string>() { 
        { "Name1", "Value1"}, 
        { "Name2", "Value2"} 
       } 
      }; 

      return View(vm); 

     } 

     [HttpPost] 
     public ViewResult Edit(SomeVM vm) //Posted values in vm.Fields 
     { 
      return View(); 
     } 
    } 

CSHTML:

rédacteurs en chef pour les valeurs que (de vous pourriez bien sûr ajouter LabelFor pour générer des étiquettes basées sur la clé):

@model MvcApplication2.Controllers.SomeVM 

@using (Html.BeginForm()) { 
    @Html.ValidationSummary(true) 

    <fieldset> 
     <legend>SomeVM</legend> 

     @foreach(var kvpair in Model.Fields) 
     { 
      @Html.EditorFor(m => m.Fields[kvpair.Key]) //html: <input name="Fields[Name1]" …this is how the model binder knows during the post that this textbox value gets stuffed in a dictionary named “Fields”, either a parameter named Fields or a property of a parameter(in this example vm.Fields). 
     } 

     <p> 
      <input type="submit" value="Save" /> 
     </p> 
    </fieldset> 
} 

Modification des deux clés/valeurs : @ {var fields = Model.Fields.ToList(); }

@for (int i = 0; i < fields.Count; ++i) 
    { 
     //It is important that the variable is named fields, to match the property name in the Post method's viewmodel. 
     @Html.TextBoxFor(m => fields[i].Key) 
     @Html.TextBoxFor(m => fields[i].Value) 

     //generates using integers, even though the dictionary doesn't use integer keys, 
     //it allows model binder to correlate the textbox for the key with the value textbox:    
     //<input name="fields[0].Key" ... 
     //<input name="fields[0].Value" ... 

     //You could even use javascript to allow user to add additional pairs on the fly, so long as the [0] index is incremented properly 
    } 
0

Je sais que c'est une question un peu plus ancienne, mais je n'ai pas aimé les solutions suggérées, donc je donne la mienne. J'ai réécrit le classeur de modèle par défaut pour gérer KeyValuePairs afin que je puisse les utiliser comme avant.

public class CustomModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var model = base.BindModel(controllerContext, bindingContext);    
     model = ResolveKeyValuePairs(bindingContext, model); 
     return model; 
    } 

    private object ResolveKeyValuePairs(ModelBindingContext bindingContext, object model) 
    { 
     var type = bindingContext.ModelType; 
     if (type.IsGenericType) 
     { 
      if (type.GetGenericTypeDefinition() == typeof (KeyValuePair<,>)) 
      {      
       var values = bindingContext.ValueProvider as ValueProviderCollection; 
       if (values != null) 
       { 
        var key = values.GetValue(bindingContext.ModelName + ".Key"); 
        var keyValue = Convert.ChangeType(key.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[0]); 
        var value = values.GetValue(bindingContext.ModelName + ".Value"); 
        var valueValue = Convert.ChangeType(value.AttemptedValue, bindingContext.ModelType.GetGenericArguments()[1]); 
        return Activator.CreateInstance(bindingContext.ModelType, new[] {keyValue, valueValue}); 
       } 

      } 
     } 
     return model; 
    } 
Questions connexes