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.
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;
}
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);
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.
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.
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>
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.
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.
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>
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}" />
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.
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.
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
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