SlideShare une entreprise Scribd logo
1  sur  141
WPF Does n Don´ts
Hendrik Lösch
Senior Consultant & Coach
Hendrik.Loesch@saxsys.de
@HerrLoesch
Hendrik-Loesch.de
Just-About.Net
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 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 oder über einen Template Selector.
<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"
ViewModel
Locator
var viewType = view.GetType();
var viewModelType = GetViewModelType(viewType);
var viewModel = ServiceLocator.Current.GetInstance(viewModelType);
view.DataContext = viewModel;
private static Type GetViewModelType(Type viewType)
{
var viewName = viewType.FullName;
viewName = viewName.Replace(".Views.", ".ViewModels.");
var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel";
var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix,
viewAssemblyName);
return Type.GetType(viewModelName);
}
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“.
Data Binding
Performance & Pitfalls
MemoryLeaks durch Events
Events halten Referenzen auf ihre Abonnenten. Diese Abonnenten werden deshalb nicht vom
GarbageCollector aus dem Speicher entfernt.
Lösung: Events möglichst als WeakEvents umsetzen
WeakEventManager
https://msdn.microsoft.com/en-us/library/hh199438(v=vs.110).aspx
https://www.codeproject.com/Articles/738109/The-NET-weak-event-pattern-in-Csharp
Umsetzung mit MVVM Light nutzt die eigenen Klassen WeakAction & WeakFunc
Binding Leak (in .Net 4.5 gefixt)
Wird auf ein Objekt gebunden, dass nicht INotifyPropertyChanged verwendet (bspw. List<T>)
wird intern eine Referenz auf den PropertyDescriptor gehalten, der versucht Änderungen
innerhalb des Bindings zu ermitteln. Diese Bindung war bis 4.5 nicht Weak umgesetzt und
konnte daher gerade bei großen Listen zu erheblichen Datenlecks führen.
Lösung:
- Listen verwenden die INotifyCollectionChanged verwenden (ObservableCollection).
- INotifyPropertyChanged bei jedem Binding Ziel einsetzen.
- DepedencyProperties verwenden.
ObservableCollections
ObservableCollections informieren die UI bei jeder Änderung an ihrem Inhalt über diese
Änderungen. Werden viele Daten per Add ausgetauscht, entsteht eine sehr hohen Last.
Lösungen:
- ObservableCollection nur einsetzen wo tatsächlich über Änderungen an der Liste informiert
werden muss.
- Eigene Klasse von ObseravableCollection ableiten und eine Methode „Add-Range“ und/oder
„Remove-Range“ hinzufügen, die erst die Aktionen durchführt und dann entsprechende Events
feuert.
- Die komplette Observable Collection durch eine neue ersetzen die bei der Instanziierung den
kompletten neuen Inhalt als Parameter erhält.
Binden gegen IEnumerable aus einer
Linq Query
Linq Queries werden verzögert ausgeführt. So lange ToList, ToArray, Count, Sum, usw. nicht
aufgerufen wurden, liegt nur eine Query, noch nicht aber die Daten vor. Wird gegen eine Query
gebunden, wird diese ausgeführt und liefert die Daten an die UI. Bei Refreshs kann es deshalb
dazu kommen, dass Queries sehr häufig ausgeführt werden und immer wieder auf eine
Datenbank zugreifen.
Lösung: Bei Rückgabewert IEnumerable<T> u.ä. immer ToList im ViewModel aufrufen um sicher
zu gehen, dass die Daten im Speicher vorliegen und nicht mit Queries gearbeitet wird.
Publications = publicationRepository.GetPublications().Select(x => new PublicationDetailsViewModel(x)).ToList();
Statt
Publications = publicationRepository.GetPublications().Select(x => new PublicationDetailsViewModel(x));
Sehr hohes Limit für Undo-Aktionen bei
Textboxen
TextBoxen haben ein Limit von 100 Undo-Aktionen (Strg + Z). Werden viele Änderungen an
Textboxen vorgenommen, werden auch viele Daten vom Undo-Manager verwaltet.
Lösung: Undo-Limit fest hinterlegen
<TextBox Text="{Binding PublicationDate}" UndoLimit="10" />
Animationen durch Transformationen
WPF Animationen können genutzt werden um bspw. Elemente zu drehen oder zu verschieben.
In diesem Fall wird das interne Koordinatensystem und alle darin befindlichen Objekte
verändert. Je mehr Objekte davon beeinflusst werden, desto aufwändig ist die Transformation.
Lösung:
- Wenn nicht anders notwendig, können auch Gifs verwendet werden (BusyIndicator)
- Wenn viele Objekte verschoben werden müssen, kann auch ein Screenshot des Parents
gemacht, dessen Inhalt durch das Screenshot ersetzt und damit nur der Screenshot bewegt
werden. (Animation bei der Navigation in Anwendungen).
Theming mit Opacity
Der Disabled Zustand von Controls wird gelegentlich dadurch umgesetzt, dass ihre Opacity
verändert und sie somit halbtransparent werden. Durch diese Transparenz muss aber der
Hintergrund mit gerändert werden und alle darunter liegenden Elemente müssen angepasst
werden. Dies kann sehr rechenintensiv sein.
Lösung:
- Brushes mit einem entsprechenden ARGB Wert nutzen (#77ffffff)
- Komplett auf Transparenzeffekt im Disabled Zustand verzichten.
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.
User Control vs. Custom
Control
Custom Control
• Ableitung von einer konkreten Klasse != UserControl
• Styling- und Templating-Möglichkeiten
• Versieht eine Summe von Primitiven (Border, Textblock, …)
mit Funktionalität
• Visueller Aufbau im ControlTemplate
• Default Style möglich (Generic.xaml)
• Zusammenfassung in einer Control Library möglich
• Element-Referenzierung über OnApplyTemplate()
• Logik im CodeBehind, es gibt kein ViewModel
Custom Control
UI Element – Leichtgewichtigste Basisklasse mit grundsätzlichen Features Layout, Focus und Events
FrameworkElement – fühgt Styling, tooltips und Contextmenüs und data binding.
Control – Unterstützt Templates, hat Basisproperties wie Foreground, Background und FontSize
ContentControl – Wie Control nur mit zusätzlichem Inhalt.
HeaderContentControl – Wie ContentControl nur das der Content noch einen Header hat (Tab, Groupbox, …)
ItemsControl – Wie Controls, kann aber mehrere andere UIElemente enthalten. Hat keine Selektion
HeaderItemsControl – Wie HeaderContentControl aber für mehrere Items ()MenuItem)
Selector – Wie ItemsControl aber mit Selektion (ListBox)
RangeBase – Basis für alle Control die einen Wertebereich darstellen (Sliders, ProgressBar)
UserControl
• Leitet ab von User Control (Leitet seinerseits von ContentControl ab)
• Stellt eine Summe von Controls dar ohne deren Verhalten zu verändern!
• Verfügt über ein ViewModel in welchem die Logik gekapselt ist.
Custom Controls als Layout Container
Custom Controls müssen selbst nicht unbedingt sichtbar sein.
Sie können zur Anordnung von anderen Controls genutzt werden.
Custom Controls für wiederkehrendes Layouting nutzen
Layout
eines
Formulars
Layout
Container
Layout ist gekapselt
◦ Dadurch einheitlich.
◦ Besser lesbar.
◦ Keine Inkonsistenzen über die UI
hinweg.
◦ Weniger fehleranfällig beim Einsatz.
◦ Aufwändig bei der erstmaligen
Erstellung.
Aufbau der Solution
Kleinere Projekte
Aufbau eines Application Frame der die Anwendung
initialisiert und das Hauptfenster enthält.
Trennung von Implementierung, Schnittstellen und
Tests.
Technische Schichten kommunizieren nicht
miteinander, nur über Schnittstellen.
Zwei Arten von Test Dlls:
◦ Unit Tests – laufen schnell und ohne externe
Abhängigkeiten
◦ Integrations Tests – Teil des Nightly oder CI Builds,
enthält auch UI Tests
Applikationsrahmen
Applikationsrahmen enthält einen Bootstrapper der
während der Startphase alles wichtigen
Initialisierungsschritte vornimmt.
Definiert das Hauptfenster mit dessen genereller
Logik.
Kann in kleineren Projekten auch Infrastrukturlogik
enthalten.
Contracts / Interfaces
Definiert die Schnittstellen der Applikation.
Enthält in kleineren Projekten auch das Domänenmodell.
Data & Logic
Je eine Dlls für Datenhaltung und Geschäftslogik um sie physisch
zu trennen.
- Erlaubt mehr Flexibilität bei größer werdender Codebasis.
- Verhindert Kreisabhängigkeiten zwischen den Schichten.
Achtung: Geschäftslogik sollte möglichst nicht in den ViewModels
liegen!
UI
Enthält Views & ViewModels.
Kann MVVM Helferklassen enthalten.
Kann ggf. das Theme enthalten.
Projekte mit viel Funktionalität
Module werden als
eigenständiger Verbund
behandelt.
Sie werden über den
Applikationsrahmen erzeugt
und nutzen die zentralen
Schnittstellen.
Die Infrastruktur wird wie ein
eigenständiges Modul
behandelt.
Effekte
Arten
Effekte (Basisklasse Effect)
• Blur und DropShadow
• Performance-hungrig daher sparsam verwenden
• Drittanbieter stellen weitere Effekte bereit
Bitmap-Effekte (Basisklasse BitmapEffect)
• Bevel, Emboss, Outer Glow, ...
• Sollte nicht mehr verwendet werden
• Schlechte Performance
Lösung
Primitive und Custom
Controls nutzen
Tools
XAML Styler
Code Guidelines für XAML inkl. Umformatierung
https://marketplace.visualstudio.com/items?itemName=ChrisChen.XamlStyler
Snoop
Debugging und Analyse Tool für WPF
https://github.com/cplotts/snoopwpf
ResMerger
• Tool zum Mergen von verschiedenen
Resource Dictionaries zu einem
großen Resource Dictionary
• App bindet nur noch ein Resource
Dictionary ein
• Merge wird in Build Prozess
integriert
https://github.com/dctdct/WPF-ResMerger
ObjectFiller
C# Framework um Daten zu generieren.
Sehr praktisch für Prototyping, Tests und Design
Daten.
http://objectfiller.net/
Fontawesome
https://fontawesome.com/
Icon Bibliothek die als Schrift umgesetzt ist und sich damit leicht integrieren lässt.
<fa:ImageAwesome
Name="IsBusyProgressBar"
Icon="Spinner"
Spin="True"
SpinDuration="1“ />
https://github.com/charri/Font-Awesome-WPF/blob/master/README-WPF.md

