Silverlight 5 und MVVM – Fight the Gap (Teil 3)
Kommentare

MarkupExtensions in MVVM
Ein gutes und bereits prominentes Beispiel für den Einsatz von MarkupExtensions im MVVM-Umfeld ist die Umsetzung einer Lokalisierung von Oberflächenelementen (ein Beispiel

MarkupExtensions in MVVM

Ein gutes und bereits prominentes Beispiel für den Einsatz von MarkupExtensions im MVVM-Umfeld ist die Umsetzung einer Lokalisierung von Oberflächenelementen (ein Beispiel gibt es hier). Hier soll skizziert werden, welchen Weg die Lösung einschlägt, da daran einige interessante Eigenschaften der MarkupExtensions sichtbar werden. Vorweg soll bereits gezeigt werden, wie die Übersetzungserweiterung eingesetzt werden soll: In einem beliebigen Steuerelement sollen beliebige Eigenschaften übersetzbar sein, indem folgende Schreibweise angewendet wird: . Zunächst benötigt man eine Klasse, in der die lokalisierten Ressourcen gehalten werden. Sie sollte so ausgelegt sein, dass die Herkunft und Beschaffung der Daten flexibel variiert werden kann. So kann man die Daten entweder aus dem IsolatedStorage, aus Ressourcen oder über einen Service laden. Eine einfache Umsetzung eines solchen Wörterbuchs zeigt Listing 5. 

Listing 5: Die Basisklasse LanugageDictionary und eine einfache Implementierung

public abstract class LanguageDictionary 
{ 
public static LanguageDictionary Current 
{ 
get; 
set; 
} 
public Object this[String Key] 
{ 
get 
{ 
return GetTranslatedValue(Key); 
} 
} 
protected abstract Object GetTranslatedValue(string Key); 
} 
public class TestLanguageDictonary : LanguageDictionary 
{ 
private Dictionary m_TranslatedValues = new Dictionary(); 
public TestLanguageDictonary(CultureInfo Culture) 
{ 
m_TranslatedValues.Add("Lab.MVVM.Client.Content1View;Label1;Text", "Test " + Culture.Name); 
} 
protected override Object GetTranslatedValue(string Key) 
{ 
return m_TranslatedValues[Key]; 
} 
}   

Die Klasse LanguageDictionary selbst ist abstrakt und verfügt lediglich über eine statische Property, die das aktuell zu verwendende Wörterbuch beinhaltet. Über einen Indexer kann unter Angabe eines Ressourcenschlüssels die entsprechend lokalisierte Ressource abgerufen werden. Dieser Indexer bedient sich der abstrakten Methode GetTranslatedValue, die den eigentlichen übersetzten Wert beschaffen muss. Dadurch trifft die Klasse LanguageDictionary keine Annahmen über die Beschaffung und Ablage der Daten. Die Klasse TestLanguageDictionary stellt eine einfache Testimplementierung des Wörterbuchs dar: Die übersetzten Werte werden in einem Dictionary gespeichert, das fest mit einem einzigen Wert gefüllt wird. Bei der Instanziierung wird die gewünschte Kultur übergeben, die einfach dem übersetzen Wert angehängt wird. Das hier gewählte Format des Schlüssels ist ;;. Prinzipiell kann der Schlüssel frei gewählt werden. Zusammen gesetzt wird dieser Schlüssel in der MarkupExtension, die zur Übersetzung herangezogen wird. Da sie, wie oben zu erkennen, keine Parameter mehr erfordert, muss der Schlüssel komplett aus dem Kontext hergeleitet werden. Der komplette Code für die TranslateExtension ist in Listing 6 zu finden. 

Listing 6: Die Klasse TranslateExtension

public class TranslateExtension : MarkupExtension 
{ 
public override object ProvideValue(IServiceProvider serviceProvider) 
{ 
IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 
IRootObjectProvider rootObject = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider; 
FrameworkElement root = rootObject.RootObject as FrameworkElement; 
FrameworkElement instance = targetProvider.TargetObject as FrameworkElement; 
PropertyInfo prop = targetProvider.TargetProperty as PropertyInfo; 
DependencyProperty dependecyProp = DependencyPropertyResolver.Current.Resolve(instance, prop.Name); 
String Key = String.Format("{0};{1};{2}", root.GetType().FullName, instance.Name, prop.Name); 
Binding translateBinding = new Binding(String.Format("[{0}]", Key)) { Source = LanguageDictionary.Current, Mode=BindingMode.TwoWay }; 
return translateBinding.ProvideValue(serviceProvider); 
} 
}   

Die ProvideValueMethode bekommt ein Argument vom Typ IServiceProvider übergeben. Dieses Interface definiert die Methode GetService, über die man abfragen kann, ob der Dienstanbieter ein bestimmtes Interface implementiert; wenn ja, erhält man eine entsprechende Instanz zurück. Aktuell implementiert der übergebene serviceProvider die beiden Interfaces IProvidedValueTarget und IRootObjectProvider. Die Eigenschaften der Interfaces sind in Tabelle 1 dargestellt. Diese erhält man durch die folgenden Befehle:

IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; 
IRootObjectProvider rootObject = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;   

Interface

Eigenschaft

Beschreibung

IProvideValueTarget

TargetObject

Ein Object, das die Instanz des Steuerelements beinhaltet, auf das die Erweiterung angewendet wird

IProvideValueTarget

TargetProperty

Ein PropertyInfo für die Eigenschaft, auf die die Erweiterung angewendet wird (in WPF wird hier ein Objekt vom Typ DependencyProperty geliefert)

IRootObjectProvider

RootObject

Ein Object, das das Wurzelelement des TargetObject beinhaltet, in der Regel das UserControl, in dem sich das Steuerelement befindet

Tabelle 1: Methoden der Interfaces IProvideValueTarget und IRootObjectProvider

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -