Wenn es etwas gibt, dass ich bei der Nutzung von Xaml besonders mag, dann ist es die strikte Trennung zwischen Logik und Darstellung. Alles was zur Darstellung gehört, wird in Xaml geschrieben, die Logik wiederum findet sich im testbaren ViewModel. Dies erlaubt eine einfache Trennung der Verantwortlichkeiten und eine Parallelisierung der Arbeit, weil Designer und Entwickler unabhängig von einander arbeiten.

Damit der Designer dabei auch wirklich unabhängig vom Entwickler, nicht aber an diesem vorbei, arbeitet, sollten sich beide auf eindeutige Schnittstellen einigen. Das Wort „Schnittstelle“ kann dabei ruhig wörtlich genommen werden. Denn wenn sich beide zum Beispiel auf ein Interface für ein ViewModel einigen, kann es anschließend zwei dieser ViewModels geben. Eines, das nur die Logik für die Anzeige enthält und eines mit der tatsächlichen Logik die zur Laufzeit gebraucht wird.

public interface IPersonViewModel
{
   string Name { get; }
}

Schreibt das Interface also bestimmte Daten vor, die in der UI gebaucht werden, kann das DesignerViewModel diese ganz simpel bereit stellen. Dabei ist ein großer Vorteil gegenüber einen ViewModel, das sowohl im Designer als auch live genutzt werden soll, dass die Designdaten zu keiner „Verschmutzung“ der eigentlich Produktivlogik führen und jene dadurch ggf. unübersichtlich macht.

internal class PersonDesignerViewModel
{
   string Name { get{ return "Peter"; } }
}

Um nun dieses DesignerViewModel zur Designtime zu instanziieren. Muss nicht einmal der Konstruktor der View angepasst werden. Es reicht wenn man in den Header der View folgendes Attribut einfügt. Dadurch wird das ViewModel direkt instaziiert.

d:DataContext=“{d:DesignInstance personHandling:PersonDesignerViewModel}“

Eine kleine Gefahr besteht nun noch. Und zwar könnten ViewModel und DesignerViewModel auseinander laufen. Um dies zu vermeiden empfehle ich in den Unit Tests statt der entsprechenden Klasse, immer das Interface des ViewModels als Typ des System under Test zu verwenden. Alle Änderungen schlagen sich dann direkt auf beide Klassen nieder, wodurch eine Änderung in den Tests auch Änderungen an beiden Klassen nach sich zieht.

[TestMethod]
public void PersonDataMustBeProvided()
{
   IPersonViewModel sut = PersonViewModel();
   Assert.IsNotNull(sut.Name);
}