Contenu connexe

Similaire à WPF Dos n Don'ts - der WPF Rundumschlag

Knockout.js
Knockout.jsKnockout.js
Knockout.jsdevtyr
 
Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJSSebastian Springer
 
Magento: Event/Observer
Magento: Event/ObserverMagento: Event/Observer
Magento: Event/Observertherouv
 
Zend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesZend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesRalf Eggert
 
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
 
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDI
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDISchlanke Webarchitekturen nicht nur mit JSF 2 und CDI
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDIadesso AG
 
MicroProfile 2.x: Der alternative Standard
MicroProfile 2.x: Der alternative StandardMicroProfile 2.x: Der alternative Standard
MicroProfile 2.x: Der alternative StandardOPEN KNOWLEDGE GmbH
 
2009 03 17 Spring101
2009 03 17 Spring1012009 03 17 Spring101
2009 03 17 Spring101gueste4be40
 
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJS
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJSWebGL - 3D im Browser - Erfahrungsbericht mit BabylonJS
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJSOliver Hader
 
2009 - DNC: Silverlight ohne UI - Nur als Cache
2009 - DNC: Silverlight ohne UI - Nur als Cache2009 - DNC: Silverlight ohne UI - Nur als Cache
2009 - DNC: Silverlight ohne UI - Nur als CacheDaniel Fisher
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPAmh0708
 
