2012-04-04 14 views
6

Ich habe ein Ansichtsmodell mit einer Filter-Eigenschaft eine Action mit Eigenschaften zu schaffen, die viele Eigenschaften hat, die ich meine DatenWie für das View-Modell

Beispiel Filter verwenden, um:

class MyViewModel : IHasFilter 
{ 
    public MyData[] Data { get; set; } 
    public FilterViewModel Filter { get; set; } 
} 

class FilterViewModel 
{ 
    public String MessageFilter { get; set; } 
    //etc. 
} 

arbeitet Diese gut, wenn mit meiner Ansicht. Ich kann die Eigenschaften von Model.Filter einstellen und sie werden an den Controller übergeben. Was ich jetzt zu tun versuche, ist eine ActionLink erstellen, die eine Abfragezeichenfolge hat, die mit dem obigen Format arbeitet.

Der Query-String von meiner Ansicht erzeugt von oben sieht wie folgt aus:

http://localhost:51050/?Filter.MessageFilter=Stuff&Filter.OtherProp=MoreStuff 

Ich brauche eine Actionlink in einer anderen Ansicht für jede Zeile in einem Raster zu erzeugen, die oben auf den View geht.

Ich habe versucht:

Html.ActionLink(
    item.Message, 
    "Index", 
    "Home", 
    new { Filter = new { MessageFilter = item.Message, }, }, 
    null); 

Ich habe auch versucht, um das routeValues Argument setzen:

new MyViewModel { Filter = new FilterViewModel { MessageFilter = item.Message, }, }, 

Aber diese nicht die Query-String, wie das oben eine erzeugen.

+0

+1 eine gute Frage –

+0

danke für den Schnitt; Ich habe die Antwort refaktoriert und im Ersatz vergessen, das 'Präfix' hinzuzufügen! –

+0

@AndrasZoltan Keine Probleme. – DaveShaw

Antwort

1

Sie könnten eine Routevaluedictionary aus einer FilterViewModel Instanz erstellen und dann ToDictionary auf das verwenden, um einen anderen Routevalues ​​mit allen Schlüsseln mit 'Filter.' Präfix zu übergeben.

es weiter machen, Sie eine spezielle Überschreibung von RouteValueDictionary konstruieren könnte, die ein Präfix akzeptiert (daher macht es auch für andere Szenarien nützlicher):

public class PrefixedRouteValueDictionary : RouteValueDictionary 
{ 
    public PrefixedRouteValueDictionary(string prefix, object o) 
    : this(prefix, new RouteValueDictionary(o)) 
    { } 

    public PrefixedRouteValueDictionary(string prefix, IDictionary<string, object> d) 
    : base(d.ToDictionary(kvp=>(prefix ?? "") + kvp.Key, kvp => kvp.Value)) 
    { } 
} 

, die mit Sie jetzt tun können:

Html.ActionLink( 
    item.Message, 
    "Index", 
    "Home", 
    new PrefixedRouteValueDictionary("Filter.", 
    new FilterViewModel() { MessageFilter = item.Message }), 
    null); 

Der Nachteil ist jedoch, dass die Add, Remove, TryGetValue und this[string key] Methoden nicht geändert werden, um die prefix zu berücksichtigen. Das kann erreicht werden, indem Versionen dieser Methoden definiert werden, aber weil sie nicht virtuell sind, funktionieren sie nur von Anrufern, die wissen, dass sie mit einem PrefixedRouteValueDictionary anstelle eines RouteValueDictionary sprechen.

+0

Das war meinem anfänglichen Gedanken ähnlich. Ein weiterer Nachteil besteht darin, dass Sie nur einen Präfix pro ActionLink-Methodenaufruf haben und alle Elemente das Präfix haben müssen. Ich habe meine Antwort mit einer anderen möglichen Lösung aktualisiert, abhängig von den genauen Bedürfnissen und dem Gedanken, dass Sie interessiert sein könnten. Prost. – Craig

2

Interessante Frage (+1). Ich nehme an, dass der Zweck darin besteht, den Standard-Modellbinder zu verwenden, um die Querystring-Parameter an Ihre Action Parameter zu binden.

