2009-08-19 7 views
11

Czy można powiązać taki rodzaj własności?ASP MVC.NET - jak powiązać KeyValuePair?

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

Próbowałem użyć następującego kodu w widoku, ale to nie działa:

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

Proszę opisać, co oznacza "nie działa". –

+0

Co dokładnie nie działa? Czy mógłbyś trochę rozwinąć? – Jimmeh

+0

Próbuję przetworzyć dane, które zostały zaktualizowane w widoku, więc w kontrolerze otrzymuję poprawny zaktualizowany model, ale ta konkretna właściwość nie ma wartości. W moim przypadku jest to [null, null]. –

Odpowiedz

8

jest strukturą, a nie klasą, dlatego każde połączenie z Twoją własnością Stuff zwraca kopię oryginalnego KeyValuePair. Tak więc, po powiązaniu z Model.Stuff.Value i Model.Stuff.Key, faktycznie pracujesz nad dwoma różnymi instancjami KeyValuePair<K,V>, z których żaden nie jest tym z twojego modelu. Więc kiedy są aktualizowane, to nie aktualizuje właściwości Rzeczy w modelu ... QED

Przy okazji, właściwości Klucz i Wartość są tylko do odczytu, więc nie można ich modyfikować: trzeba zastąpić wystąpienie KeyValuePair

Poniższy obejście powinno działać:

model:

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

Widok:

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

Dziękujemy za obejście problemu, działa. Nie wygląda ładnie, ale to wystarczy w tym przypadku. –

+0

Wygląda na to, że używanie klasy jest czystsze. To frustrujące - więcej kodu (jak wyżej) lub innej klasy. Hmm ... Dzięki, że o to pytasz. – Cymen

+0

Również [this] (http://khalidabuhakmeh.com/submitting-a-dictionary-to-an-asp-net-mvc-action), post za pomocą 'Dictionary' może pomóc komuś. – stom

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

może działać?

+0

Próbowałem tego, ale wynik jest taki sam. –

0

Jeśli chcesz powiązać słownik, tak aby każda wartość zawierała texbox do edycji, poniżej jest jeden sposób na jego działanie. Naprawdę istotnymi częściami, które wpływają na sposób generowania atrybutu nazwy w HTML, jest wyrażenie modelu, które zapewnia, że ​​powiązanie modelu występuje przy odświeżeniu. Ten przykład działa tylko w przypadku Słownika.

W tym artykule opisano objaśnienie składni HTML, która sprawia, że ​​powiązanie działa, ale pozostawia składnię Razor, aby osiągnąć tę dość tajemnicę. Ponadto, artykuł jest zupełnie inny, ponieważ umożliwia edycję zarówno kluczy, jak i wartości, i używa indeksu całkowitego, mimo że klucz słownika jest ciągiem, a nie liczbą całkowitą. Więc jeśli próbujesz powiązać słownik, naprawdę musisz najpierw ocenić, czy chcesz, aby wartości były edytowalne, czy też oba klucze i wartości, zanim zdecydujesz, które podejście przyjąć, ponieważ te scenariusze są całkiem inne.

Jeśli kiedykolwiek potrzebujesz powiązać obiekt złożony, tzn. Słownik, powinieneś po prostu mieć pole tekstowe dla każdej właściwości z wyraże- niem wiercenia w obiekcie, podobnym do tego artykułu.

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:

Edytory dla wartości tylko (oczywiście można dodać LabelFor do generowania etykiet w oparciu o klucz):

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

Edycja oba klawisze/Wartości : @ {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

Wiem, że jest to nieco starsze pytanie, ale nie podobało mi się żadne sugerowane rozwiązanie, więc daję je. Mam przepisany domyślny segregator modelowy do obsługi KeyValuePairs, więc mogę używać ich tak jak poprzednio.

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