Thomas Bandt

Über mich | Kontakt | Archiv

ASP.NET MVC 2 - Enumeration, DropDownList, Localization

Es gibt Daten für die es keinen Sinn macht sie in einer Datenbank zu hinterlegen. Etwa, wenn sie sich nur sehr selten ändern, oder wenn eine Änderung zu einer Inkonsitenz des gesamten Systems führen kann. Die Sprache einer (Web-) Anwendung fällt mitunter in diese Kategorie, natürlich abhängig davon, ob Benutzer Sprachen bearbeiten oder hinzufügen können usw.

Im konkreten Fall will ich die Sprachen statisch verwalten, weshalb ich in meiner Domäne ein Enum Language eingeführt habe:

   1:  namespace Blubr.Domain.Model
   2:  {
   3:      public enum Language
   4:      {
   5:          German = 1,
   6:          English = 2,
   7:          French = 3
   8:      }
   9:  }

Wenn sich ein Benutzer registriert oder sein Profil ändert, soll er natürlich beliebig zwischen den Sprachen hin- und herschalten können, also muss man ihm die verfügbaren Sprachen anzeigen, z.B. in einer Selectbox.

Da fragt sich nur wie? Wie bekommt man nun die Werte aus der statischen Liste in eine DropDownList? Und vor allem noch in der richtigen Sprache? Hätte man die Werte in einer Datenbank, wäre das alles kein großes Problem.

Lösungen gibt es viele, von der statischen im View angelegten HTML-Select-Liste angefangen, bis zu Schleifen-Konstrukten, die für jeden Value des Enum noch in einem Wörterbuch nachschlagen und so z.B. "German" durch "Deutsch" ersetzen.

Da das ASP.NET-MVC-Team aber mit Version 2.0 des MVC-Frameworks die Verwendung von DataAnnotations einführt, wähle ich diesen Weg (auch weil er mir sehr gut gefällt). Im nächten Schritt erzeuge ich also ein View-Model und deklariere die Sprache als Attribut:

   1:  namespace Blubr.Web.Models
   2:  {
   3:      public enum LanguageViewModel
   4:      {
   5:          [Display(Name = "Deutsch")]
   6:          German = 1,
   7:          [Display(Name = "English")]
   8:          English = 2,
   9:          [Display(Name = "Française")]
  10:          French = 3
  11:      }
  12:  }

Jetzt habe ich also schon einmal die korrekten Übersetzungen drin. Damit diese aus dem Objekt automatisch als Listen-Elemente erzeugt werden können, war etwas Handarbeit notwendig, im Ergebnis in Form einer Extension-Method:

   1:  public static SelectList ToSelectList<T>(this T enumeration)
   2:  {
   3:   
   4:      var source = Enum.GetValues(typeof(T));
   5:      var items = new Dictionary<object, string>();
   6:      var displayAttributeType = typeof (DisplayAttribute);
   7:   
   8:      foreach (var value in source)
   9:      {
  10:          FieldInfo field = value.GetType().GetField(value.ToString());
  11:          DisplayAttribute attrs = (DisplayAttribute)field.
  12:              GetCustomAttributes(displayAttributeType, false).First();
  13:          items.Add(value, attrs.GetName());
  14:      }
  15:   
  16:      return new SelectList(items, "Key", "Value", enumeration);
  17:   
  18:  }

Diese sieht nur auf den ersten Blick kompliziert aus. Im Prinzip schnappt sie sich per Reflection die Eigenschaften der einzelnen Werte der Aufzählung und erzeugt daraus eine Liste von Parametern für die DropDownList im View:

   1:  namespace Blubr.Web.Models
   2:  {
   3:      public class SignUpViewModel
   4:      {
   5:          public LanguageViewModel Language { get; set; }
   6:      }
   7:  }

   1:  <%= Html.LabelFor(m => m.Language) %>
   2:  <%= Html.DropDownListFor(m => m.Language, Model.Language.ToSelectList()) %>

 

Natürlich lässt sich das Ganze auch lokalisieren. Hierzu legt man wie gewohnt eine Ressourcen-Datei an und gibt dann bei der Deklaration des Attributes den Parameter ResourceType an, dem der Typ der Ressource zugewiesen wird. Der Rest bleibt gleich:

   1:  public enum LanguageViewModel
   2:  {
   3:      [Display(Name = "Deutsch")]
   4:      German = 1,
   5:      [Display(Name = "English")]
   6:      English = 2,
   7:      [Display(ResourceType = typeof(Localization.Demo), Name = "French")]
   8:      French = 3
   9:  }

Im Ergebnis wird automatisch der richtige String entsprechend der aktuellen Culture aus der Ressource-Datei gezogen, der Parameter "Name" fungiert dabei in diesem Fall als ID.

Damit das fehlerfrei funktioniert muss man übrigens aufpassen, wie man in der Extension-Method auf die Name-Eigenschaft zugreift. Den Hinweis habe ich nur zufällig in der MSDN gefunden: ein direkter Zugriff auf die Name-Property funktioniert im Fall der Lokalisierung nämlich nicht (zurückgegeben wird ein leerer String), erst der Gang über die GetName()-Property bringt das gewünschte Ergebnis.

Kommentare

  1. Nea schrieb am Dienstag, 31. August 2010 13:00:00 Uhr:

    Hmm habs so implementiert - kann auch auf die Extension im Code zugreifen - aber nicht in der ascx Seite seltsamerweise, wo ich ja eben das Dropdown plazieren will...
  2. Thomas schrieb am Dienstag, 31. August 2010 13:12:00 Uhr:

    Du musst den Namespace in der Web.config bzw. der View registrieren.
  3. Rene Drescher-Hackel schrieb am Samstag, 5. Februar 2011 12:04:00 Uhr:

    Hallo Thomas,

    und wie bringst du den View dazu, die jeweilige Sprache auch umzusetzen?

    Ich habe alles Mögliche schon versucht, finde aber keine Lösung.

    Beispiel:
    View:
    <%: Html.LabelFor(m => m.BenutzerName)%>

    Model:
    [LocalizedDisplayName("BenutzerName")]
    public string BenutzerName { get; set; }

    Alle Views nutzen dies Klasse WeblogViewPage, wo ich InitializeCulture
    entsprechend überschreibe.

    Dennoch ändert sich beim Umschalten der Sprache nicht der Text - den ich aus einer Resources beziehe.





« Zurück  |  Weiter »