Einführung in ASP.NET Core Middlewares
Einführung in ASP.NET Core MiddlewaresEinführung in ASP.NET Core Middlewares
Einführung in ASP.NET Core MiddlewaresMatthias Jauernig
 
Lean web architecture mit jsf 2.0, cdi & co.
Lean web architecture mit jsf 2.0, cdi & co.Lean web architecture mit jsf 2.0, cdi & co.
Lean web architecture mit jsf 2.0, cdi & co.adesso AG
 

Similaire à WPF Dos n Don'ts - der WPF Rundumschlag (20)

MVVM mit WPF
MVVM mit WPFMVVM mit WPF
MVVM mit WPF
 
Knockout.js
Knockout.jsKnockout.js
Knockout.js
 
Große Applikationen mit AngularJS
Große Applikationen mit AngularJSGroße Applikationen mit AngularJS
Große Applikationen mit AngularJS
 
Magento: Event/Observer
Magento: Event/ObserverMagento: Event/Observer
Magento: Event/Observer
 
CDI
CDICDI
CDI
 
Zend Framework 2 - Best Practices
Zend Framework 2 - Best PracticesZend Framework 2 - Best Practices
Zend Framework 2 - Best Practices
 
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
 
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDI
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDISchlanke Webarchitekturen nicht nur mit JSF 2 und CDI
Schlanke Webarchitekturen nicht nur mit JSF 2 und CDI
 