Out of the box Ich glaube nicht, dass die ActionLink Methode dies für Sie tun wird (natürlich gibt es nichts, was Sie daran hindert, Ihre eigenen zu rollen). Wenn wir im Reflektor suchen, können wir sehen, dass, wenn die object zu RouteValueDictionary hinzugefügt wird, nur Schlüsselwertpaare hinzugefügt werden. Dies ist der Code, der die Schlüsselwertpaare hinzufügt, und wie Sie sehen können, werden die Objekteigenschaften nicht durchsucht.

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values)) 
{ 
    object obj2 = descriptor.GetValue(values); 
    this.Add(descriptor.Name, obj2); 
} 

So für Ihr Objekt

var values = new { Filter = new Filter { MessageFilter = item.Message } } 

der Schlüssel hinzugefügt wird, ist Filter und der Wert ist Ihr Filter Objekt, das mit dem vollständig qualifizierten Namen des Objekttypen auswerten wird.

Das Ergebnis ist Filter=Youre.Namespace.Filter.

bearbeitenmögliche Lösung auf Ihre genauen Bedürfnisse je


Erweiterungsmethodemacht die Arbeit

Beachten Sie, dass es die statischen Rahmen Methoden verwendet ExpressionHelper und ModelMetadata (die werden auch von den vorhandenen Helfern verwendet), um die passenden Namen zu ermitteln, die der Standard-Modellbinder wird verstehen und Wert der Eigenschaft jeweils.

public static class ExtentionMethods 
{ 
    public static MvcHtmlString ActionLink<TModel, TProperty>(
     this HtmlHelper<TModel> helper, 
     string linkText, 
     string actionName, 
     string controllerName, 
     params Expression<Func<TModel, TProperty>>[] expressions) 
    { 
     var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext); 

     var url = urlHelper.Action(actionName, controllerName); 

     if (expressions.Any()) 
     { 
      url += "?"; 

      foreach (var expression in expressions) 
      { 
       var result = ExpressionHelper.GetExpressionText(expression); 

       var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, helper.ViewData); 

       url = string.Concat(url, result, "=", metadata.SimpleDisplayText, "&"); 
      } 

      url = url.TrimEnd('&'); 
     } 

     return new MvcHtmlString(string.Format("<a href='{0}'>{1}</a>", url, linkText)); 
    } 
} 

Beispielmodelle

public class MyViewModel 
{ 
    public string SomeProperty { get; set; } 

    public FilterViewModel Filter { get; set; } 
} 

public class FilterViewModel 
{ 
    public string MessageFilter { get; set; } 
} 

Aktion

public ActionResult YourAction(MyViewModel model) 
{ 
    return this.View(
     new MyViewModel 
     { 
      SomeProperty = "property value", 
      Filter = new FilterViewModel 
      { 
       MessageFilter = "stuff" 
      } 
     }); 
} 

Nutzungs

Eine beliebige Anzahl von Ansichtsmodelleigenschaften kann über den letzten params Parameter der Methode zum Querystring hinzugefügt werden.

@this.Html.ActionLink(
    "Your Link Text", 
    "YourAction", 
    "YourController", 
    x => x.SomeProperty, 
    x => x.Filter.MessageFilter) 

Markup

<a href='/YourAction/YourController?SomeProperty=some property value&Filter.MessageFilter=stuff'>Your Link Text</a> 

Statt string.Format verwenden Sie TagBuilder verwenden könnte, sollte die Abfragezeichenfolgeflag codiert werden sicher in einer URL übergeben werden und diese Erweiterung Methode würde eine zusätzliche Validierung müssen aber Ich denke, es könnte nützlich sein. Beachten Sie auch, dass diese Erweiterungsmethode zwar für MVC 4 erstellt wurde, jedoch leicht für frühere Versionen geändert werden kann. Ich wusste nicht, dass das eine der MVC-Tags bis jetzt für Version 3 war.

+0

* Natürlich gibt es nichts, was dich davon abhält, dein eigenes zu rollen * - und wie könnte ich das tun? :) Und du hast recht, ich habe das "Filter = Your.Nam ..." gesehen. – DaveShaw

+0

@DaveShaw obwohl Sie vielleicht nicht zustimmen, ich fühle mich, als ob dies eine wertvolle Antwort auf Ihre Frage ist. Prost. – Craig

+0

@DaveShaw dachte auf dem Heimweg von der Arbeit nach und nahm einen Stich. Diese Lösung könnte reif sein, wenn Sie damit arbeiten, aber ich denke, es könnte nützlich sein. – Craig

Verwandte Themen