SlideShare ist ein Scribd-Unternehmen logo
1 von 107
Was ist MVVM?
ModelView
View ViewModel Model
DataBinding
View ViewModel Model
DataBinding
XAML C#/VB SQL/WSDL
JSON/XML
ViewModel
DataBinding
ViewModel
DataBinding
ViewModel
ViewModel
Tests
ViewModel
ViewModel
Model
DataBinding
ViewModelDataBinding
„Entity
ViewModel“
ViewModel Basisklassen
SELBST GEBAUT
Eigene ViewModel Basisklasse
In aller Regel benötigt man eine Implementierung von INotifyProperty Changed.
Durch das CallerMemberName Attribut kann man sich viel Arbeit sparen.
ViewModel Basisklassen
MVVM LIGHT
ObservableObject
Basisklasse für alle Klassen die über Änderungen an ihren Eigenschaften informieren wollen.
ViewModelBase
Erbt von ObserableObject.
Kann ermitteln ob es sich im Designmodus befindet oder nicht.
Bietet Zugriff auf einen Messenger um Änderungen auch anderen ViewModels bekannt zu
machen.
Commands
ICommand
Aktionen werden in Form von Objekten gekapselt und können, verbunden mit ihren
Statusinformationen, vielfach weiter verwendet werden.
public interface ICommand
{
bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Delegate
Command
Implementiert ICommand.
Die Überprüfung von
„CanExecute“ muss „per Hand“
getriggert werden.
Kann Performancevorteile zur
Laufzeit haben, da weniger Events
auftreten.
RelayCommands
Implementiert ICommand
Im Gegensatz zum DelegateCommand wird CanExecute
immer dann geprüft wenn sich Änderungen an der UI
ergeben.
D.h. leichte einzusetzen mit höherer Last zur Laufzeit.
Wird von allen MVVM Frameworks bereit gestellt.
CompositeCommands
Erlauben es verschiedene Commands „zeitgleich“ auszulösen.
Aktionen werden nur für registrierte Commands ausgeführt deren CanExecute true zurück gibt.
Wird durch Prism bereitgestellt.
var compositeCommand = new CompositeCommand();
var updateCommand = new DelegateCommand(Update).ObservesCanExecute((vm) => vm.WasChanged);
compositeCommand.Register(updateCommand);
var notificationCommand = new DelegateCommand(Notify).ObservesCanExecute((vm) =>
vm.RaiseNotification);
compositeCommand.Register(notificationCommand);
Property based Commands
Werden automatisch ausgeführt wenn sich eine Property verändert.
Das Command registriert sich für PropertyChanged der Property und führ die ExecuteMethode
aus wenn eine Änderung vorliegt.
Erlaubt bspw. die Umsetzung komplexer „CalculatedProperties“.
Wird von Prism bereitgestellt.
var command = new DelegateCommand(x => DoSomething(x)).ObservesProperty(() => this.Title);
Daten zur Design Zeit
verwenden
OHNE FRAMEWORKS
Referenzierung in der View
1. Im Konstruktor auf den DataContext schreiben
◦ Einfach aber fehleranfällig
2. Im Xaml für den DesignTime Data Context registrieren
◦ Weniger fehleranfällig
◦ Macht zwei Registrierungsverfahren notwendig -> eins für Design- und eines für Laufzeit
Mehrere Konstruktoren
Man nutzt zwei Konstruktoren, einen mit und einen ohne Parameter.
Der IoC Container bedient nur den mit Parametern -> Somit ist man im Designer aktiv wenn der
parameterlose Konstruktor aufgerufen wurde.
Mehrere Konstruktoren
Man nutzt zwei Konstruktoren, einen mit und einen ohne Parameter.
Der IoC Container bedient nur den mit Parametern -> Somit ist man im Designer aktiv wenn der
parameterlose Konstruktor aufgerufen wurde.
Vorteile
- Sehr einfach umzusetzen.
- Der Aufwand bei der Synchronisierung ist recht einfach.
Nachteile
- Es kann schnell zu Fehlern kommen weil schwergewichtige Aktionen zur Designzeit ausgelöst
werden.
Mehrere Klassen
Man nutzt zwei Klassen. Eine für die Realimplementierung und eine für die Designdaten.
Im Designer wird nur die Designvariante verwendet.
Vorteile
- Eindeutige Trennung der Belange.
- Es können nicht versehentlich aufwändige Aktionen im Designer ausgeführt werden.
Nachteile
- Die Synchronisation beider ViewModel kann schwierig sein.
Daten zur Design Zeit
verwenden
MIT MVVM LIGHT
Designzeit Daten mit MVVM Light
ViewModelBase bietet eine Eigenschaft IsInDesignMode mit der überprüft wird ob man sich im
Designmodus befindet.
Designzeit Daten mit MVVM Light
ViewModelBase bietet eine Eigenschaft IsInDesignMode mit der überprüft wird ob man sich im
Designmodus befindet.
Dependency Injection geht in diesem Fall nicht und führt zu einer Exception im Designer.
Demnach braucht man mindestens 2 Konstruktoren.
Daten für das Design
ObjectFiller.Net
Behaviours und Trigger
Event to Command Binding
Üblicherweise lassen sich Events nicht and Commands binden.
Mit Interaction Triggers und Behaviours kann man dies umgehen.
Behaviours können selbst erstellt oder in Kombination mit Blend verwendet werden.
InteractionTrigger stammen aus System.Windows.Interactivity.
<i:Interaction.Triggers>
<i:EventTrigger>
<i:InvokeCommandAction Command="{Binding InitializationCommand, Mode=OneTime}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Event to Command Binding
MVVM Light enthält ein entsprechendes Behaviour.
<i:Interaction.Triggers>
<i:EventTrigger>
<Custom:EventToCommand Command="{Binding InitializationCommand, Mode=OneTime}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
xmlns:Custom="http://www.galasoft.ch/mvvmlight"
Attached Properties
Attached Properties & Behaviors
Sind DependencyProperties mit denen man Elemente im Xaml um Informationen erweitern
kann.
Löst das setzen der Daten auch Aktionen aus, handelt es sich um attached Behaviours.
Attached
Properties &
Behaviors
Das zusätzliche Verhalten kann durch
den Changed-Eventhandler der
Property angehangen bzw. ausgelöst
werden (blaue Pfeile).
Wichtig: Es handelt sich nicht um
„normale“ Dependency Properties. Sie
werden als attached registriert (roter
Pfeil).
Behaviors
MIT SYSTEM.WINDOWS.INTERACTIVITY
Interactivity
Behaviors
Durch die Basisklasse Behavior wird
genau angegeben welche Funktionen
auf welchen Typ angewendet werden
können.
Dies spart viel Code, ist typsischer und
leichter verständlich als Attached
behaviors.
<TextBox Text="{Binding Title}">
<i:Interaction.Behaviors>
<MVVM:SpecialCharacterFilterBehavior />
</i:Interaction.Behaviors>
</TextBox>
ViewModelDiscovery
Dependency
Injection
Die View fordert ihr ViewModel
als Constructor Parameter an und
weißt sie selbst ihrem DataContext
zu.
Per Template
View Discovery per Template
Bietet sich vor allem für EntityViewModels an.
Die View wird als Control oder DataTemplate für einen Typ registriert. Dadurch erfolgt die
Zuweisung zur View automatisch.
Durch Template Selector kann man sehr einfach das Template anhand des Status des
ViewModels umschalten.
View Discovery per Template
Hat man mehrere Typen die unterschiedlich dargestellt werden soll, gibt man die DataTemplates
mit entsprechender Typedefinition als Ressource an. Die Auswahl geschieht dann automatisch
anhand des Typs.
<ContentControl Content="{Binding SelectedPublication}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewModels:PublicationEntityViewModel}">
<local:PublicationDetailsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:MediumEntityViewModel}">
<local:MediumDetailsView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
ViewModel Locator
IN PRISM
ViewModel Locator in Prism
Nutzt eine Attached Property.
Sucht im Namespace „ViewModels“ nach einem ViewModel, dass so heißt wie die View.
◦ Bspw: PublicationView braucht ein PublicationViewModel
Die Instanziierung erfolgt per IoC Container sobald eine Navigation mit dem RegionManager
erfolgt.
mvvm:ViewModelLocator.AutoWireViewModel="True"
Der Bootstrapper
Bootstrapper?!?
Komponente die der Initialisierung der Anwendung zum Programmstart dient.
Typische Schritte sind:
1. Erstellen des IoC Containers und registrieren aller notwendigen Komponenten.
2. Initialisieren der wichtigsten Infrastruktur Komponenten (ViewModelLocator,
DialogManager, Logger).
3. Erstellen und anzeigen des Hauptfensters.
Initialisieren des IoC Containers
am Beispiel AutoFac
Initialisieren und anzeigen des
Hauptfensters
Registrieren und starten des
Bootstrappings
Entfernen der StartUpUri aus App.xaml und ersetzen durch
einen Eventhandler für OnLoad.
Instanziieren und starten des Bootstrappings.
Prisms Bootstrapper
Quelle: http://msdn.microsoft.com/en-us/library/gg430868(v=pandp.40).aspx
IoC Container
“Hand made IoC”
var parser = new Parser();
var rechner = new Rechner(parser);
var ui = new ConsoleUI(rechner)
ui.Start();
Der Abhängigkeitsgraph
UI Rechner Parser
Der Abhängigkeitsgraph
UI Rechner Parser
Addition
Subtraktion
Der Abhängigkeitsgraph
UI Rechner Parser
Addition
Subtraktion
Console
Der Abhängigkeitsgraph
UI Rechner Parser
Addition
Subtraktion
Console
WPF
Der Abhängigkeitsgraph
UI Rechner Parser
Addition
Subtraktion
Bootstrapper
Console
WPF
Container
Der IoC Container
Parser
Addition
Subtraktion
UI
Rechner
Bootstrapper
Parser
Der IoC Container
Addition
Subtraktion
Rechner
Bootstrapper
UI
Container
UI
http://autofac.org/
• Sehr leistungsfähig und umfangreich.
• Bietet viele verschiedene Lebenszyklen und
Einflussmöglichkeiten bei der Erstellung von
Instanzen.
• Erlaubt XML-Konfiguration.
• Bietet Schnittstellen für einfache Isolation von
Bestandteilen.
• Ist Verfügbar als PCL.
• Ist sehr gut dokumentiert.
AutoFac
http://lightcore.ch/
• Sehr leichtgewichtig und schnell.
• Erlaubte Lebenszyklen:
• Transient
• Singleton
• Bezogen auf HTTP Request
• Bezogen auf Thread
• Nicht verfügbar als PCL.
• Ist gut dokumentiert.
LightCore
http://mef.codeplex.com/
• Ist Teil des .Net Frameworks.
• Bietet die wichtigsten Funktionen zum
Auflösen von Abhängigkeitsgraphen.
• Verwendet häufig Reflection.
• Kein klassischer IoC Container.
• Steht als PCL auch für andere
Anwendungen zur Verfügung.
• Ist sehr gut dokumentiert.
MEF (Managed Extensibility Framework)
https://unity.codeplex.com/
• Wurde von Microsoft Patterns & Practices
entwickelt.
• Bietet viele verschiedene Lebenszyklen und
Einflussmöglichkeiten bei der Instanziierung
von Klassen.
• Erlaubt XML-Konfiguration.
• Erlaubt Aspekt-Orientierte-Programmierung
(AOP)
• Ist verfügbar als PCL.
• Ist sehr gut dokumentiert.
Unity
• Es gibt sehr viele IoC Container am Markt.
• Diese haben einen unterschiedlichen Funktionsumfang und werden
unterschiedlich gut gepflegt.
• Vor dem Einsatz sollte unbedingt geklärt werden, wie umfangreich die
Dokumentation ist und wann das letzte Update veröffentlicht wurde.
• Wenn Geschwindigkeit wichtig ist:
• sollte auf weniger umfangreiche Frameworks zurück gegriffen
• und auf XML Konfigurationen verzichtet werden.
Zusammenfassung
Navigation
Grundkonzept
Es wird ein Interface erstellt über welches die Navigation gesteuert wird.
Eine oder mehrere Regionen in der Applikation werden als Navigationsziele definiert.
Eine Region kann ein ContentControl (zeigt nur eine View zeitgleich an) oder ein ItemsControl
(zeigt mehrere Views zeitgleich an).
Der NavigationService arbeitet sehr eng mit dem Hauptfenster zusammen und muss daher beim
Bootstrapping gesondert initialisiert werden!
MVVM Light INavigationService
public interface INavigationService
{
string CurrentPageKey { get; }
void GoBack();
void NavigateTo(string pageKey);
void NavigateTo(string pageKey, object parameter);
}
Es gibt keine Implementierung für WPF!
Es kann nur eine Region verwaltet werden!
Die Navigation erfolgt im Stil einer „Kioskapp“
Prism RegionManager
Verschiedene Regionen können verwaltet werden.
Es kann geprüft werden ob Navigation möglich ist.
ViewModels können selbst die Navigation verhindern.
Parameterübergabe ist möglich.
Es können jederzeit Regionen registriert und entfernt werden.
Regionen können Views verwalten die selbst Regionen enthalten.
Status basierte
Navigation
Nutzung von DataTemplates
und Data Triggers
Status basierte Navigation ändert die Anzeige anhand des
Status des ViewModels.
Es gibt demnach zwei unterschiedliche Darstellungen für das
gleiche ViewModel.
Dazu werden verschiedene DataTemplates festgelegt und per
DataTrigger, VisualStateManager oder TemplateSelector
zwischen diesen umgeschaltet.
Validierung
Validation Rules
Regelsätze werden eigenständig als Objekte bereit gestellt und an die Controls gebunden.
Sie sind somit nicht mehr Teil des ViewModels sondern liegen in der Verantwortung der View.
ValidationRules erben von der Klasse ValidationRule.
public class NotNullOrEmptyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return new ValidationResult(false, $"No value was set for {PropertyName}.");
}
return ValidationResult.ValidResult;
}
public string PropertyName { get; set; }
}
Validation Rules
Regelsätze werden eigenständig als Objekte bereit gestellt und an die Controls gebunden.
Sie sind somit nicht mehr Teil des ViewModels sondern liegen in der Verantwortung der View.
ValidationRules müssen als embedded collection im Xaml angegeben werden.
Sie haben nur sehr wenig Informationen über ihren Kontext!
<TextBox>
<TextBox.Text>
<Binding Path="Title" UpdateSourceTrigger="PropertyChanged" >
<Binding.ValidationRules>
<MVVM:NotNullOrEmptyValidationRule PropertyName="Title" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Validierung mit Exceptions
Validierung geschieht auf Basis eines Bindings.
Wird eine Exception geworfen, gilt die Validierung automatisch als fehlgeschlagen.
Es handelt sich um eine sehr teure Art der Validierung und sollte daher möglichst vermieden
werden.
<TextBoxText="{Binding PublicationDate,
ValidatesOnExceptions=True,
UpdateSourceTrigger=PropertyChanged}" />
IDataErrorInfo
Definiert Eigenschaften um Fehlermeldungen für einzelne Properties oder das gesamte Objekt
abzurufen.
/// <summary>Provides the functionality to offer custom error information that a user interface can bind to.</summary>
public interface IDataErrorInfo
{
/// <summary>Gets the error message for the property with the given name.</summary>
/// <param name="columnName">The name of the property whose error message to get. </param>
/// <returns>The error message for the property. The default is an empty string ("").</returns>
string this[string columnName] { get; }
/// <summary>Gets an error message indicating what is wrong with this object.</summary>
/// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
string Error { get; }
}
IDataErrorInfo
Um Validierungen vorzunehmen und einen Validierungsrahmen zu erhalten, muss beim Binding
ValidatesOnDataErrors auf True gesetzt werden.
<TextBox Text="{Binding Title, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
public string this[string columnName]
{
get
{
if (columnName == GetPropertyName(() => Title) && string.IsNullOrEmpty(Title))
{
return "No title was given.";
}
return string.Empty;
}
}
Kommt aus dem ObserveableObject von
MVVM Light
IDataErrorInfo
Vorteile:
Integriert in WPF wodurch Validierungshinweise direkt an den Fehlerstellen angezeigt werden.
Nachteile:
Hat eine Property keinen Wert, wird keine Validierung vorgenommen!
Die Validierung geschieht synchron zur Eingabe, man kann sie nur sehr schwer steuern.
Properties werden schrittweise validiert, was bei Calculated Properties zu erheblichen
Problemen führen kann.
DataAnnotations
Über Annotationen kann für jede Property angegeben werden wie sie validiert werden soll.
Diese Annotations sind in System.ComponentModel.DataAnnotations definiert. Dazu muss diese
Dll referenziert werden!
Es gibt eine sehr große Zahl unterschiedlicher Annotationen.
[Required(AllowEmptyStrings = false, ErrorMessage = "Please enter valid title.")]
public string Title
{
get { return publication.Title; }
set { publication.Title = value; }
}
DataAnnotations
Die Auswertung kann im Rahmen von IDataErrorInfo mit Hilfe der Validator-Klasse für jede
Property geschehen.
protected virtual string Validate(string propertyName)
{
var error = string.Empty;
var value = GetPropertyValue(propertyName, this);
var results = new List<ValidationResult>(1);
var context = new ValidationContext(this, null, null) { MemberName = propertyName };
var result = Validator.TryValidateProperty(value, context, results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
SetError(propertyName, error);
return error;
}
DataAnnotations
Über die Validator-Klasse kann aber auch ein komplettes Objekt Validiert werden.
public virtual bool Validate(object instance)
{
var validationContext = new ValidationContext(instance);
var validationResults = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(instance, validationContext, validationResults);
SetErrors(validationResults);
return isValid;
}
INotifyDataErrorInfo
Definiert Eigenschaften um Validierung asynchron vorzunehmen.
Statt, dass direkt validiert wird, werden Fehler erst nach einem Event angezeigt.
Pro Property sind mehrere Fehlermeldungen möglich.
public interface INotifyDataErrorInfo
{
/// <summary>Gets a value that indicates whether the entity has validation errors. </summary>
/// <returns>true if the entity currently has validation errors; otherwise, false.</returns>
bool HasErrors { get; }
/// <summary>Gets the validation errors for a specified property or for the entire entity.</summary>
/// <returns>The validation errors for the property or entity.</returns>
IEnumerable GetErrors(string propertyName);
/// <summary>Occurs when the validation errors have changed for a property or for the entire entity.
event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
INotifyDataErrorInfo
Wenn validiert werden soll, wird zunächst HasErrors geprüft.
Dies triggert einen Zugriff auf GetErrors falls HasErrors true ist.
Mit ErrorsChanged kann der Vorgang indirekt von außen gestartet werden.
public interface INotifyDataErrorInfo
{
/// <summary>Gets a value that indicates whether the entity has validation errors. </summary>
/// <returns>true if the entity currently has validation errors; otherwise, false.</returns>
bool HasErrors { get; }
/// <summary>Gets the validation errors for a specified property or for the entire entity.</summary>
/// <returns>The validation errors for the property or entity.</returns>
IEnumerable GetErrors(string propertyName);
/// <summary>Occurs when the validation errors have changed for a property or for the entire entity.
event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
<TextBox Text="{Binding Title, ValidatesOnNotifyDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
Validierungsinformationen in Xaml
abrufen
Jedes UI Element hat Zugriff auf Validierungsinformationen.
Dies kann genutzt werden um das Control entsprechend zu stylen.
<TextBox Text="{Binding Title, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ErrorStyle}">
<Style x:Key="ErrorStyle" TargetType="FrameworkElement">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
Der Event Aggregator
Publisher Subscriber
Delegate
MethodeDelegate
Event
Aggregator SubscriberPublisher
Delegate
Delegate
Methode
Event
Aggregator SubscriberPublisher
Publisher Subscriber
Delegate
Delegate
Methode
Methode
Event
Aggregator SubscriberPublisher
Publisher Subscriber
Delegate
Delegate
Methode
Methode
MVVM Frameworks
EIN VERGLEICH
Prism
Catel
Caliburn Micro
MVVM Light Toolkit
Generelle Features
eine Auswahl
Prism Catel Caliburn.Micro MVVM Light
Bootstrapper ✔ ✔ ✔ ✘
Modulverwaltung ✔ ✘ ✘ ✘
View Composition ✔ ✘* ✘* ✘
Konvention basierende Konfiguration ✔ ✔ ✔ ✘
Event Aggregator ✔ ✔ ✔ ✔
RelayCommand / DelegateCommand ✔ ✔ ✔ ✔
EventToCommand trigger ✘* ✔ ✔ ✔
IoC Container Support ✔ ✔ ✔ ✔
Eigener IoC Container ✘* ✔ ✔ ✔
View Model Base
Prism Catel Caliburn.Micro MVVM Light
INotifyPropertyChanged ✔ ✔ ✔ ✔
INotifyPropertyChangeing ✘ ✔ ✔ ✔
IDataErrorInfo ✘ ✔ ✘ ✘
IEditableObject ✘ ✔ ✘ ✘
Serialisierung der Daten ✘ ✔ ✘ ✘
Methoden Mapping ✘ ✘ ✔ ✘
Property Mapping ✘ ✔ ✔ ✘
View Handling
Prism Catel Caliburn.Micro MVVM LIght
View Composition ✔ ✘* ✘ ✘
Navigation zwischen Views ✔ ✔ ✔ ✘*
Informationen über Navigation ✔ ✔* ✘ ✘
Verhindern von Navigation ✔ ✘ ✘ ✘
ViewModels für Sub Views ✘ ✔ ✘ ✘
Lazy loading von View Models ✔ ✔ ✘ ✘
Events wenn ein View geladen wurde ✘ ✔ ✘ ✘
Debugging
Logging mit
Visual Studio
In den Debugoptionen kann
eingestellt werden, welche Dinge
während des Debugs in die
OutputView ausgegeben werden.
Debugging mit PresentationTraceSources
WPF ermöglicht das „Logging“ in die Console über attached Properties.
Logging wird nur auf bestimmte Elemente angewendet.
Dabei steht der genaue Wert für komplexe Datentypen jedoch nicht zur Verfügung!
<TextBox Text="{Binding PublicationDate, PresentationTraceSources.TraceLevel=High}" />
Debugging mit
PresentationTraceSources
WPF ermöglicht das „Logging“ in die
Console über attached Properties.
Weitere Senken können gesondert
konfiguriert werden.
Debugging mit
Convertern
Problem
Man kann nicht ohne weiteres in Xaml
debuggen oder loggen.
Lösung
Man definiert einen Converter der C#
Code in das Xaml „injiziert“.
Dependency Properties
Dependency Properties
Nutzt man wenn:
- Man eine Eigenschaft gegen andere Eigenschaften binden möchte.
- Wenn Animationen umgesetzt werden sollen.
- Wenn sie in Styles berücksichtigt werden sollen. Bspw. DataTrigger
- Wenn sie mit einer Ressource versehen werden sollen.
Dependency Properties
public static readonly DependencyProperty SetTextProperty =
DependencyProperty.Register("SetText", typeof(string), typeof(MyObject), new
PropertyMetadata("", new PropertyChangedCallback(OnSetTextChanged)));
public string SetText
{
get { return (string)GetValue(SetTextProperty); }
set { SetValue(SetTextProperty, value); }
}
Dependency Properties werden üblicherweise auf DependencyObjects und damit in
CustomControls oder UserControls verwendet.
Namenspattern: [Name]Property
Der Tatsächliche Name wird bei der Registrierung als Parameter angegeben. Die Werte werden
über Get und Set des DependencyObjects gesetzt.

Weitere ähnliche Inhalte

Ähnlich wie MVVM mit WPF

Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJSSebastian Springer
 
PHP-Module in statischen Seiten - Architektur-Ansätze
PHP-Module in statischen Seiten - Architektur-AnsätzePHP-Module in statischen Seiten - Architektur-Ansätze
PHP-Module in statischen Seiten - Architektur-AnsätzeRalf Lütke
 
2009 03 17 Spring101
2009 03 17 Spring1012009 03 17 Spring101
2009 03 17 Spring101gueste4be40
 
Zend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesZend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesRalf Eggert
 
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtAutomatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtOPITZ CONSULTING Deutschland
 
Magento: Event/Observer
Magento: Event/ObserverMagento: Event/Observer
Magento: Event/Observertherouv
 
Präsentation webservices
Präsentation webservicesPräsentation webservices
Präsentation webservicesxxtesaxx
 
Einführung in Windows Presentation Foundation
Einführung in Windows Presentation FoundationEinführung in Windows Presentation Foundation
Einführung in Windows Presentation Foundationchmoser79
 
GWT Introduction
GWT IntroductionGWT Introduction
GWT Introductionpfleidi
 
Knockout.js
Knockout.jsKnockout.js
Knockout.jsdevtyr
 
Refactoring Rails Applications
Refactoring Rails ApplicationsRefactoring Rails Applications
Refactoring Rails ApplicationsJonathan Weiss
 
Automatischer Build mit Maven
Automatischer Build mit MavenAutomatischer Build mit Maven
Automatischer Build mit MavenStefan Scheidt
 

Ähnlich wie MVVM mit WPF (20)

Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJS
 
CDI
CDICDI
CDI
 
PHP-Module in statischen Seiten - Architektur-Ansätze
PHP-Module in statischen Seiten - Architektur-AnsätzePHP-Module in statischen Seiten - Architektur-Ansätze
PHP-Module in statischen Seiten - Architektur-Ansätze
 
2009 03 17 Spring101
2009 03 17 Spring1012009 03 17 Spring101
2009 03 17 Spring101
 
Zend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesZend Framework 2 - Best Practices
Zend Framework 2 - Best Practices
 
GWT – Google Web Toolkit in der Praxis
GWT – Google Web Toolkit in der PraxisGWT – Google Web Toolkit in der Praxis
GWT – Google Web Toolkit in der Praxis
 
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan ScheidtAutomatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
Automatischer Build mit Maven - OPITZ CONSULTING - Stefan Scheidt
 
Magento: Event/Observer
Magento: Event/ObserverMagento: Event/Observer
Magento: Event/Observer
 
Feature Flags mit Togglz
Feature Flags mit TogglzFeature Flags mit Togglz
Feature Flags mit Togglz
 
Beschreibung
BeschreibungBeschreibung
Beschreibung
 
Schnelleinstieg in Angular
Schnelleinstieg in AngularSchnelleinstieg in Angular
Schnelleinstieg in Angular
 
Elsholz stoll js_03_10
Elsholz stoll js_03_10Elsholz stoll js_03_10
Elsholz stoll js_03_10
 
Präsentation webservices
Präsentation webservicesPräsentation webservices
Präsentation webservices
 
Einführung in Windows Presentation Foundation
Einführung in Windows Presentation FoundationEinführung in Windows Presentation Foundation
Einführung in Windows Presentation Foundation
 
Iks Sonderdruck Eclipse Magazin Mai 2010: Automatisierte SWTBot-Tests
Iks Sonderdruck Eclipse Magazin Mai 2010: Automatisierte SWTBot-TestsIks Sonderdruck Eclipse Magazin Mai 2010: Automatisierte SWTBot-Tests
Iks Sonderdruck Eclipse Magazin Mai 2010: Automatisierte SWTBot-Tests
 
GWT Introduction
GWT IntroductionGWT Introduction
GWT Introduction
 
Knockout.js
Knockout.jsKnockout.js
Knockout.js
 
Refactoring Rails Applications
Refactoring Rails ApplicationsRefactoring Rails Applications
Refactoring Rails Applications
 
Test-Automation mit Selenium WebDriver - ein Artikel der iks im dotnetpro
Test-Automation mit Selenium WebDriver - ein Artikel der iks im dotnetproTest-Automation mit Selenium WebDriver - ein Artikel der iks im dotnetpro
Test-Automation mit Selenium WebDriver - ein Artikel der iks im dotnetpro
 
Automatischer Build mit Maven
Automatischer Build mit MavenAutomatischer Build mit Maven
Automatischer Build mit Maven
 

Mehr von Hendrik Lösch

Why (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyWhy (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyHendrik Lösch
 
We (don't) need a software architect!?!
We (don't) need a software architect!?!We (don't) need a software architect!?!
We (don't) need a software architect!?!Hendrik Lösch
 
Restrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationRestrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationHendrik Lösch
 
Vom Monolith zum Modulith
Vom Monolith zum ModulithVom Monolith zum Modulith
Vom Monolith zum ModulithHendrik Lösch
 
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungDer Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungHendrik Lösch
 
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als SoftwarearchitektHendrik Lösch
 
Software ist was du draus machst!
Software ist was du draus machst!Software ist was du draus machst!
Software ist was du draus machst!Hendrik Lösch
 
Einstieg in das Vueniverse
Einstieg in das VueniverseEinstieg in das Vueniverse
Einstieg in das VueniverseHendrik Lösch
 
Survivalkit für Codehausmeister
Survivalkit für CodehausmeisterSurvivalkit für Codehausmeister
Survivalkit für CodehausmeisterHendrik Lösch
 
Confessions of a Codehausmeister
Confessions of a CodehausmeisterConfessions of a Codehausmeister
Confessions of a CodehausmeisterHendrik Lösch
 
Clean mit visual studio
Clean mit visual studioClean mit visual studio
Clean mit visual studioHendrik Lösch
 
Advanced Refactoring Patterns
Advanced Refactoring PatternsAdvanced Refactoring Patterns
Advanced Refactoring PatternsHendrik Lösch
 
Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Hendrik Lösch
 
Der Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteDer Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteHendrik Lösch
 

Mehr von Hendrik Lösch (20)

Why (most) softwareprojects fail silently
Why (most) softwareprojects fail silentlyWhy (most) softwareprojects fail silently
Why (most) softwareprojects fail silently
 
We (don't) need a software architect!?!
We (don't) need a software architect!?!We (don't) need a software architect!?!
We (don't) need a software architect!?!
 
Restrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen GroßapplikationRestrukturierung einer industriellen Großapplikation
Restrukturierung einer industriellen Großapplikation
 
Vom Monolith zum Modulith
Vom Monolith zum ModulithVom Monolith zum Modulith
Vom Monolith zum Modulith
 
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die ArchitekturbewertungDer Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
Der Software auf den Zahn gefühlt - Einstieg in die Architekturbewertung
 
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
„Wie reden Sie denn mit mir?!?“ – Stakeholder überzeugen als Softwarearchitekt
 
Software ist was du draus machst!
Software ist was du draus machst!Software ist was du draus machst!
Software ist was du draus machst!
 
Modular mit .NET
Modular mit .NETModular mit .NET
Modular mit .NET
 
.NET zu .NET Core
.NET zu .NET Core.NET zu .NET Core
.NET zu .NET Core
 
Workshop Vue js
Workshop Vue jsWorkshop Vue js
Workshop Vue js
 
Migrationsstrategien
MigrationsstrategienMigrationsstrategien
Migrationsstrategien
 
Einstieg in das Vueniverse
Einstieg in das VueniverseEinstieg in das Vueniverse
Einstieg in das Vueniverse
 
Survivalkit für Codehausmeister
Survivalkit für CodehausmeisterSurvivalkit für Codehausmeister
Survivalkit für Codehausmeister
 
Confessions of a Codehausmeister
Confessions of a CodehausmeisterConfessions of a Codehausmeister
Confessions of a Codehausmeister
 
Hey, wie geht es dir?
Hey, wie geht es dir?Hey, wie geht es dir?
Hey, wie geht es dir?
 
Clean mit visual studio
Clean mit visual studioClean mit visual studio
Clean mit visual studio
 
Advanced Refactoring Patterns
Advanced Refactoring PatternsAdvanced Refactoring Patterns
Advanced Refactoring Patterns
 
Codesmells
CodesmellsCodesmells
Codesmells
 
Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018Advanced Refactoring Patterns - Dev Day 2018
Advanced Refactoring Patterns - Dev Day 2018
 
Der Healthcheck für Softwareprojekte
Der Healthcheck für SoftwareprojekteDer Healthcheck für Softwareprojekte
Der Healthcheck für Softwareprojekte
 

MVVM mit WPF

  • 4. View ViewModel Model DataBinding XAML C#/VB SQL/WSDL JSON/XML
  • 13. Eigene ViewModel Basisklasse In aller Regel benötigt man eine Implementierung von INotifyProperty Changed. Durch das CallerMemberName Attribut kann man sich viel Arbeit sparen.
  • 15. ObservableObject Basisklasse für alle Klassen die über Änderungen an ihren Eigenschaften informieren wollen.
  • 16. ViewModelBase Erbt von ObserableObject. Kann ermitteln ob es sich im Designmodus befindet oder nicht. Bietet Zugriff auf einen Messenger um Änderungen auch anderen ViewModels bekannt zu machen.
  • 18. ICommand Aktionen werden in Form von Objekten gekapselt und können, verbunden mit ihren Statusinformationen, vielfach weiter verwendet werden. public interface ICommand { bool CanExecute(object parameter); void Execute(object parameter); event EventHandler CanExecuteChanged; }
  • 19. Delegate Command Implementiert ICommand. Die Überprüfung von „CanExecute“ muss „per Hand“ getriggert werden. Kann Performancevorteile zur Laufzeit haben, da weniger Events auftreten.
  • 20. RelayCommands Implementiert ICommand Im Gegensatz zum DelegateCommand wird CanExecute immer dann geprüft wenn sich Änderungen an der UI ergeben. D.h. leichte einzusetzen mit höherer Last zur Laufzeit. Wird von allen MVVM Frameworks bereit gestellt.
  • 21. CompositeCommands Erlauben es verschiedene Commands „zeitgleich“ auszulösen. Aktionen werden nur für registrierte Commands ausgeführt deren CanExecute true zurück gibt. Wird durch Prism bereitgestellt. var compositeCommand = new CompositeCommand(); var updateCommand = new DelegateCommand(Update).ObservesCanExecute((vm) => vm.WasChanged); compositeCommand.Register(updateCommand); var notificationCommand = new DelegateCommand(Notify).ObservesCanExecute((vm) => vm.RaiseNotification); compositeCommand.Register(notificationCommand);
  • 22. Property based Commands Werden automatisch ausgeführt wenn sich eine Property verändert. Das Command registriert sich für PropertyChanged der Property und führ die ExecuteMethode aus wenn eine Änderung vorliegt. Erlaubt bspw. die Umsetzung komplexer „CalculatedProperties“. Wird von Prism bereitgestellt. var command = new DelegateCommand(x => DoSomething(x)).ObservesProperty(() => this.Title);
  • 23. Daten zur Design Zeit verwenden OHNE FRAMEWORKS
  • 24. Referenzierung in der View 1. Im Konstruktor auf den DataContext schreiben ◦ Einfach aber fehleranfällig 2. Im Xaml für den DesignTime Data Context registrieren ◦ Weniger fehleranfällig ◦ Macht zwei Registrierungsverfahren notwendig -> eins für Design- und eines für Laufzeit
  • 25. Mehrere Konstruktoren Man nutzt zwei Konstruktoren, einen mit und einen ohne Parameter. Der IoC Container bedient nur den mit Parametern -> Somit ist man im Designer aktiv wenn der parameterlose Konstruktor aufgerufen wurde.
  • 26. Mehrere Konstruktoren Man nutzt zwei Konstruktoren, einen mit und einen ohne Parameter. Der IoC Container bedient nur den mit Parametern -> Somit ist man im Designer aktiv wenn der parameterlose Konstruktor aufgerufen wurde. Vorteile - Sehr einfach umzusetzen. - Der Aufwand bei der Synchronisierung ist recht einfach. Nachteile - Es kann schnell zu Fehlern kommen weil schwergewichtige Aktionen zur Designzeit ausgelöst werden.
  • 27. Mehrere Klassen Man nutzt zwei Klassen. Eine für die Realimplementierung und eine für die Designdaten. Im Designer wird nur die Designvariante verwendet. Vorteile - Eindeutige Trennung der Belange. - Es können nicht versehentlich aufwändige Aktionen im Designer ausgeführt werden. Nachteile - Die Synchronisation beider ViewModel kann schwierig sein.
  • 28. Daten zur Design Zeit verwenden MIT MVVM LIGHT
  • 29. Designzeit Daten mit MVVM Light ViewModelBase bietet eine Eigenschaft IsInDesignMode mit der überprüft wird ob man sich im Designmodus befindet.
  • 30. Designzeit Daten mit MVVM Light ViewModelBase bietet eine Eigenschaft IsInDesignMode mit der überprüft wird ob man sich im Designmodus befindet. Dependency Injection geht in diesem Fall nicht und führt zu einer Exception im Designer. Demnach braucht man mindestens 2 Konstruktoren.
  • 31. Daten für das Design
  • 34. Event to Command Binding Üblicherweise lassen sich Events nicht and Commands binden. Mit Interaction Triggers und Behaviours kann man dies umgehen. Behaviours können selbst erstellt oder in Kombination mit Blend verwendet werden. InteractionTrigger stammen aus System.Windows.Interactivity. <i:Interaction.Triggers> <i:EventTrigger> <i:InvokeCommandAction Command="{Binding InitializationCommand, Mode=OneTime}"/> </i:EventTrigger> </i:Interaction.Triggers>
  • 35. Event to Command Binding MVVM Light enthält ein entsprechendes Behaviour. <i:Interaction.Triggers> <i:EventTrigger> <Custom:EventToCommand Command="{Binding InitializationCommand, Mode=OneTime}"/> </i:EventTrigger> </i:Interaction.Triggers> xmlns:Custom="http://www.galasoft.ch/mvvmlight"
  • 37. Attached Properties & Behaviors Sind DependencyProperties mit denen man Elemente im Xaml um Informationen erweitern kann. Löst das setzen der Daten auch Aktionen aus, handelt es sich um attached Behaviours.
  • 38. Attached Properties & Behaviors Das zusätzliche Verhalten kann durch den Changed-Eventhandler der Property angehangen bzw. ausgelöst werden (blaue Pfeile). Wichtig: Es handelt sich nicht um „normale“ Dependency Properties. Sie werden als attached registriert (roter Pfeil).
  • 40. Interactivity Behaviors Durch die Basisklasse Behavior wird genau angegeben welche Funktionen auf welchen Typ angewendet werden können. Dies spart viel Code, ist typsischer und leichter verständlich als Attached behaviors. <TextBox Text="{Binding Title}"> <i:Interaction.Behaviors> <MVVM:SpecialCharacterFilterBehavior /> </i:Interaction.Behaviors> </TextBox>
  • 42. Dependency Injection Die View fordert ihr ViewModel als Constructor Parameter an und weißt sie selbst ihrem DataContext zu.
  • 44. View Discovery per Template Bietet sich vor allem für EntityViewModels an. Die View wird als Control oder DataTemplate für einen Typ registriert. Dadurch erfolgt die Zuweisung zur View automatisch. Durch Template Selector kann man sehr einfach das Template anhand des Status des ViewModels umschalten.
  • 45. View Discovery per Template Hat man mehrere Typen die unterschiedlich dargestellt werden soll, gibt man die DataTemplates mit entsprechender Typedefinition als Ressource an. Die Auswahl geschieht dann automatisch anhand des Typs. <ContentControl Content="{Binding SelectedPublication}"> <ContentControl.Resources> <DataTemplate DataType="{x:Type viewModels:PublicationEntityViewModel}"> <local:PublicationDetailsView /> </DataTemplate> <DataTemplate DataType="{x:Type viewModels:MediumEntityViewModel}"> <local:MediumDetailsView /> </DataTemplate> </ContentControl.Resources> </ContentControl>
  • 47. ViewModel Locator in Prism Nutzt eine Attached Property. Sucht im Namespace „ViewModels“ nach einem ViewModel, dass so heißt wie die View. ◦ Bspw: PublicationView braucht ein PublicationViewModel Die Instanziierung erfolgt per IoC Container sobald eine Navigation mit dem RegionManager erfolgt. mvvm:ViewModelLocator.AutoWireViewModel="True"
  • 49. Bootstrapper?!? Komponente die der Initialisierung der Anwendung zum Programmstart dient. Typische Schritte sind: 1. Erstellen des IoC Containers und registrieren aller notwendigen Komponenten. 2. Initialisieren der wichtigsten Infrastruktur Komponenten (ViewModelLocator, DialogManager, Logger). 3. Erstellen und anzeigen des Hauptfensters.
  • 50. Initialisieren des IoC Containers am Beispiel AutoFac
  • 51. Initialisieren und anzeigen des Hauptfensters
  • 52. Registrieren und starten des Bootstrappings Entfernen der StartUpUri aus App.xaml und ersetzen durch einen Eventhandler für OnLoad. Instanziieren und starten des Bootstrappings.
  • 55. “Hand made IoC” var parser = new Parser(); var rechner = new Rechner(parser); var ui = new ConsoleUI(rechner) ui.Start();
  • 57. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion
  • 58. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Console
  • 59. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Console WPF
  • 60. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Bootstrapper Console WPF
  • 63. http://autofac.org/ • Sehr leistungsfähig und umfangreich. • Bietet viele verschiedene Lebenszyklen und Einflussmöglichkeiten bei der Erstellung von Instanzen. • Erlaubt XML-Konfiguration. • Bietet Schnittstellen für einfache Isolation von Bestandteilen. • Ist Verfügbar als PCL. • Ist sehr gut dokumentiert. AutoFac
  • 64. http://lightcore.ch/ • Sehr leichtgewichtig und schnell. • Erlaubte Lebenszyklen: • Transient • Singleton • Bezogen auf HTTP Request • Bezogen auf Thread • Nicht verfügbar als PCL. • Ist gut dokumentiert. LightCore
  • 65. http://mef.codeplex.com/ • Ist Teil des .Net Frameworks. • Bietet die wichtigsten Funktionen zum Auflösen von Abhängigkeitsgraphen. • Verwendet häufig Reflection. • Kein klassischer IoC Container. • Steht als PCL auch für andere Anwendungen zur Verfügung. • Ist sehr gut dokumentiert. MEF (Managed Extensibility Framework)
  • 66. https://unity.codeplex.com/ • Wurde von Microsoft Patterns & Practices entwickelt. • Bietet viele verschiedene Lebenszyklen und Einflussmöglichkeiten bei der Instanziierung von Klassen. • Erlaubt XML-Konfiguration. • Erlaubt Aspekt-Orientierte-Programmierung (AOP) • Ist verfügbar als PCL. • Ist sehr gut dokumentiert. Unity
  • 67. • Es gibt sehr viele IoC Container am Markt. • Diese haben einen unterschiedlichen Funktionsumfang und werden unterschiedlich gut gepflegt. • Vor dem Einsatz sollte unbedingt geklärt werden, wie umfangreich die Dokumentation ist und wann das letzte Update veröffentlicht wurde. • Wenn Geschwindigkeit wichtig ist: • sollte auf weniger umfangreiche Frameworks zurück gegriffen • und auf XML Konfigurationen verzichtet werden. Zusammenfassung
  • 69. Grundkonzept Es wird ein Interface erstellt über welches die Navigation gesteuert wird. Eine oder mehrere Regionen in der Applikation werden als Navigationsziele definiert. Eine Region kann ein ContentControl (zeigt nur eine View zeitgleich an) oder ein ItemsControl (zeigt mehrere Views zeitgleich an). Der NavigationService arbeitet sehr eng mit dem Hauptfenster zusammen und muss daher beim Bootstrapping gesondert initialisiert werden!
  • 70. MVVM Light INavigationService public interface INavigationService { string CurrentPageKey { get; } void GoBack(); void NavigateTo(string pageKey); void NavigateTo(string pageKey, object parameter); } Es gibt keine Implementierung für WPF! Es kann nur eine Region verwaltet werden! Die Navigation erfolgt im Stil einer „Kioskapp“
  • 71. Prism RegionManager Verschiedene Regionen können verwaltet werden. Es kann geprüft werden ob Navigation möglich ist. ViewModels können selbst die Navigation verhindern. Parameterübergabe ist möglich. Es können jederzeit Regionen registriert und entfernt werden. Regionen können Views verwalten die selbst Regionen enthalten.
  • 73. Nutzung von DataTemplates und Data Triggers Status basierte Navigation ändert die Anzeige anhand des Status des ViewModels. Es gibt demnach zwei unterschiedliche Darstellungen für das gleiche ViewModel. Dazu werden verschiedene DataTemplates festgelegt und per DataTrigger, VisualStateManager oder TemplateSelector zwischen diesen umgeschaltet.
  • 75. Validation Rules Regelsätze werden eigenständig als Objekte bereit gestellt und an die Controls gebunden. Sie sind somit nicht mehr Teil des ViewModels sondern liegen in der Verantwortung der View. ValidationRules erben von der Klasse ValidationRule. public class NotNullOrEmptyValidationRule : ValidationRule { public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if (value == null || string.IsNullOrWhiteSpace(value.ToString())) { return new ValidationResult(false, $"No value was set for {PropertyName}."); } return ValidationResult.ValidResult; } public string PropertyName { get; set; } }
  • 76. Validation Rules Regelsätze werden eigenständig als Objekte bereit gestellt und an die Controls gebunden. Sie sind somit nicht mehr Teil des ViewModels sondern liegen in der Verantwortung der View. ValidationRules müssen als embedded collection im Xaml angegeben werden. Sie haben nur sehr wenig Informationen über ihren Kontext! <TextBox> <TextBox.Text> <Binding Path="Title" UpdateSourceTrigger="PropertyChanged" > <Binding.ValidationRules> <MVVM:NotNullOrEmptyValidationRule PropertyName="Title" /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
  • 77. Validierung mit Exceptions Validierung geschieht auf Basis eines Bindings. Wird eine Exception geworfen, gilt die Validierung automatisch als fehlgeschlagen. Es handelt sich um eine sehr teure Art der Validierung und sollte daher möglichst vermieden werden. <TextBoxText="{Binding PublicationDate, ValidatesOnExceptions=True, UpdateSourceTrigger=PropertyChanged}" />
  • 78. IDataErrorInfo Definiert Eigenschaften um Fehlermeldungen für einzelne Properties oder das gesamte Objekt abzurufen. /// <summary>Provides the functionality to offer custom error information that a user interface can bind to.</summary> public interface IDataErrorInfo { /// <summary>Gets the error message for the property with the given name.</summary> /// <param name="columnName">The name of the property whose error message to get. </param> /// <returns>The error message for the property. The default is an empty string ("").</returns> string this[string columnName] { get; } /// <summary>Gets an error message indicating what is wrong with this object.</summary> /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns> string Error { get; } }
  • 79. IDataErrorInfo Um Validierungen vorzunehmen und einen Validierungsrahmen zu erhalten, muss beim Binding ValidatesOnDataErrors auf True gesetzt werden. <TextBox Text="{Binding Title, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" /> public string this[string columnName] { get { if (columnName == GetPropertyName(() => Title) && string.IsNullOrEmpty(Title)) { return "No title was given."; } return string.Empty; } } Kommt aus dem ObserveableObject von MVVM Light
  • 80. IDataErrorInfo Vorteile: Integriert in WPF wodurch Validierungshinweise direkt an den Fehlerstellen angezeigt werden. Nachteile: Hat eine Property keinen Wert, wird keine Validierung vorgenommen! Die Validierung geschieht synchron zur Eingabe, man kann sie nur sehr schwer steuern. Properties werden schrittweise validiert, was bei Calculated Properties zu erheblichen Problemen führen kann.
  • 81. DataAnnotations Über Annotationen kann für jede Property angegeben werden wie sie validiert werden soll. Diese Annotations sind in System.ComponentModel.DataAnnotations definiert. Dazu muss diese Dll referenziert werden! Es gibt eine sehr große Zahl unterschiedlicher Annotationen. [Required(AllowEmptyStrings = false, ErrorMessage = "Please enter valid title.")] public string Title { get { return publication.Title; } set { publication.Title = value; } }
  • 82. DataAnnotations Die Auswertung kann im Rahmen von IDataErrorInfo mit Hilfe der Validator-Klasse für jede Property geschehen. protected virtual string Validate(string propertyName) { var error = string.Empty; var value = GetPropertyValue(propertyName, this); var results = new List<ValidationResult>(1); var context = new ValidationContext(this, null, null) { MemberName = propertyName }; var result = Validator.TryValidateProperty(value, context, results); if (!result) { var validationResult = results.First(); error = validationResult.ErrorMessage; } SetError(propertyName, error); return error; }
  • 83. DataAnnotations Über die Validator-Klasse kann aber auch ein komplettes Objekt Validiert werden. public virtual bool Validate(object instance) { var validationContext = new ValidationContext(instance); var validationResults = new List<ValidationResult>(); var isValid = Validator.TryValidateObject(instance, validationContext, validationResults); SetErrors(validationResults); return isValid; }
  • 84. INotifyDataErrorInfo Definiert Eigenschaften um Validierung asynchron vorzunehmen. Statt, dass direkt validiert wird, werden Fehler erst nach einem Event angezeigt. Pro Property sind mehrere Fehlermeldungen möglich. public interface INotifyDataErrorInfo { /// <summary>Gets a value that indicates whether the entity has validation errors. </summary> /// <returns>true if the entity currently has validation errors; otherwise, false.</returns> bool HasErrors { get; } /// <summary>Gets the validation errors for a specified property or for the entire entity.</summary> /// <returns>The validation errors for the property or entity.</returns> IEnumerable GetErrors(string propertyName); /// <summary>Occurs when the validation errors have changed for a property or for the entire entity. event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; }
  • 85. INotifyDataErrorInfo Wenn validiert werden soll, wird zunächst HasErrors geprüft. Dies triggert einen Zugriff auf GetErrors falls HasErrors true ist. Mit ErrorsChanged kann der Vorgang indirekt von außen gestartet werden. public interface INotifyDataErrorInfo { /// <summary>Gets a value that indicates whether the entity has validation errors. </summary> /// <returns>true if the entity currently has validation errors; otherwise, false.</returns> bool HasErrors { get; } /// <summary>Gets the validation errors for a specified property or for the entire entity.</summary> /// <returns>The validation errors for the property or entity.</returns> IEnumerable GetErrors(string propertyName); /// <summary>Occurs when the validation errors have changed for a property or for the entire entity. event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; } <TextBox Text="{Binding Title, ValidatesOnNotifyDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
  • 86. Validierungsinformationen in Xaml abrufen Jedes UI Element hat Zugriff auf Validierungsinformationen. Dies kann genutzt werden um das Control entsprechend zu stylen. <TextBox Text="{Binding Title, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ErrorStyle}"> <Style x:Key="ErrorStyle" TargetType="FrameworkElement"> <Style.Triggers> <Trigger Property="Validation.HasError" Value="True"> <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" /> </Trigger> </Style.Triggers> </Style>
  • 93. Prism
  • 94. Catel
  • 97. Generelle Features eine Auswahl Prism Catel Caliburn.Micro MVVM Light Bootstrapper ✔ ✔ ✔ ✘ Modulverwaltung ✔ ✘ ✘ ✘ View Composition ✔ ✘* ✘* ✘ Konvention basierende Konfiguration ✔ ✔ ✔ ✘ Event Aggregator ✔ ✔ ✔ ✔ RelayCommand / DelegateCommand ✔ ✔ ✔ ✔ EventToCommand trigger ✘* ✔ ✔ ✔ IoC Container Support ✔ ✔ ✔ ✔ Eigener IoC Container ✘* ✔ ✔ ✔
  • 98. View Model Base Prism Catel Caliburn.Micro MVVM Light INotifyPropertyChanged ✔ ✔ ✔ ✔ INotifyPropertyChangeing ✘ ✔ ✔ ✔ IDataErrorInfo ✘ ✔ ✘ ✘ IEditableObject ✘ ✔ ✘ ✘ Serialisierung der Daten ✘ ✔ ✘ ✘ Methoden Mapping ✘ ✘ ✔ ✘ Property Mapping ✘ ✔ ✔ ✘
  • 99. View Handling Prism Catel Caliburn.Micro MVVM LIght View Composition ✔ ✘* ✘ ✘ Navigation zwischen Views ✔ ✔ ✔ ✘* Informationen über Navigation ✔ ✔* ✘ ✘ Verhindern von Navigation ✔ ✘ ✘ ✘ ViewModels für Sub Views ✘ ✔ ✘ ✘ Lazy loading von View Models ✔ ✔ ✘ ✘ Events wenn ein View geladen wurde ✘ ✔ ✘ ✘
  • 101. Logging mit Visual Studio In den Debugoptionen kann eingestellt werden, welche Dinge während des Debugs in die OutputView ausgegeben werden.
  • 102. Debugging mit PresentationTraceSources WPF ermöglicht das „Logging“ in die Console über attached Properties. Logging wird nur auf bestimmte Elemente angewendet. Dabei steht der genaue Wert für komplexe Datentypen jedoch nicht zur Verfügung! <TextBox Text="{Binding PublicationDate, PresentationTraceSources.TraceLevel=High}" />
  • 103. Debugging mit PresentationTraceSources WPF ermöglicht das „Logging“ in die Console über attached Properties. Weitere Senken können gesondert konfiguriert werden.
  • 104. Debugging mit Convertern Problem Man kann nicht ohne weiteres in Xaml debuggen oder loggen. Lösung Man definiert einen Converter der C# Code in das Xaml „injiziert“.
  • 106. Dependency Properties Nutzt man wenn: - Man eine Eigenschaft gegen andere Eigenschaften binden möchte. - Wenn Animationen umgesetzt werden sollen. - Wenn sie in Styles berücksichtigt werden sollen. Bspw. DataTrigger - Wenn sie mit einer Ressource versehen werden sollen.
  • 107. Dependency Properties public static readonly DependencyProperty SetTextProperty = DependencyProperty.Register("SetText", typeof(string), typeof(MyObject), new PropertyMetadata("", new PropertyChangedCallback(OnSetTextChanged))); public string SetText { get { return (string)GetValue(SetTextProperty); } set { SetValue(SetTextProperty, value); } } Dependency Properties werden üblicherweise auf DependencyObjects und damit in CustomControls oder UserControls verwendet. Namenspattern: [Name]Property Der Tatsächliche Name wird bei der Registrierung als Parameter angegeben. Die Werte werden über Get und Set des DependencyObjects gesetzt.