MicroProfile 2.x: Der alternative Standard
MicroProfile 2.x: Der alternative StandardMicroProfile 2.x: Der alternative Standard
MicroProfile 2.x: Der alternative Standard
 
MVVM Pattern
MVVM Pattern MVVM Pattern
MVVM Pattern
 
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
 
2009 03 17 Spring101
2009 03 17 Spring1012009 03 17 Spring101
2009 03 17 Spring101
 
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJS
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJSWebGL - 3D im Browser - Erfahrungsbericht mit BabylonJS
WebGL - 3D im Browser - Erfahrungsbericht mit BabylonJS
 
2009 - DNC: Silverlight ohne UI - Nur als Cache
2009 - DNC: Silverlight ohne UI - Nur als Cache2009 - DNC: Silverlight ohne UI - Nur als Cache
2009 - DNC: Silverlight ohne UI - Nur als Cache
 
Lightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPALightweight AOP with CDI and JPA
Lightweight AOP with CDI and JPA
 
Einführung in ASP.NET Core Middlewares
Einführung in ASP.NET Core MiddlewaresEinführung in ASP.NET Core Middlewares
Einführung in ASP.NET Core Middlewares
 
Net@night asp.net mvc
Net@night asp.net mvcNet@night asp.net mvc
Net@night asp.net mvc
 
Pimcore
PimcorePimcore
Pimcore
 
Feature Flags mit Togglz
Feature Flags mit TogglzFeature Flags mit Togglz
Feature Flags mit Togglz
 
Lean web architecture mit jsf 2.0, cdi & co.
Lean web architecture mit jsf 2.0, cdi & co.Lean web architecture mit jsf 2.0, cdi & co.
Lean web architecture mit jsf 2.0, cdi & co.
 

Plus de 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
 

Plus de 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
 

