2009-08-19 12 views
11

¿Es posible vincular ese tipo de propiedad?ASP MVC.NET: cómo vincular KeyValuePair?

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

He intentado utilizar el código siguiente en la vista, pero no funciona:

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

Por favor, describa que "no funciona" significa. –

+0

¿Exactamente qué no funciona? ¿Podrías elaborar un poco? – Jimmeh

+0

Estoy tratando de procesar los datos que se han actualizado en la vista, por lo que en el controlador estoy recibiendo el modelo correcto actualizado, pero esta propiedad en particular no tiene un valor. En mi caso es [nulo, nulo]. –

Respuesta

8

KeyValuePair<K,V> es una estructura, no una clase, por lo que cada llamada a su propiedad Stuff devuelve una copia del original KeyValuePair. Entonces, cuando se vincula a Model.Stuff.Value y a Model.Stuff.Key, en realidad está trabajando en dos instancias diferentes de KeyValuePair<K,V>, ninguna de las cuales es la de su modelo. Entonces, cuando se actualizan, no actualiza la propiedad Stuff en su modelo ... QED

Por cierto, las propiedades de clave y valor son de solo lectura, por lo que no puede modificarlas: tiene que reemplazar la instancia KeyValuePair

la siguiente solución debería funcionar:

Modelo:

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

Vista:

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

Gracias por la solución, funciona. No se ve bonito, pero eso es suficiente para este caso. –

+0

Parece que es más limpio usar una clase para esto. Es frustrante: más código (como el anterior) u otra clase. Hrm ... Gracias por preguntar esto. – Cymen

+0

También [este] (http://khalidabuhakmeh.com/submitting-a-dictionary-to-an-asp-net-mvc-action), publicar usando 'Dictionary' puede ayudar a alguien. – stom

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

podría funcionar?

+0

Intenté esto, pero el resultado es el mismo. –

0

Si necesita vincular un diccionario, de modo que cada valor tenga una caja de texto para editarlo, a continuación se muestra una forma de hacerlo funcionar. Las partes realmente importantes que afectan la forma en que se genera el atributo de nombre en el HTML es la expresión del modelo, que es lo que asegura que la vinculación del modelo se produce en la devolución de datos. Este ejemplo solo funciona para Dictionary.

El artículo vinculado explica la sintaxis HTML que hace que el enlace funcione, pero deja la sintaxis de Razor para lograr esto como un gran misterio. Además, el artículo es bastante diferente en cuanto a que permiten editar las claves y los valores, y usan un índice entero a pesar de que la clave del diccionario es una cadena, no un número entero. Por lo tanto, si intenta vincular un diccionario, primero deberá evaluar si desea que los valores sean editables, o las claves y los valores, antes de decidir qué enfoque adoptar, porque esos escenarios son bastante diferentes.

Si alguna vez necesita vincularse a un objeto complejo, es decir, diccionario, entonces solo debe poder tener un cuadro de texto para cada propiedad con la expresión de exploración en la propiedad, similar al artículo.

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:

Editores para valores solamente (por supuesto, usted podría añadir LabelFor para generar etiquetas basadas en la tecla):

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

Edición de ambas llaves/valores : @ {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

Sé que esta es una pregunta un poco más antigua, pero no me gustó ninguna de las soluciones sugeridas, así que doy la mía. He reescrito el archivador de modelo predeterminado para manejar KeyValuePairs para que pueda usarlos como antes.

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