MVVM ICommand mit Silverlight -mal wirklich ganz einfach

Wenn man heute jemand sein will, muss ein Silverlight Projekt mindestens MVVM drin haben und besonders cool soll ja PRISM oder Unity sein. Als erklärter Pragmatiker, muss ich mir zuerst einmal immer  mindestens 30 minütige Ausführungen über mich ergehen lassen, warum das MVxx Ding so wichtig ist. Da fallen Argumente wie Testbarkeit (ich hasse Wörter die mit keit enden), Trennung von UI und Logik, sauberer Code und überhaupt Weltfrieden. In den nächsten 30 Minuten versucht dann mein Gegenüber mich von der Notwendigkeit seines Lieblings MVVM Frameworks zu überzeugen. Weil mit MVVM wird zwar alles einfacher wird, aber man braucht viel mehr Code dafür und eine Design Ansicht haben sowieso nur Luschen. In der Gesamtheit betrachtet eine Lösung für ein Problem, das ich nicht habe. Warum soll es so schrecklich sein 10 Zeilen Code in einer ASPX Seite oder Silverlight Code behind Datei zu haben?

Gar nichts. Wer das so machen möchte kann meist sehr sauberen Code schreiben den andere auch pflegen können. PUNKT.

Trotzdem muss jeder Entwickler mit ein wenig Erfahrung seine Optionen kennen. Und da ist der7das MVVM (Model View ViewModel) Design Pattern eben dabei. Ich will mich auch nicht auf Entwurfsmuster versteifen, nur so viel. Auch Patterns regeln das Leben des Entwicklers nicht. Es ist vielmehr eine gemeinsame Kurzsprache die vieles offen lässt.

Mein aktuelles Silverlight Beispiel ist deswegen auf das absolut notwendigste reduziert. Selbst das Model entfällt (also nur VVM). Üblicherweise gehts im Model um Daten. Brauchen wir für die Basics nicht. Wir werden ein UI schreiben, das keinen Code enthält und eine Klasse (das Viewmodel) die die Jobs erledigt. Die UI hat einen Textbox und einen Button. Bei Click auf den Button wird der Inhalt der Textbox per Messagebox angezeigt.

Meine Klasse wird eine Eigenschaft bekommen und einen Button, der eine Methode aufruft. Die Property die später dem Button zugeordnet wird, muss seit Silverlight 4 das Interface ICommand beinhalten. Das ist eben so. Andere MVxxx Frameworks lösen das durchaus anders.

Public Class page60viewmodel
       Public Property Button1() As ICommand

Nun beginnt der eigentliche Trick. In XAML kann man sehr leicht Objekte deklarativ instanzieren. Dies ist auch ein Superfeature, das ich gerne und dauernd benutze.

xmlns:local="clr-namespace:KursSL">
<UserControl.DataContext>
     <local:page60viewmodel/>
</UserControl.DataContext>

Das ist nur eine mögliche Deklaration. Man sieht auch öfter das man das in einem Resources Element mit einem Key erledigt und dann das DataContext Attribut zuweist. Es spielt auch keine Rolle auf welcher Hierarchie Ebene man das tut. Üblicherweise ganz oben per Layoutroot oder wie hier gleich im Usercontrol. Und jetzt kann man sehr lässig per Command Attribut an die Eigenschaft Button1 binden.

<Button Content="Button" 
                Command="{Binding Button1}"

Das entspricht klassischem Databinding in WPF und Silverlight.

Jetzt stellt sich die berechtigte Frage: wo schreibe ich den Code hin, der ausgeführt werden soll? Dazu geht's wieder ab in die Klasse (Viewmodel). Im Konstruktor wird das Event registriert, mit dem Delegate Event. In anderen Frameworks kommen andere Methoden als ActionCommand zur Anwendung. Bei Verwendung von PRISM (eine optionale Bibliothek für WPF und SL) muss ein DelegateCommand erzeugt werden.

 Public Sub New()
        Me.Button1 = New ActionCommand(AddressOf pressButton1)
 End Sub

Also schreiben wir ein ganz wenig Code für den eigentlichen Button Click Event.

Private Sub pressButton1(ByVal obj As Object)
        MessageBox.Show("presses" + ????)
End Sub

Damit ist das implementieren eines Commands auch fertig. Ein wichtiger Teil fehlt noch. Wie kommen die UI Daten an die Stelle der drei Fragezeichen. Dafür bekommt die Klasse eben noch eine Eigenschaft. Die Eigenschaft wird dann wieder mit der Binding Syntax im UI an die Eigenschaft der Textbox gebunden. Auch das hat nichts mit Entwurfsmustern zu tun. Das ist seit den Anfängen von WPF so.

Hier also die komplette Seite in XAML

