Silverlight 5 in der Praxis

Seite 3: Das Hauptfenster

Inhaltsverzeichnis

Hier geht es konkret um ein Hauptfenster und drei verschiedene untergeordnete Fenster, die wechselweise angezeigt werden sollen. Für jedes untergeordnete Fenster ist ein Paar, bestehend aus View und ViewModel, anzulegen. Eine View wird hier in Form eines UserControls realisiert.

<UserControl …
<Border Grid.Row="2" Grid.ColumnSpan="2" …>
<ContentControl Content="{Binding ActiveViewModel}" />
</Border>

</UserControl>

Das Hauptfenster stellt entsprechend einen Bereich (bei Bedarf können es auch mehrere Bereiche für gleichzeitig darzustellende Unterfenster sein) für die Anzeige der Dialogfenster zur Verfügung (Abbildung 6):

Das Rahmenfenster der Anwendung enthält die Navigationssteuerelemente und den Platz für die untergeordneten Fenster. (Abb. 6)

Auch das Hauptfenster benötigt ein ViewModel, das die Bedienlogik des Gesamtprogramms abbilden soll. Im Konstruktor von MainPage.xaml wird dieses ViewModel (MainViewModel) instanziiert und der Eigenschaft DataContext zugewiesen. Es verfügt über die Eigenschaft ActiveViewModel, die im Hauptfenster an die Content-Eigenschaft des Platzhalters (ContentControl, siehe oben) für die Views gebunden ist:

public class MainViewModel : NotifyPropertyChanged
{
private ViewModelBase activeViewModel;

public ViewModelBase ActiveViewModel
{
get { return activeViewModel; }
set { activeViewModel = value; OnPropertyChanged("ActiveViewModel"); }
}

private FlugauswahlViewModel flugauswahlViewModel;
private PassagierauswahlViewModel passagierauswahlViewModel;
private BuchungAbschließenViewModel buchungAbschließenViewModel;

}

Zur Laufzeit setzt nun die Programmlogik die Eigenschaft ActiveViewModel auf eine der ViewModel-Instanzen (FlugauswahlViewModel, PassagierauswahlViewModel oder BuchungAbschließenViewModel). Über INotifyPropertyChanged erfährt der Bindungsausdruck von der Änderung. Doch mit einer ViewModel-Instanz kann ein Silverlight-Control naturgemäß wenig anfangen und würde eigentlich nur den von ToString() generierten Text anzeigen. Wie erfolgt nun die automatische Darstellung der zugehörigen View?

Der Trick besteht darin, für jede Kombination von View und ViewModel ein entsprechendes DataTemplate bereitzustellen:

<UserControl.Resources>
<DataTemplate DataType="vm:FlugauswahlViewModel">
<v:FlugauswahlView />
</DataTemplate>
<DataTemplate DataType="vm:PassagierauswahlViewModel">
<v:PassagierauswahlView />
</DataTemplate>
<DataTemplate DataType="vm:BuchungAbschließenViewModel">
<v:BuchungAbschließenView/>
</DataTemplate>
</UserControl.Resources>

Diese, in WPF weit verbreitete Vorgehensweise, ließ sich in Silverlight 4 nicht realisieren. Die Möglichkeit, ein allgemeines Template für einen bestimmten Datentyp festzulegen, besteht erst seit der Version 5. Beachten Sie aber bitte die abweichende Syntax zwischen Silverlight und WPF:

Silverlight 5: <DataTemplate DataType="DerTyp">
WPF:<DataTemplate DataType="{x:Type DerTyp}">

So wird automatisch beim Umschalten der Eigenschaft ActiveViewModel in ein anderes ViewModel die zugeordnete View geladen und angezeigt. Das MainViewModel muss keine Kenntnis der betreffenden UserControls besitzen.

Die Navigation im Hauptfenster erfolgt im Beispiel über eine einfache Schaltfläche, die an die Eigenschaft WeiterCommand gebunden ist:

  <Button Content="{Binding WeiterCommand.Value, FallbackValue=Weiter}" 
Command="{Binding WeiterCommand}"
IsEnabled="{Binding ActiveViewModel.IsValid}"…/>

Diese Eigenschaft vom Typ ActionCommand ist mit der Methode Weiter() verknüpft, die die Ablauflogik des Hauptfensters steuert. Hier stellt das ViewModel ebenfalls eine Schnittstelle zum Auslösen eines Commands bereit, muss aber nicht wissen, wie die Oberfläche dieses Command später auslösen wird, welche Steuerelemente daran beteiligt sind und welche Events gefeuert werden.

public MainViewModel()
{
WeiterCommand = new ActionCommand(Weiter);

}



private int aktuellerZustand;

public ActionCommand WeiterCommand { get; set; }
private void Weiter()
{
switch (aktuellerZustand)
{
case 0:
ActiveViewModel = passagierauswahlViewModel;
ZustandSetzen(1);
break;

}
}