WPF Dos n Don'ts - der WPF Rundumschlag

  • 1. WPF Does n Don´ts
  • 2. Hendrik Lösch Senior Consultant & Coach Hendrik.Loesch@saxsys.de @HerrLoesch Hendrik-Loesch.de Just-About.Net
  • 6. View ViewModel Model DataBinding XAML C#/VB SQL/WSDL JSON/XML
  • 15. Eigene ViewModel Basisklasse In aller Regel benötigt man eine Implementierung von INotifyProperty Changed. Durch das CallerMemberName Attribut kann man sich viel Arbeit sparen.
  • 17. ObservableObject Basisklasse für alle Klassen die über Änderungen an ihren Eigenschaften informieren wollen.
  • 18. 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.
  • 20. 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; }
  • 21. Delegate Command Implementiert ICommand. Die Überprüfung von „CanExecute“ muss „per Hand“ getriggert werden. Kann Performancevorteile zur Laufzeit haben, da weniger Events auftreten.
  • 22. 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.
  • 23. 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);
  • 24. 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);
  • 25. Daten zur Design Zeit verwenden OHNE FRAMEWORKS
  • 26. 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
  • 27. 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.
  • 28. 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.
  • 29. 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.
  • 30. Daten zur Design Zeit verwenden MIT MVVM LIGHT
  • 31. Designzeit Daten mit MVVM Light ViewModelBase bietet eine Eigenschaft IsInDesignMode mit der überprüft wird ob man sich im Designmodus befindet.
  • 32. 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.
  • 33. Daten für das Design
  • 36. 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>
  • 37. 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"
  • 39. 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.
  • 40. 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).
  • 42. 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>
  • 44. Dependency Injection Die View fordert ihr ViewModel als Constructor Parameter an und weißt sie selbst ihrem DataContext zu.
  • 46. View Discovery per Template Bietet sich vor allem für EntityViewModels an. Die View wird als 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.
  • 47. 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 oder über einen Template Selector. <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>
  • 49. 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"
  • 50. ViewModel Locator var viewType = view.GetType(); var viewModelType = GetViewModelType(viewType); var viewModel = ServiceLocator.Current.GetInstance(viewModelType); view.DataContext = viewModel; private static Type GetViewModelType(Type viewType) { var viewName = viewType.FullName; viewName = viewName.Replace(".Views.", ".ViewModels."); var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName; var suffix = viewName.EndsWith("View") ? "Model" : "ViewModel"; var viewModelName = String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}", viewName, suffix, viewAssemblyName); return Type.GetType(viewModelName); }
  • 52. 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.
  • 53. Initialisieren des IoC Containers am Beispiel AutoFac
  • 54. Initialisieren und anzeigen des Hauptfensters
  • 55. Registrieren und starten des Bootstrappings Entfernen der StartUpUri aus App.xaml und ersetzen durch einen Eventhandler für OnLoad. Instanziieren und starten des Bootstrappings.
  • 58. “Hand made IoC” var parser = new Parser(); var rechner = new Rechner(parser); var ui = new ConsoleUI(rechner) ui.Start();
  • 60. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion
  • 61. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Console
  • 62. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Console WPF
  • 63. Der Abhängigkeitsgraph UI Rechner Parser Addition Subtraktion Bootstrapper Console WPF
  • 66. 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
  • 67. 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
  • 68. 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)
  • 69. 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
  • 70. • 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
  • 72. 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!
  • 73. 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“
  • 74. 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.
  • 76. 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.
  • 78. 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; } }
  • 79. 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>
  • 80. 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}" />
  • 81. 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; } }
  • 82. 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
  • 83. 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.
  • 84. 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; } }
  • 85. 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; }
  • 86. 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; }
  • 87. 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; }
  • 88. 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}" />
  • 89. 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>
  • 96. Prism
  • 97. Catel
  • 100. 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 ✘* ✔ ✔ ✔
  • 101. View Model Base Prism Catel Caliburn.Micro MVVM Light INotifyPropertyChanged ✔ ✔ ✔ ✔ INotifyPropertyChangeing ✘ ✔ ✔ ✔ IDataErrorInfo ✘ ✔ ✘ ✘ IEditableObject ✘ ✔ ✘ ✘ Serialisierung der Daten ✘ ✔ ✘ ✘ Methoden Mapping ✘ ✘ ✔ ✘ Property Mapping ✘ ✔ ✔ ✘
  • 102. 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 ✘ ✔ ✘ ✘
  • 104. Logging mit Visual Studio In den Debugoptionen kann eingestellt werden, welche Dinge während des Debugs in die OutputView ausgegeben werden.
  • 105. 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}" />
  • 106. Debugging mit PresentationTraceSources WPF ermöglicht das „Logging“ in die Console über attached Properties. Weitere Senken können gesondert konfiguriert werden.
  • 107. 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“.
  • 109. MemoryLeaks durch Events Events halten Referenzen auf ihre Abonnenten. Diese Abonnenten werden deshalb nicht vom GarbageCollector aus dem Speicher entfernt. Lösung: Events möglichst als WeakEvents umsetzen WeakEventManager https://msdn.microsoft.com/en-us/library/hh199438(v=vs.110).aspx https://www.codeproject.com/Articles/738109/The-NET-weak-event-pattern-in-Csharp Umsetzung mit MVVM Light nutzt die eigenen Klassen WeakAction & WeakFunc
  • 110. Binding Leak (in .Net 4.5 gefixt) Wird auf ein Objekt gebunden, dass nicht INotifyPropertyChanged verwendet (bspw. List<T>) wird intern eine Referenz auf den PropertyDescriptor gehalten, der versucht Änderungen innerhalb des Bindings zu ermitteln. Diese Bindung war bis 4.5 nicht Weak umgesetzt und konnte daher gerade bei großen Listen zu erheblichen Datenlecks führen. Lösung: - Listen verwenden die INotifyCollectionChanged verwenden (ObservableCollection). - INotifyPropertyChanged bei jedem Binding Ziel einsetzen. - DepedencyProperties verwenden.
  • 111. ObservableCollections ObservableCollections informieren die UI bei jeder Änderung an ihrem Inhalt über diese Änderungen. Werden viele Daten per Add ausgetauscht, entsteht eine sehr hohen Last. Lösungen: - ObservableCollection nur einsetzen wo tatsächlich über Änderungen an der Liste informiert werden muss. - Eigene Klasse von ObseravableCollection ableiten und eine Methode „Add-Range“ und/oder „Remove-Range“ hinzufügen, die erst die Aktionen durchführt und dann entsprechende Events feuert. - Die komplette Observable Collection durch eine neue ersetzen die bei der Instanziierung den kompletten neuen Inhalt als Parameter erhält.
  • 112. Binden gegen IEnumerable aus einer Linq Query Linq Queries werden verzögert ausgeführt. So lange ToList, ToArray, Count, Sum, usw. nicht aufgerufen wurden, liegt nur eine Query, noch nicht aber die Daten vor. Wird gegen eine Query gebunden, wird diese ausgeführt und liefert die Daten an die UI. Bei Refreshs kann es deshalb dazu kommen, dass Queries sehr häufig ausgeführt werden und immer wieder auf eine Datenbank zugreifen. Lösung: Bei Rückgabewert IEnumerable<T> u.ä. immer ToList im ViewModel aufrufen um sicher zu gehen, dass die Daten im Speicher vorliegen und nicht mit Queries gearbeitet wird. Publications = publicationRepository.GetPublications().Select(x => new PublicationDetailsViewModel(x)).ToList(); Statt Publications = publicationRepository.GetPublications().Select(x => new PublicationDetailsViewModel(x));
  • 113. Sehr hohes Limit für Undo-Aktionen bei Textboxen TextBoxen haben ein Limit von 100 Undo-Aktionen (Strg + Z). Werden viele Änderungen an Textboxen vorgenommen, werden auch viele Daten vom Undo-Manager verwaltet. Lösung: Undo-Limit fest hinterlegen <TextBox Text="{Binding PublicationDate}" UndoLimit="10" />
  • 114. Animationen durch Transformationen WPF Animationen können genutzt werden um bspw. Elemente zu drehen oder zu verschieben. In diesem Fall wird das interne Koordinatensystem und alle darin befindlichen Objekte verändert. Je mehr Objekte davon beeinflusst werden, desto aufwändig ist die Transformation. Lösung: - Wenn nicht anders notwendig, können auch Gifs verwendet werden (BusyIndicator) - Wenn viele Objekte verschoben werden müssen, kann auch ein Screenshot des Parents gemacht, dessen Inhalt durch das Screenshot ersetzt und damit nur der Screenshot bewegt werden. (Animation bei der Navigation in Anwendungen).
  • 115. Theming mit Opacity Der Disabled Zustand von Controls wird gelegentlich dadurch umgesetzt, dass ihre Opacity verändert und sie somit halbtransparent werden. Durch diese Transparenz muss aber der Hintergrund mit gerändert werden und alle darunter liegenden Elemente müssen angepasst werden. Dies kann sehr rechenintensiv sein. Lösung: - Brushes mit einem entsprechenden ARGB Wert nutzen (#77ffffff) - Komplett auf Transparenzeffekt im Disabled Zustand verzichten.
  • 117. 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.
  • 118. 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.
  • 119. User Control vs. Custom Control
  • 120. Custom Control • Ableitung von einer konkreten Klasse != UserControl • Styling- und Templating-Möglichkeiten • Versieht eine Summe von Primitiven (Border, Textblock, …) mit Funktionalität • Visueller Aufbau im ControlTemplate • Default Style möglich (Generic.xaml) • Zusammenfassung in einer Control Library möglich • Element-Referenzierung über OnApplyTemplate() • Logik im CodeBehind, es gibt kein ViewModel
  • 121. Custom Control UI Element – Leichtgewichtigste Basisklasse mit grundsätzlichen Features Layout, Focus und Events FrameworkElement – fühgt Styling, tooltips und Contextmenüs und data binding. Control – Unterstützt Templates, hat Basisproperties wie Foreground, Background und FontSize ContentControl – Wie Control nur mit zusätzlichem Inhalt. HeaderContentControl – Wie ContentControl nur das der Content noch einen Header hat (Tab, Groupbox, …) ItemsControl – Wie Controls, kann aber mehrere andere UIElemente enthalten. Hat keine Selektion HeaderItemsControl – Wie HeaderContentControl aber für mehrere Items ()MenuItem) Selector – Wie ItemsControl aber mit Selektion (ListBox) RangeBase – Basis für alle Control die einen Wertebereich darstellen (Sliders, ProgressBar)
  • 122. UserControl • Leitet ab von User Control (Leitet seinerseits von ContentControl ab) • Stellt eine Summe von Controls dar ohne deren Verhalten zu verändern! • Verfügt über ein ViewModel in welchem die Logik gekapselt ist.
  • 123. Custom Controls als Layout Container Custom Controls müssen selbst nicht unbedingt sichtbar sein. Sie können zur Anordnung von anderen Controls genutzt werden. Custom Controls für wiederkehrendes Layouting nutzen
  • 125. Layout Container Layout ist gekapselt ◦ Dadurch einheitlich. ◦ Besser lesbar. ◦ Keine Inkonsistenzen über die UI hinweg. ◦ Weniger fehleranfällig beim Einsatz. ◦ Aufwändig bei der erstmaligen Erstellung.
  • 127. Kleinere Projekte Aufbau eines Application Frame der die Anwendung initialisiert und das Hauptfenster enthält. Trennung von Implementierung, Schnittstellen und Tests. Technische Schichten kommunizieren nicht miteinander, nur über Schnittstellen. Zwei Arten von Test Dlls: ◦ Unit Tests – laufen schnell und ohne externe Abhängigkeiten ◦ Integrations Tests – Teil des Nightly oder CI Builds, enthält auch UI Tests
  • 128. Applikationsrahmen Applikationsrahmen enthält einen Bootstrapper der während der Startphase alles wichtigen Initialisierungsschritte vornimmt. Definiert das Hauptfenster mit dessen genereller Logik. Kann in kleineren Projekten auch Infrastrukturlogik enthalten.
  • 129. Contracts / Interfaces Definiert die Schnittstellen der Applikation. Enthält in kleineren Projekten auch das Domänenmodell.
  • 130. Data & Logic Je eine Dlls für Datenhaltung und Geschäftslogik um sie physisch zu trennen. - Erlaubt mehr Flexibilität bei größer werdender Codebasis. - Verhindert Kreisabhängigkeiten zwischen den Schichten. Achtung: Geschäftslogik sollte möglichst nicht in den ViewModels liegen!
  • 131. UI Enthält Views & ViewModels. Kann MVVM Helferklassen enthalten. Kann ggf. das Theme enthalten.
  • 132. Projekte mit viel Funktionalität Module werden als eigenständiger Verbund behandelt. Sie werden über den Applikationsrahmen erzeugt und nutzen die zentralen Schnittstellen. Die Infrastruktur wird wie ein eigenständiges Modul behandelt.
  • 134. Arten Effekte (Basisklasse Effect) • Blur und DropShadow • Performance-hungrig daher sparsam verwenden • Drittanbieter stellen weitere Effekte bereit Bitmap-Effekte (Basisklasse BitmapEffect) • Bevel, Emboss, Outer Glow, ... • Sollte nicht mehr verwendet werden • Schlechte Performance
  • 136. Tools
  • 137. XAML Styler Code Guidelines für XAML inkl. Umformatierung https://marketplace.visualstudio.com/items?itemName=ChrisChen.XamlStyler
  • 138. Snoop Debugging und Analyse Tool für WPF https://github.com/cplotts/snoopwpf
  • 139. ResMerger • Tool zum Mergen von verschiedenen Resource Dictionaries zu einem großen Resource Dictionary • App bindet nur noch ein Resource Dictionary ein • Merge wird in Build Prozess integriert https://github.com/dctdct/WPF-ResMerger
  • 140. ObjectFiller C# Framework um Daten zu generieren. Sehr praktisch für Prototyping, Tests und Design Daten. http://objectfiller.net/
  • 141. Fontawesome https://fontawesome.com/ Icon Bibliothek die als Schrift umgesetzt ist und sich damit leicht integrieren lässt. <fa:ImageAwesome Name="IsBusyProgressBar" Icon="Spinner" Spin="True" SpinDuration="1“ /> https://github.com/charri/Font-Awesome-WPF/blob/master/README-WPF.md