<UserControl x:Class="KursSL.page60"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"
             xmlns:local="clr-namespace:KursSL">
    <UserControl.DataContext>
        <local:page60viewmodel/>
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Button" Height="35"
 HorizontalAlignment="Left" Margin="114,82,0,0" 
                Command="{Binding Button1}"
                VerticalAlignment="Top" Width="136" />
        <TextBox Height="42" HorizontalAlignment="Left" Margin="38,24,0,0" 
Text="{Binding Wert,Mode=TwoWay}" 
VerticalAlignment="Top" Width="212" />
    </Grid>
</UserControl>

Nicht schön aber tut. Ist doch einfach oder? ( Bei nein, nochmal von vorne zu lesen beginnen)

Wieder zurück in der Klasse. Um zwischen der Eigenschaft und der Textbox eine richtig dicke Verbding herzustellen wird Silverlight typisch das Interface INotifyPropertyChanged implementiert. Wäre in diesem Beispiel nicht wirklich nötig, betrachte ich aber als essentiell. Dadurch wird ein Event System etabliert, das bei einer Änderung der Daten allen gebunden Controls mitteilt, das sie die Anzeige aktualisieren sollen. Das ist in asynchronen Szenarien, in denen Silverlight läuft unbedingt nötig. Ein Datagrid das an eine Liste gebunden wird, zeigt die Daten erst an, wenn ein Web Service geantwortet hat. Und mit diesem Binding Mechanismus muss sich der Entwickler nicht ums Timing kümmern. Wenn sich also der Wert ändert, teilt der Setter dem EventSystem mit, welchen Namen (Achtung Case Sensitive Zeichenkette) diese Variable hat. Hier nun das komplette Beispiel.

Imports Microsoft.Expression.Interactivity.Core
Imports System.ComponentModel
Public Class page60viewmodel
    Implements INotifyPropertyChanged
    Public Event PropertyChanged(ByVal sender As Object, 
ByVal e As System.ComponentModel.PropertyChangedEventArgs)
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Public Sub New() Me.Button1 = New ActionCommand(AddressOf pressButton1) End Sub Public Property Button1() As ICommand Private _Wert As String Public Property Wert() As String Get Return _Wert End Get Set(ByVal value As String) _Wert = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Wert")) End Set End Property Private Sub pressButton1(ByVal obj As Object) MessageBox.Show("presses" + Wert) End Sub End Class

Nun kann man sich fragen ob das mit einem einfachen Dreizeiler nicht auch funktioniert. Natürlich. Das ist auch der Haupt Kritik Punkt an MVVM. Der Coding und Logik Aufwand können erheblich steigen. Um diesen Aufwand wieder zu reduzieren, gibt es eben Frameworks wie PRSIM oder MVVMlight.

Meine persönliche Meinung dazu. Code im User Interface ist nicht per se zu verteufeln. Aber es ist Deine Entscheidung. Ich hoffe ich konnte ein wenig Licht ins dunkel bringen.

Ergänzend: man kann Commands auch mit noch weniger Aufwand verwenden. Dazu gibt es einen Artikel hier im Blog.

Kommentare (1) -

Sim
12.01.2012 06:44:24 #

Hi,

selten habe ich so sehr gelacht wie heute! Dein Beitrag hat mir aus der Seele gesprochen!!! Smile Ich habe alles durch, vorwärts und rückwärts, viel Code für Kleinigkeiten - Hauptsache MVVM-treu! Wenn einem MVVM so sehr ans Herz gelegt wird, wieso werden die Systeme nicht gleich von Haus aus mit allen Mechanismen ausgestattet, die man dafür benötigt? Einen Trigger für Events, der dann noch als EventToCommand schliesslich im Viewmodel landet und dort dann seine erhoffte Wirkung erzielt, benötigt Menschen, die mindestens 2 Glas Ouzo hinter sich haben und Spass am Codieren und Selbstverausgabung haben!
Nein, ich bin kein Gegner von MVVM, aber ich frage mich, ob in Zeiten, wo z.B. IPhone meine (gesprochene!) Frage nach dem Wetter beantworten kann, solche Pirouetten im Coding wirklich noch notwendig sind, nur um "cleane", wartungsfreundliche Programme zu erzeugen.

MVVM ist ein Weg aus dem Dilemma, sich mit Spaghetti-Code und -Design herum schlagen zu müssen, keine Frage... HAL war da schon weiter: Was kann ich für dich tun? - Gib mir eine Verbindung zum Planeten Erde... und schwupp... das Programm lief Smile

Wo ist der Spruch "Information on your Fingertip" von Bill Gates wahr geworden? Nirgendwo - wir Programmierer sitzen immer noch da und müssen jeden Schritt eintippseln, um dann stolz zeigen zu dürfen, was der Computer heutzutage alles kann...

Mit einem netten Grinsen.. you made my day with your blog-entry Smile

Sim

Pingbacks and trackbacks (1)+

Kommentare sind geschlossen