Tutorialreihe <WPF lernen/>

    • WPF

    Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

      Tutorialreihe <WPF lernen/>

      WPF verständlich und anhand von Praxisbeispielen erklärt


      Autor: Sascha Patschka

      Inhaltsverzeichnis (inkrementell)

      ___1. Vorstellung
      _____1.1 Einleitung
      _____1.2 Aufbau der Tutorialreihe

      ___2. Grundlagen der WPF
      _____2.1 Einführung in die WPF
      ________2.1.1 Die wichtigsten Controls und deren Verwendung
      ______________2.1.1.1 Videocast: Die wichtigsten Controls und ihr Verhalten
      ______________2.1.1.2 Videocast: Style, Templates und Trigger
      ______________2.1.1.3 Videocast: Controls eine neue Optik verpassen
      ________2.1.2 XAML Namespaces
      ______________2.1.2.1 Kurze Theorie
      ______________2.1.2.2 Anwendung interessanter Namespaces
      ______________2.1.2.3 Eigene Namespaces erstellen und integieren
      ________2.1.3 Resourcen
      ______________2.1.3.1 Was sind Resourcen, was bringen sie mir
      ______________2.1.3.2 Unterschied StaticResource und DynamicResource
      ________2.1.4 Binding und das Bindingsystem (Cooming soon)
      ______________2.1.4.1 Was ist DataBinding? Das Konzept dahinter
      ______________2.1.4.2 Binding anhand einfacher Beispiele und Klassen
      ______________2.1.4.3 DesignTime Support für Binding
      ______________2.1.4.4 Binding über Converter
      ______________2.1.4.5 Binding über DataTemplates
      ______________2.1.4.6 Binding an Collections. Warum ICollectionViewSource? Filtern, Sortieren, Gruppieren ohne viel Aufwand
      ______________2.1.4.7 Validierung von Benutzereingaben
      ______________2.1.4.8 Rücksicht nehmen auf die aktuelle Culture
      ________2.1.5 Dependency Properties (In planing)
      ______________2.1.5.1 Was sind Dependency Properties und wie unterscheiden sie sich von normalen Properties
      ______________2.1.5.2 Eigene DependencyProperties implementieren
      ________2.1.6 Markuperweiterungen
      ______________2.1.6.1 Kurze Theorie
      ______________2.1.6.2 Beispiele Anhand von Resourcen und Styles
      ________2.1.7 Attached Properties
      ______________2.1.7.1 Kurze Theorie (wozu Attached Properties)
      ______________2.1.7.2 Beispiele Anhand vom Grid und dem DockPanel
      ______________2.1.7.3 Eigene Attached Properties erstellen ;)
      ________2.1.8 Attached Events
      ______________2.1.8.1 Wir gehen gleich in die Praxis, es gibt nicht viel zu sagen, soll trotzdem erwähnt sein
      ________2.1.9 Inputs und Commands
      ______________2.1.9.1 Die Input-API
      ______________2.1.9.2 Tastatur und Mausklassen
      ______________2.1.9.3 Eventrouting (Direct, Bubbling, Tunneling)
      ______________2.1.9.4 Keyboard, Mouse und TeytInput
      ______________2.1.9.5 Touch und Multitouch (wird ja immer wichtiger)
      ______________2.1.9.6 Focus (Der Unterschied zwischen Keyboardfocus und Logicalfocus)
      ______________2.1.9.7 Commands (Integrierte und Eigene)
      ______________2.1.9.8 Die RelayCommand Klasse
      ______________2.1.9.9 CommandBinding und CommandParameter
      ___3. Eine Telefonbuch Applikation unter WPF (ohne Binding)
      _____3.1 Hauptfenster erstellen und Funktionen festlegen
      _____3.2 Ein primitives Telefonbuch rein mit CodeBehind ala WinForms
      _____3.3 Fazit
      ___4. Eine Telefonbuch Applikation unter WPF (mit Binding der CodeBehind)
      _____4.1 Umbau der TelefonbuchA Applikation unter WPF (mit Binding der CodeBehind)
      _____4.2 Fazit - Was ist besser, was schlechter
      ___5. Das MVVM Pattern
      _____5.1 Was ist das MVVM Pattern?
      _____5.2 Wann MVVM und wann nicht?
      _____5.3 Welchen Mehrwert kann ich aus dem Pattern gewinnen?
      _____5.4 MVVM und CodeBehind - verboten?
      _____5.5 Model - View - ViewModel - Wars das?
      _____5.6 Erstellen einer korrekten MVVM Projektmappe in VisualStudio
      ___6. Unser Telefonbuch in MVVM
      _____6.1 Projekt anlegen und Struktur besprechen
      _____6.2 Das Model erstellen
      _____6.3 ViewModel - Der Core - was benötigen wir alles ehe wir anfangen können mit unserem Programm
      _____6.4 Wie Messageboxen, Dialoge, MouseCursor oder TaskbarInfo steuern wenn ich die View nicht kenne?
      _____6.5 Jetzt anfangen? Ne? Warum?
      _____6.6 Das MainViewModel erstellen
      _____6.7 ....
      _____6.8 Fazit zum MVVM Pattern
      ___7. Lokalisierung und Globalisierung
      _____7.1 Lokalisierung nur mit Boardmitteln
      _____7.2 Lokalisierung mit schwung (unter zuhilfenahme von zwei NuGet-Paketen)
      _____7.3 Globalisierung (Datum, Währung, usw.)
      _____7.4 Lokalisieren von Werten aus Fremsystemen (DB, XML usw.)
      _____7.5 Gute Hilfsprogramme und Helferlein
      ___8. UnitTests und IntergrationTests
      _____8.1 Wozu UnitTests?
      _____8.2 Wie schreibe ich Tests (Grundlagen)
      _____8.3 Testen unseres ViewModels möglich?
      _____8.4 ...
      _____8.5 ...
      _____8.6 Fazit
      ___9. Repository (DataAccessLayer)
      _____9.1 Noch einen Schritt weiter? Wozu?
      _____9.2 Besprechen und Aufsetzen eines Repositorys
      _____9.3 Wir bauen unser Telefonbuch abermals neu ;)
      _____9.4 Fazit

      Änderungen und streichungen des Inhaltsverzeichnis bzw. Teile davon vorbehalten

      Für Fragen, Anregungne, Kritik, Lob oder Diskussionen gibt es den Supportthread für dieses Tutorial !!
      Antworten hier werden von den Mods in der Supportthread verschoben.
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 18 mal editiert, zuletzt von „Nofear23m“ ()

      1.0 Vorstellung

      1.0

      Vorstellung




      Mein Name ist Patschka Sascha-Heinz, ich bin 1983 geboren und arbeite als EDV Techniker. Beruflich habe ich fast nichts mit der Programmierung zu tun und komme sohin nur privat dazu mich sowohl weiterzubilden als auch mehr Übung zu bekommen.
      Da ich fast von Anfang an unter WPF programmiere und unter WinForms wirklich nur ca. 2-3 Monate gearbeitet habe gab es für mich von Anfang an nur die Richtung zur WPF. Die WPF ist ein sehr leistungsstarkes Framework welches einem nicht nur in Punkto Optik neue Möglichkeiten eröffnet.

      Anfangs hatte ich keine Ahnung von Pattern wie dem MVVM oder anderen, da ich das meiste einfach per "lerning by doing" gelernt habe. Erst nach einigen Jahren aktiver Programmierung unter WPF kam ich zu dem Pattern MVVM. Erstmals totales Neuland mit vielen verschiedenen Ansätzen und Anfangs schwer zu durchschauen, dachte ich mir nicht das dieses Pattern mich irgendwann dazu bringen könnte eine Aussage wie "wenn möglich verwende ich nur noch MVVM in der WPF" zu tätigen, doch seit einiger Zeit ist dies immer öfter der Fall.

      Ich kann sehr gut nachvollziehen wie frustrierend es sein kann mit der WPF zu arbeiten wenn man bereits längere Zeit mit z.B. WinForms gearbeitet hat. Es kann(!) sehr frustrierend sein wenn man nicht auf Anhieb weiterkommt und im Netz finden sich sowohl was die WPF Ansicht und deren Verwendung angeht viele verschiedene Ansätze also auch was das MVVM angeht. Das kann sehr frustrierend sein. Einige davon mehr oder weniger gut und manche leider auch sehr schlecht und gar nicht skalierbar. Ich selbst habe bereits sicher 20 verschiedene Ansätze der Umsetzung eines mehr oder weniger korrekten MVVM Patterns gesehen. Weiteres über MVVM in einem späteren Kapitel.

      Grüße
      Sascha

      Dateien
      • 1.0.pdf

        (290 kB, 162 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

      1.1 Einleitung

      1.1

      Einleitung




      Ich werde absichtlich so wenig wie nur möglich mit Fremdwörtern oder kompliziertem Code um mich werfen. Es soll in dieser Tutorialreihe darum gehen den Code zu verstehen.
      Auch Anfänger sollten den Code lesen und nachbauen können. Evtl. wird auch Code auskommentiert werden und darunter eine andere Möglichkeit geboten wie z.B. eine Schleife gegen eine Lambda Expression vereinfacht werden kann, einfach damit auch Personen welche noch nicht mit Lambda gearbeitet haben verstehen was hier passiert.

      Einige Dinge werden in den Folgekapiteln sicher einfacher gehen oder besser gelöst werden können, hierfür steht der Diskussionsthread zur Verfügung stehen. Auch werde ich nur die wichtigsten Zeilen kommentieren damit bei Anfängern der Lerneffekt nicht ausbleibt. Ich werde in VisualStudio 2017 Update 3 schreiben und die .Net Sprache VB.NET verwenden.
      Falls Ihr Fragen zu diesem Tutorial, den Code oder über mich habt freue ich mich über ein Mail oder eine PM von euch. Auch für Kritik bin ich natürlich immer offen. Mails bitte über die Forumsfunktion über mein Profil oder eine PM hier im Forum bzw. im Supportthread. Ich setze in dieser Tutorialreihe Kenntnisse in der objektorientierten Programmierung voraus und gehe davon aus das die Grundkenntnisse und Syntax von VB.Net soweit bekannt sind.

      1.2

      Aufbau dieser Tutorialreihe



      Es wird ca. 1-mal pro Woche ein Beitrag mit mindestens einem Kapitel hier online gestellt. Es kann vorkommen das auch mal 2 oder mehr Kapitel behandelt werden. Je nachdem wie ich dazu komme und Zeit habe. Falls es vorkommen sollte das ich mal eine Woche auslasse entschuldige ich mich bereits im Voraus dafür, bitte habt Verständnis das ich mal in Urlaub fahre oder beruflich etwas mehr Stress habe.
      Diese Tutorialreihe wird als "Hybrid" aufgebaut. Teile werden als normale Beiträge in reinem Text bzw. mit Bildern erstellt, andere Teile aber auch als Videocast.
      Es wird außerdem für jedes Kapitel ein ZIP File bzw. PDF online gestellt welches das Inhaltsverzeichnis und die Kapitel bis zum aktuellen Zeitpunkt enthält. Außerdem mit in dem ZIP File wenn vorhanden die VisualStudio Solution abwärtskompatibel bis Visual Studio 2015, sowie Links zu den Videos sofern vorhanden.
      Sollte ein Beitrag rein als Text ohne Video erstellt worden sein wird ein PDF mit in der ZIP enthalten sein damit jeder auch offline in Ruhe alles lesen kann.

      Sämtliche Verweise in den Solutions werden nur als NuGet Verweise in das jeweilige Projekt eingebunden um sicherzustellen das die Solution nach dem Download auch bei jedem läuft da NuGet automatisch nachgeladen wird wenn nicht vorhanden. Weitere Infos könnt Ihr hier nachlesen. Warum mit Videos? Man könnte jetzt sagen das ist totaler Quatsch und aus einem Video kann ich nichts rauskopieren und ich kann schwer später gezielt zu einem bestimmten Code springen um mir diesen nochmals anzusehen.
      Aber genau das sollte auch vermieden werden. Ich bin kein Freund von Copy&Paste. Nur wenn ich den Code tippe kann ich versuchen ihn zu lernen und zu verstehen. Auch bin ich der Meinung dass über ein Video viel mehr über die Funktionalität von der IntelliSense vermittelt werden kann. Außerdem kann ich in einem Video schöner gewisse Tastenkombinationen und Tricks vermitteln welche einem das tägliche Leben leichter machen oder einem viel Tipparbeit ersparen wie z.B. bei CodeSnippets.

      Das sind alles Gründe warum ich persönlich ein Video einem normalen Text/Bild Beitrag vorziehe. Bitte entschuldigt wenn ich in meinem Video evtl. mal in meinen österreichischen Dialekt falle. Ich werde mich bemühen so gut wie möglich in einem verständlichen Hochdeutsch zu sprechen.

      Grüße
      Sascha
      Dateien
      • 2.0.pdf

        (476,09 kB, 60 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Nofear23m“ ()

      2.0 Grundlagen der WPF

      2.0

      Grundlagen der WPF




      Wir kommen zu den Grundlagen. Die WPF bietet eine eigene "Deisgnersprache". XAML (Extensible Application Markup Language); ist eine Markupsprache und ist ähnlich im Aufbau wie XML.
      Der Großteil der Benutzeroberfläche wird in der WPF mit XAML erstellt.

      Was ist XAML?
      Im Grunde vereinfacht XAML das Erstellen einer UI einer .NET Anwendung. Es können sichtbare UI-Elemente im deklarativen XAML-Markup erstellt und anschließend die UI-definition mithilfe von Code-Behind, die über partielle Klassendefinitionen an das Markup geknüpft sind, von der Laufzeitlogik trennen.
      Die Darstellung passiert als Text über XML Dateien welche die Erweiterung .xaml aufweisen. Es kann mit jeder Codierung gearbeitet werden, typisch ist jedoch die Codierung als UTF-8.

      Ich drifte aber zu weit ab, hier ein Beispiel für einen XAML - Code:

      VB.NET-Quellcode

      1. <Window xml:Class="MainWindow"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. Title="Window with Button"
      5. Width="250" Height="100">
      6. <!-- Button hinzufügen -->
      7. <Button Name="button">Klick mich!</Button>
      8. </Window>


      Hier werden ein Fenster und eine Schaltfläche mithilfe der Elemente Window und Button definiert. Jedes Element wird mit Attributen konfiguriert und korrespondiert mit den jeweiligen Properties des Elements. Zeile zwei und drei beinhalten die per Default eingetragenen XAML Namespaces, dazu kommen wir auch später mal, da möchte ich im Moment nicht so weit vorgreifen. Ein Window hat ein Property Title und kann hier mit dem Attribut Title geändert werden.
      Geben wir also für den Button beim Attribut 'Name' den Wert 'button' an wird dieser Wert in das Property 'Name' geschrieben. In diesem Fall handelt es sich um ein Dependency Property aber dazu kommen wir mal in einem der Videos zu sprechen.

      So würde dieser Code im Programm aussehen:



      Unter Windows 7 so:



      Im Hintergrund passiert nichts anderes als das die WPF die Objekte anhand Ihrer Attribute und deren Werten erstellt.
      Erstellen wir dieses Fenster mal im Code:

      VB.NET-Quellcode

      1. Dim MainWindow As New Window
      2. MainWindow.Title = "Window with Button"
      3. MainWindow.Width = 250
      4. MainWindow.Height = 100
      5. Dim myButton As New Button
      6. myButton.Name = "button"
      7. myButton.Content = "Klick mich!"
      8. AddHandler myButton.Click, AddressOf Button_Click
      9. MainWindow.Content = myButton


      Wenn wir nun den XAML mit dem Code vergleichen fällt uns ziemlich schnell auf wie die WPF das macht.
      In einem <Button/> wird z.B. Dim myButton As New Button erstellt.
      Jedes Attribut steht für ein Property der jeweiligen Klasse.
      z.B. wird myButton.Name = "button" in XAML zu Name="button"

      Wenn man diese Info hat wird man die XAML Syntax gleich viel schneller verstehen.

      Code Behind

      Ich lese immer wieder in Foren das kein Code Behind verwendet werden soll und das man unter WPF nur MVVM verwenden soll, alles andere wäre Quatsch.
      Doch das ist nicht korrekt, auch in Code von Microsoft und diversen großen Herstellern wie DevExpress, welcher einer der größten Komponentenhersteller im .Net Bereich ist, wird immer wieder über Code Behind gearbeitet. Zweifels ohne ist die WPF auf Binding und gewisse Pattern ausgerichtet wodurch man gewisse Vorteile erlangt wenn man diese verwendet.

      Dennoch ist es so dass ich für eine kleinere Anwendung kein MVVM empfehlen würde. Näheres in einem späteren Kapitel.
      Ähnlich wie bei WinForms kann ich nun einen Handler für den Button_Click erzeugen und in diesem meinen Code schreiben. Wir schreiben das Attribut 'Click' in den Button XAML Code und drücken zweimal Tab.Nun wird folgende Codezeile als Button vorhanden sein:

      XML-Quellcode

      1. <Button Name="button" Click="button_Click_1">Klick mich!</Button>


      Visual Studio hat uns nun den Code in der Code Behind des Windows erstellt.
      Mit einem Rechtsklick auf den Code des Click Attributes können wir nun mit "Gehe zu Definition" direkt zu diesem Code springen.

      Hier steht nun folgendes:

      VB.NET-Quellcode

      1. Private Sub button_Click_1(sender As Object, e As RoutedEventArgs)
      2. End Sub


      Und dies kennen wir nun ja wieder aus WinForms.
      Was anders ist, ist der RoutedEventArg, dies erkläre ich allerdings im Kapitel RoutedEvents, da möchte ich jetzt noch nicht vorgreifen.

      Ein weiteres Beispiel mit Attributen:

      XML-Quellcode

      1. <Button Background="Blue" Foreground="Red" Content="This is a button"/>


      Es wird die Schriftfarbe des Controls auf die Farbe "Red" gesetzt und der Hintergrund des Controls auf "Blue".

      Allerdings gibt es auch Eigenschaften eines Objekts welche nicht über die Attributsyntax gesetzt werden da diese z.B. zu komplex sind. Auf diese kann dann über die Eigenschaftenelementsyntax zugegriffen werden.
      Oft ist es aber auch Geschmacksache oder Übersichtlichkeit für welche Art man sich entscheidet.
      Hier ein Beispiel für das umgestalten des oben stehenden Codes unter Verwendung der Eigenschaftenelementsyntax:

      XML-Quellcode

      1. <Button>
      2. <Button.Background>
      3. <SolidColorBrush Color="Blue"/>
      4. </Button.Background>
      5. <Button.Foreground>
      6. <SolidColorBrush Color="Red"/>
      7. </Button.Foreground>
      8. <Button.Content>
      9. This is a button
      10. </Button.Content>
      11. </Button>


      Dies wäre nun derselbe Button nur das die Properties über die Eigenschaftenelementsyntax gesetzt wurden.
      Jetzt könnte man sich denken: "Wozu gibt es denn die Eigenschaftenelementsyntax wenn ich das mit der Attributsyntax ja auch machen kann?" Hierfür gibt es mehrere Gründe. Einer wäre z.B. dass die Background Eigenschaft eines Buttons ja eigentlich einen Brush erwartet. Warum kann man dann Background="Blue" anwenden? Die WPF stellt intern diverse TypeConverter bereit, so kann sie den String "Red" in einen SolidColorBrush mit der Color "Red" umwandeln. Das gleiche gilt für die Eigenschaft Content des Buttons welche von Typ „Object“ ist.
      Hier macht die WPF automatisch einen String daraus.
      Aber warum soll ich die lange Variante schreiben wenn ich die kurze doch auch schreiben könnte?

      In diesem Fall (SolidColorBrush) geht das noch alles über die Attributsyntax da die WPF den String ja in einen SolidColorBrush wandelt.
      Wenn man nun einen Farbverlauf als Hintergrund verwenden möchte muss man aber schon auf die Eigenschaftenelementsyntax zurückgreifen. z.B.:

      XML-Quellcode

      1. <Button>
      2. <Button.Background>
      3. <LinearGradientBrush>
      4. <LinearGradientBrush.GradientStops>
      5. <GradientStop Offset="0.0" Color="Red" />
      6. <GradientStop Offset="1.0" Color="Blue" />
      7. </LinearGradientBrush.GradientStops>
      8. </LinearGradientBrush>
      9. </Button.Background>
      10. <Button.Foreground>
      11. <SolidColorBrush Color="Red"/>
      12. </Button.Foreground>
      13. <Button.Content>
      14. This is a button
      15. </Button.Content>
      16. </Button>


      Hier wird beim ersten Button für die Hintergrundfarbe ein Farbverlauf gewählt.
      Dieser Farbverlauf geht von oben nach unten von Rot nach Blau. Und zwar gleichmäßig.
      Über das Offset könnte man hier Einfluss auf die "Geschwindigkeit" des Verlaufs nehmen.

      Gehen wir gleich zu den XAML Inhaltseigenschaften.
      Diese sind auch ganz interessant. In der WPF gibt es viele Controls welche ein Property Content oder Child enthalten.
      Oft sind diese Properties vom Typ Object. Da ein Object übergeben werden kann, kann dies im Grunde alles mögliche sein. Nehmen wir wieder als Beispiel den guten alten Button.
      Die folgenden Buttons sehen alle völlig gleich aus obwohl man dies auf den ersten Blick vielleicht nicht vermuten mag.

      XML-Quellcode

      1. <StackPanel>
      2. <Button>Test</Button>
      3. <Button>
      4. <Button.Content>
      5. Test
      6. </Button.Content>
      7. </Button>
      8. <Button Content="Test"/>
      9. <Button>
      10. <TextBlock Text="Test"/>
      11. </Button>
      12. <Button>
      13. <Button.Content>
      14. <TextBlock>
      15. <TextBlock.Text>
      16. Test
      17. </TextBlock.Text>
      18. </TextBlock>
      19. </Button.Content>
      20. </Button>
      21. </StackPanel>


      Hier ein Screenshot:


      XML-Quellcode

      1. <Border BorderThickness="2" BorderBrush="Blue" Margin="10">
      2. <StackPanel>
      3. <UniformGrid Columns="2">
      4. <TextBlock HorizontalAlignment="Center">Text1</TextBlock>
      5. <TextBlock HorizontalAlignment="Center"
      6. Grid.Column="1">Text2</TextBlock>
      7. </UniformGrid>
      8. <Button>
      9. <StackPanel Orientation="Horizontal">
      10. <Image Width="100"
      11. Source="http://www.vb-paradise.de/wcf/images/wbbLogo_vbp.png"/>
      12. <TextBlock Text="Gehe online" Margin="20,0,0,0"/>
      13. </StackPanel>
      14. </Button>
      15. </StackPanel>
      16. </Border>


      Hier wieder ein Screenshot:


      Ich denke das war jetzt erstmal genug Theorie, ich finde das man mit "lerning by doing" einfacher das gelernte behält. Sicher ist es gut wenn man weiß wie die Syntax aufgebaut ist, man muss aber auch damit umgehen können. Die wichtigsten Grundlagen der Syntax habe ich ja aufgezeigt, ich würde sagen wir legen jetzt bald mal los. Beim "basteln" der ersten Anwendung werden sicher viele Fragen bereits beantwortet. Ich werde auch versuchen meine Schritte immer zu kommentieren und euch hin und wieder verschiedene Wege zu zeigen um ans Ziel zu kommen.

      Weitere Grundlagen und Infos findet Ihr hier: Gut in verschiedene Kategorien unterteilt und mit vielen Beispielen. Habe diese Pages damals mehrfach gelesen.

      Ich werde in den nächsten Kapiteln eine Applikation mit reinem Code Behind erstellen und absichtlich auch aufzeigen das man auch ohne Binding in der WPF zurechtkommt, allerdings ist der Komfort nicht gegeben welchen die WPF eigentlich bietet.
      Es soll jetzt niemanden Animieren kein Binding zu verwenden, ich möchte nur aufzeigen das auch dies möglich ist und mich langsam an das Binding herantasten.
      So denke ich, kann man besser umdenken und verstehen wie das Binding funktioniert wenn man beide Wege kennt und parallelen ziehen kann.

      Fragen, Diskussionen, Lob und Kritik wieder im Supportthread!


      Im nächsten Kapitel werde ich das erste Video hochladen. Bis dann Leute :thumbup:
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Nofear23m“ ()

      Einführung in die WPF

      2.1

      Einführung in die WPF


      In diesem Kapitel werden wir ein paar Beispiele durchgehen. Das Hauptaugenmerk wird hier auf XAML gelegt. Wir werden die wichtigsten Controls kennenlernen, wie wir diese Anwenden und auch untereinander kombinieren können um somit ein völlig neues Aussehen und zum Teil neues Verhalten über Trigger und Animations in das Control bekommen. Außerdem werden wir lernen wie wir ein Fenster so gestallten das dieses völlig dynamisch auf Größenänderungen reagiert.

      Dabei werden die Grundlagen von DataBinding in der WPF durchgehen, erst anhand von Bindings innerhalb des Views wie Beispielsweise wie ich ein Property eines Controls auf ein Property eines anderen Controls Binden kann wo wir auch Converter kennenlernen werden aber auch mit Binding an eine selbst geschrieben Klasse bis zum Designtime-Support.


      Dieses Kapitel wird bereits einige Videos enthalten da in einem Video einfach besser Dinge wie Intellisense zur Geltung kommen. Bitte verzeiht mit nochmals wenn ich anfangs vielleicht nicht so geübt rüberkomme.

      2.1.1
      Die wichtigsten Controls und deren Verwendung (Video)



      Wir erstellen ein WPF Projekt und schreiben XAML, wobei wir aber aus gutem Grund auf Drag&Drop in den Designer verzichten werden, warum erkläre ich im Video.
      Wir lernen die wichtigsten Controls kennen und spielen damit dass sich diese an die Fenstergröße anpassen.

      Vorweg: Ich hatte als ich das Video letzte Woche aufnahm noch ein anderes Inhaltsverzeichnis. Bitte verzeiht mir das ich in der Powerpoint noch eine alte Kapitelnummer stehen habe.
      Ich werde mich bemühen solche Fehler in Zukunft zu unterlassen.





      Bitte nutzt für Fragen, Kritik und Lob wieder den SupportThread da dies ein Inkrementelles Tutorial ist!
      Dateien
      • 2.0.pdf

        (737,21 kB, 85 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

      2.1.1.2 - Styles

      2.1.1.2

      Styles, Templates und Trigger. Heute: Styles




      Wir lernen Styles kennen und sehen uns an was es damit auf sich hat.
      Oft soll die Darstellung von Elementen desselben Typs innerhalb einer UI identisch sein. Wie im Web das CSS gibt es in der WPF hierfür Styles. Die Wiederverwendung von Stilen (Styles) erleichtert das Entwickeln und die Wartung eines UI.

      Hier ein Beispiel für einen Style:

      XML-Quellcode

      1. <Style TargetType="{x:Type Button}">
      2. <Setter Property="VerticalContentAlignment" Value="Top" />
      3. <Setter Property="HorizontalContentAlignment" Value="Right" />
      4. <Setter Property="FontWeight" Value="Bold" />
      5. <Setter Property="Margin" Value="0,0,0,5" />
      6. <Setter Property="Background" Value="Aqua"/>
      7. </Style>


      Styles können in XAML mit einem Key versehen oder über den Typ definiert werden. Gibt man einen Key an so kann man bei jedem Steuerelement über das „Style“ Attribut den Style als StaticResource bzw. DynamicResource angegeben.

      XML-Quellcode

      1. <Window.Resources>
      2. <Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
      3. <Setter Property="VerticalContentAlignment" Value="Top" />
      4. <Setter Property="HorizontalContentAlignment" Value="Right" />
      5. <Setter Property="FontWeight" Value="Bold" />
      6. <Setter Property="Margin" Value="0,0,0,5" />
      7. <Setter Property="Background" Value="Aqua"/>
      8. </Style>
      9. </Window.Resources>
      10. <Grid>
      11. <Button Content="Testbutton" Style="{StaticResource myButtonStyle}"/>
      12. </Grid>


      Styles welchen ein „TargetType“ angegeben wird greifen auf jedes Steuerelement dieses Typs unterhalb der Hierarchie. Gibt man in den Window-Resourcen einen Style mit dem TargetType „Button“ an, greift dieser Style auf jeden Button innerhalb dieses Fensters. Auch wenn sich das Steuerelement in einem UserControl befindet welches sich im Fenster befindet greift das Style auf Buttons innerhalb des UserControls. Stichwort: Vererbung.
      Allerdings können jederzeit einzelne Setter eines Styles überschrieben werden. Wenn ein Style die Hintergrundfarbe von Buttons auf BLAU festlegt sind alle Buttons blau. Möchte ich das für einen Button explizit ändern ohne auf das Style für alle anderen Buttons verzichten zu müssen kann ich bei diesem Button einfach mit Background="Green" diesen einen Setter des Style überschreiben, die anderen Setter bleiben allerdings uneingeschränkt vorhanden.

      Auch für Styles habe ich wieder ein Video für euch. 8o



      Viel Spaß mit dem Video, gerne könnt ihr mir ein Like und/oder ein Kommentar hinterlassen und wieder den SupportThread verwenden um Fragen zu stellen.
      Bis zum nächsten Video

      Liebe Grüße
      Sascha
      Dateien
      • 2.1.1.2.pdf

        (770,53 kB, 35 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      2.1.1.2 - Templates

      2.1.1.2

      Styles, Templates und Trigger. Heute: Templates




      Bei Template müssen wir zunächst mal zwischen ControlTemplates, DataTemplates, ItemsPanelTemplates und HirachicalDataTemplates unterscheiden. Es ist erstmal wichtig zu wissen welche Art von Template man gerade benötigt um die aktuelle Aufgabenstellung meistern zu können. Nur wenn man weiß was für ein Template man gerade benötigt wird man über die Suchmaschine seiner Wahl auch korrekte Ergebnisse bekommen und erspart sich erstmal die Suche nach dem richtigen Begriff.

      Fangen wir mit den ControlTemplates an.

      Generell sind Steuerelemente in der WPF mit einer gewissen Logik versehen welche States, Styles, Events, Properties und ein Template beinhalten welche das Aussehen des Steuerelements wie z.b. einen Button definieren/steuern.Die Verbindung zwischen dieser Logik und dem Template passiert über Binding. Jedes Steuerelement besitzt ein Standard – Template welches für jede Windows-Version mitgeliefert wird. Unter Windows 7 sieht ein Button Beispielsweise anders aus als unter Windows 8/10.
      Dieses Template ist verpackt in einem Style, dem DefaultStyleKey und kann überschrieben werden. Diesen Style besitzt jedes UI Steuerelement. Ein Template ist definiert über ein Dependency Property mit dem Namen Template. Durch setzen dieses Property kann das Aussehen eines Steuerelementes völlig neu übernommen werden.

      Hier eine sehr gute Grafik von wpftutorials.net:

      Diese Grafik erklärt sehr gut wie ein solches einfaches Template aufgebaut ist und wie es auf Eigenschaftenänderung reagiert. Siehe Properties IsFocused oder IsEnabled.

      Sehen wir uns eine einfache Überschreibung eines Templates anhand eines Buttons an:

      XML-Quellcode

      1. <Style x:Key="MyButtonStyle" TargetType="Button">
      2. <Setter Property="Template">
      3. <Setter.Value>
      4. <ControlTemplate TargetType="{x:Type Button}">
      5. <Grid>
      6. <Path Data="M 0,0 A 100,100 90 0 0 100,100 L 100,100 100,0" Fill="{TemplateBinding Background}"
      7. Stroke="{TemplateBinding BorderBrush}"/>
      8. <ContentPresenter HorizontalAlignment="Center"
      9. VerticalAlignment="Center"/>
      10. </Grid>
      11. </ControlTemplate>
      12. </Setter.Value>
      13. </Setter>
      14. </Style>




      Hier wird ein ganz einfacher Button erstellt. Dieser besitzt weder Hover noch andere Styleelemente welche einen Button auszeichnen. Beispielsweise möchten wir ja wenn wir den Button klicken, dass dieser richtig hineingedrückt wird. Wenn ein Button „Disabled“ ist soll er ausgegraut sein. All diese Funktionalitäten Fehlen hier.

      Im folgenden Video sprechen wir nun darüber wie wir dies bewerkstelligen.
      Außerdem zeige ich euch wie ihr das Default-Template eines jeden Controls erfahren und anpassen könnt. Dies ist insofern Praktisch, wenn Ihr ein anderes Verhalten eines Buttons bewerkstelligen wollt, ohne ein neues Control erstellen zu müssen.

      Nun zu den DataTemplates

      DataTemplates helfen und dabei gewisse Daten oder Klassen so darzustellen wie wir das möchten. Beispielsweise Binden wir eine Property welche Autos beinhaltet an eine ListBox.Autos enthält viele Instanzen von der Klasse Auto. Die ListBox versucht nun mit den Daten etwas anzufangen und sucht nach einem DataTemplate. Erst bei sich selbst, dann in den Resourcen von darüber liegenden Controls, bis in der Hierarchie nichts mehr vorhanden ist, dann sucht sie in der Application.xaml nach einem DataTemplate.

      Nehmen wird eine einfache Klasse,- sagen wir mal diese soll ein Auto enthalten.
      Also erstellen wir uns eine Klasse „Auto“.

      VB.NET-Quellcode

      1. Public Class Auto
      2. Public Sub New()
      3. End Sub
      4. Public Sub New(marke As String, modell As String, ps As Integer)
      5. Me.Marke = marke : Me.Modell = modell : Me.PS = ps
      6. End Sub
      7. Public Sub New(marke As String, modell As String, ps As Integer, logo As Uri)
      8. Me.Marke = marke : Me.Modell = modell : Me.PS = ps : Me.Logo = logo
      9. End Sub
      10. Public Property Marke As String
      11. Public Property Modell As String
      12. Public Property PS As Integer
      13. Public Property Logo As String
      14. End Class


      In der CodeBehind des MainWindow sorgen wird jetzt einfach dafür das eine ObservableCollection (Auflistung) mit verschiedenen Auto`s befüllt wird.

      VB.NET-Quellcode

      1. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
      2. AutoListe = New ObservableCollection(Of Auto)
      3. With AutoListe
      4. .Add(New Auto("Audi", "R8 V10+", 610, "BrandImages/Audi.png"))
      5. .Add(New Auto("VW", "Arteon TDI 4 Motion", 240, "BrandImages/Vw.png"))
      6. .Add(New Auto("Seat", "Leon ST Cupra TSI DSG", 300, "BrandImages/SEAT.png"))
      7. .Add(New Auto("Skoda", "Octavia RS", 230, "BrandImages/Skoda.png"))
      8. .Add(New Auto("Lamborghini", "Aventador LP 700", 700, "BrandImages/Lambo.png"))
      9. .Add(New Auto("Bentley", "Continental Supersports", 630, "BrandImages/Bentley.png"))
      10. End With
      11. Me.DataContext = Me
      12. End Sub
      13. Public Property AutoListe As ObservableCollection(Of Auto)


      Im View (MainWindows.xaml) müssen wir nun nur noch eine ListBox erstellen und diese auf das Property „AutoListe“ binden. Fertig.
      Aber was sehen wir nun genau?

      Hier eine ListBox gebunden an Autos ohne DataTemplate:


      Nicht so schön. Wieder schlägt der TypeConverter der WPF zu.
      Die WPF wirft keinen Fehler sondern versucht die Daten welche ihr (ist die WPF ne Frau?) übergeben werden einfach irgendwie zu Rendern. Was liegt näher als .ToString aufzurufen. Und genau das macht sie.

      Wir könnten nun die ToString-Methode in der Klasse „Auto“ überschreiben um einen „verständlichen“ Text in die ListBox zu bekommen.

      VB.NET-Quellcode

      1. Public Overrides Function ToString() As String
      2. Return $"Marke: {Marke }, Modell: {Modell } Leistung: {PS }"
      3. End Function


      Was uns folgendes Ergebnis liefert:


      OK, schon um einiges besser, aber lange noch nicht zufriedenstellend. Wir befinden uns in der WPF, das geht ja mal sicher viel besser und schöner oder?
      Richtig. Mittels DataTemplate kann man nun bestimmen wie das Template für ein ListItem aussehen soll. Template = Vorlage. Also erstellen wir eine Vorlage und geben der WPF die Info für was (die Klasse Auto) dieses Template verwendet werden soll.

      Hier ein DataTemplate direkt in das ItemTemplate der ListBox:

      XML-Quellcode

      1. <ListBox ItemsSource="{Binding AutoListe}" Margin="10">
      2. <ListBox.ItemTemplate>
      3. <DataTemplate>
      4. <Grid>
      5. <Grid.ColumnDefinitions>
      6. <ColumnDefinition Width="60"/>
      7. <ColumnDefinition Width="*"/>
      8. </Grid.ColumnDefinitions>
      9. <Border Margin="5" BorderBrush="Black" BorderThickness="1">
      10. <Image Source="{Binding Logo}" Stretch="Uniform" Width="50" Height="50" RenderOptions.BitmapScalingMode="HighQuality" />
      11. </Border>
      12. <StackPanel Grid.Column="1" Margin="5">
      13. <StackPanel Orientation="Horizontal">
      14. <TextBlock Text="{Binding Path=Marke}" />
      15. <TextBlock Text="{Binding Path=Modell}" Padding="3,0,0,0"/>
      16. </StackPanel>
      17. <TextBlock FontWeight="Bold" >
      18. <Run Text="{Binding PS,FallbackValue=0}"/>
      19. <Run Text=" PS"/>
      20. </TextBlock>
      21. </StackPanel>
      22. </Grid>
      23. </DataTemplate>
      24. </ListBox.ItemTemplate>
      25. </ListBox>


      Dieses DataTemplate ändert nun das Aussehen eines ListBox-Items wie folgt:



      Mehr im Video weiter unten, aber jetzt gehen wir mal weiter zu dem ItemsPanelTemplates:

      ItemsPanelTemplates

      Diese Art von Templates erlaubt es uns das Layout, wie Items eines ItemControls wie z.b. einer ListBox zu bestimmen. Jeder ItemsControl besitzt von Haus aus ein „Default Panel“.

      Die ListBox z.b. besitzt von Haus aus ein VirtualizingStackPanel als PanelTemplate.
      Dieses ist im Grunde ein normales StackPanel welches ein Zusatzfeature besitzt, welches ermöglicht das nicht alle Elemente sofort gerendert werden sondern erst dann wenn diese z.b. durch Scrollen auftauchen was Resourcen spart und stark auffällt wenn man mehrere tausend Items in der Liste hat.

      Um dieses Template nun zu überschreiben, erstellen wir einfach ein ItemsPanelTemplate und suchen uns ein Panel-Control aus welches uns besser passt.

      XML-Quellcode

      1. <ListBox ItemsSource="{Binding AutoListe}">
      2. <ListBox.ItemsPanel>
      3. <ItemsPanelTemplate>
      4. <UniformGrid Columns="4"/>
      5. </ItemsPanelTemplate>
      6. </ListBox.ItemsPanel>
      7. </ListBox>


      In diesem Fall nehmen wir jetzt mal ein UniformGrid mit vier Spalten. Dieses UniformGrid wird nun die Items auf vier Spalten aufteilen. Das fünfte Element würde dann in eine neue Zeile verschoben werden.

      Mit dem Codeschnipsel von oben würde nun folgendes generiert werden:


      Wir können sehen das wir vier Spalten und zwei Zeilen haben. Wieder hat die WPF unsere .ToString Methode in der Klasse Auto aufgerufen.
      Aber das wir DataTemplates oben bereits durchgenommen haben ist es für uns ja jetzt ein leichtes ein solches DataTemplate abermals zu implementieren.
      Wir können gleich unser DataTemplate von oben auch in dieser ListBox verwenden, mit dem Unterschied das wir das soeben erstellte ItemsPanelTemplate auch mit in der ListBox behalten:

      XML-Quellcode

      1. <ListBox ItemsSource="{Binding AutoListe}">
      2. <ListBox.ItemsPanel>
      3. <ItemsPanelTemplate>
      4. <UniformGrid Columns="4"/>
      5. </ItemsPanelTemplate>
      6. </ListBox.ItemsPanel>
      7. <ListBox.ItemTemplate>
      8. <DataTemplate>
      9. <Grid>
      10. <Grid.ColumnDefinitions>
      11. <ColumnDefinition Width="60"/>
      12. <ColumnDefinition Width="*"/>
      13. </Grid.ColumnDefinitions>
      14. <Border Margin="5" BorderBrush="Black" BorderThickness="1">
      15. <Image Source="{Binding Logo}" Stretch="Uniform" Width="50" Height="50" RenderOptions.BitmapScalingMode="HighQuality" />
      16. </Border>
      17. <StackPanel Grid.Column="1" Margin="5">
      18. <StackPanel Orientation="Horizontal">
      19. <TextBlock Text="{Binding Path=Marke}" />
      20. <TextBlock Text="{Binding Path=Modell}" Padding="3,0,0,0"/>
      21. </StackPanel>
      22. <TextBlock FontWeight="Bold" >
      23. <Run Text="{Binding PS,FallbackValue=0}"/>
      24. <Run Text=" PS"/>
      25. </TextBlock>
      26. </StackPanel>
      27. </Grid>
      28. </DataTemplate>
      29. </ListBox.ItemTemplate>
      30. </ListBox>


      Hierdurch erhalten wir wieder die Optik unseres Items aber in der Anordnung wie wir es im ItemsPanelTemplate vorgegeben hatten:


      Wir sehen, alle Elemente werden so gerendert wie wir das haben wollten und in dem Schema wie wir dies vorgegeben haben. So eröffnen sich tolle möglichkeiten für eine neue Anwendungsoptik und eine bessere und flexiblere Benutzerführung.
      Langsam dürfte wiedermal ein kleiner WOW-Effekt einsetzen was nun alles möglich ist mit ein paar Zeilen XAML ;)

      Last but not least, HierarchialDataTemplates

      Mit HierarchialDataTemplate wie der Name vermuten lässt kann ich bestimmen wie Daten welche in Form einer Hierarchie vorliegen darstellen.
      Tja, wo liegt nun der Einsatz von einem solchen Template? Z.b. bei der Verwendung eines TreeViews oder eines Menus.

      Wir alle kennen ein Menu:


      Erstmal haben wir die erste Ebene (Root) mit „Datei“, „Bearbeiten“, Ansicht“, usw.
      Danach haben wir eine Unbestimmte Anzahl an Ebenen welche wieder eine unbestimmte Anzahl an weiteren Kind-Ebenen haben kann.

      Auch für solch einen Fall hat die WPF vorgesorgt, und ermöglicht uns das Binding.
      Damit wir allerdings auch bestimmen können wie die Darstellung passiert gibt es eben die HierarchialDataTemplates. Besser darstellen lässt sich solch ein unterfangen mit einem TreeView.

      Als Fallbeispiel nehmen wir mal eine Art Stammbaum.
      Wie soll es anders sein nehme ich den Stammbaum der Familie Porsche/Piech:

      Erstmal erstelle ich zwei Klassen. Zum einen die Klasse „Parent“ und zum anderen die Klasse „Child“. Ich denke die Namen sprechen für sich.


      Jedes Parent kann X Childs (Kinder) haben. Jedoch kann jedes ChildElement wieder X Childs (Kinder) haben, usw. Füllen wir diese Klassen mit ein paar Daten von hier und konzentrieren wir uns wieder auf den XAML.
      Wir erstellen ein TreeView und geben ein HierarchialDataTemplate an:

      XML-Quellcode

      1. TreeView DataContext="{Binding}" ItemsSource="{Binding HierarchieDaten}">
      2. <TreeView.ItemTemplate>
      3. <HierarchicalDataTemplate ItemsSource="{Binding Kinder}">
      4. <StackPanel>
      5. <StackPanel Orientation="Horizontal">
      6. <Rectangle Width="10" Height="10" Fill="Red"/>
      7. <TextBlock Text="{Binding VollerName}"/>
      8. </StackPanel>
      9. <TextBlock>
      10. <Run Text="Gegr.: "/>
      11. <Run Text="{Binding GruenderVon}"/>
      12. </TextBlock>
      13. </StackPanel>
      14. </HierarchicalDataTemplate>
      15. </TreeView.ItemTemplate>
      16. </TreeView>


      Das sieht ja gar nicht so kompliziert aus. Das TreeView selbst haben wir an ein Property mit dem Namen „HierachieDaten“ gebunden. Dieses befindet sich in der CodeBehind und ist vom Typ Parent.
      Wie wir im Diagramm sehen konnten hat dieses ein Property Kinder. Dieses kann viele Child halten und Child wiederum auch viele Childs.

      Beim Erstellen des HierarchialDataTemplates geben wird für dieses dann als ItemsSource das Property an welches innerhalb der Elternklasse die Childs hält. Und der Rest ist einfach nur XAML welches bestimmt wie ein TreeViewItem dann für dieses Template aussehen soll.

      Folgende Ansicht wird in diesem Fall generiert:


      Soweit sieht das ja schon ganz gut aus. Wie wir aber sehen können hat nicht jedes Kind der Familie wieder etwas oder eine Marke gegründet. Da wir aber im Template hinterlegt haben das ein Item so gerendert werden soll wird uns der Text „Gegr.:“ angezeigt, da die Klasse Child allerdings kein solches Property besitzt kann die WPF gar nicht darauf Binden.

      Dies macht sich auch im Ausgabefenster bemerkbar:


      OK, wir wissen also das wir nicht am Holzweg sind, aber es hier noch Luft nach oben gibt, es muss ja möglich sein für jede Art von Element eine eigene Optik zu definieren.
      Naja, vorher hatten wir gerade noch die DataTemplates gelernt, das wäre doch eine Möglichkeit gleich das gelernte anhand dieses Beispiels zu versuchen.

      Wir müssen also ein wenig umdenken. Wir möchten mehrere HierarchialDataTemplates definieren, werden jedoch scheitern wenn wir versuchen noch ein Template darunter zu erstellen. Wir versuchen es in den Ressourcen:

      XML-Quellcode

      1. <TreeView DataContext="{Binding}" ItemsSource="{Binding HierarchieDaten}">
      2. <TreeView.Resources>
      3. <HierarchicalDataTemplate ItemsSource="{Binding Kinder}" DataType="{x:Type local:Parent}">
      4. <StackPanel>
      5. <StackPanel Orientation="Horizontal">
      6. <Rectangle Width="10" Height="10" Fill="Red"/>
      7. <TextBlock Text="{Binding VollerName}"/>
      8. </StackPanel>
      9. <TextBlock>
      10. <Run Text="Gegr.: "/>
      11. <Run Text="{Binding GruenderVon}"/>
      12. </TextBlock>
      13. </StackPanel>
      14. </HierarchicalDataTemplate>
      15. <HierarchicalDataTemplate ItemsSource="{Binding Kinder}" DataType="{x:Type local:Child}">
      16. <StackPanel Orientation="Horizontal">
      17. <Rectangle Width="10" Height="10" Fill="Blue"/>
      18. <TextBlock Text="{Binding VollerName}"/>
      19. </StackPanel>
      20. </HierarchicalDataTemplate>
      21. </TreeView.Resources>
      22. </TreeView>


      Wenn wir uns dieses TreeView ansehen werden wir die Stirn runzeln.
      Zwei Templates? JA, das erste Template dient zur Anzeige eines Parent-Objekts. Zu erkennen an der Angabe des DataType: DataType="{x:Type local:Parent}"
      Das zweite Template dient zur Anzeige des Child-Objekts: DataType="{x:Type local:Child}"
      Die WPF entscheidet nun anhand des Übergebenen Typs jedes Knotens wie dieser gerendert werden soll.

      Hier das Ergebnis:


      Man kann schön erkennen das beim Child-Knoten nicht nur das Quadrat in einer anderen Farbe erstrahlt sondern auch das hier der TextBlock mit „Gegr.:“ fehlt, da wir diesen TextBlock ins Child-Template nicht mit eingebaut haben.
      Eine schöne und saubere Sache.

      In folgendem Video gehe ich etwas näher darauf ein und zeige euch verschiedene Templates anhand von einigen Beispielen:



      Ich wünsche euch viel spass mit dem Video, bitte hinterlasst mir ein Like und/oder ein Kommentar, und verwendet für Fragen gerne den Supportthread.
      Anbei die Sulution und wieder das aktuellste PDF.


      Grüße und bis zum nächsten Tutorial.
      Sascha
      Dateien
      • WPF_Templates.zip

        (1,05 MB, 49 mal heruntergeladen, zuletzt: )
      • 2.1.1.2_#2.pdf

        (964,8 kB, 54 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

      2.1.1.2

      Styles, Templates und Trigger. Heute: Trigger




      Auch bei Triggern müssen wir wieder unterscheiden da es hier mehrere Arten von Triggern gibt.
      Folgende Trigger gibt es:
      • Trigger
      • DataTrigger
      • Multitrigger
      • MultiDataTrigger
      • EventTrigger
      Trigger werden überwiegend in Styles oder ControlTemplates verwendet. Er Triggert ein Property auf ein anderes Property dieses Controls.
      Beispiel: Ein Trigger soll die Hintergrundfarbe des Buttons auf ROT setzen wenn das Property IsMouseOver True ist.

      XML-Quellcode

      1. <Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
      2. <Style.Triggers>
      3. <Trigger Property="IsMouseOver" Value="True">
      4. <Setter Property="Background" Value="Red" />
      5. </Trigger>
      6. </Style.Triggers>
      7. </Style>


      Ein DataTrigger wird bei Datenbindung eingesetzt und wird überwiegend in DataTemplates verwendet. Beispielsweise kann ein DataTrigger verwendet werden um die Hintergrundfarbe eines Controls in ROT zu ändern wenn das Property Alert der Klasse True ist. DataTrigger können allerdings auch in ControlTemplates nützlich sein um einen roten Rahmen um ein Controls zu machen wenn in der Klasse Kontoinfo das Property Kontostand weniger als 0 Beträgt. Hierfür kann ein IValueConverter bestimmten das bei negativen Werten der Converter True zurückgibt und sohin der Rahmen Rot gezeichnet wird.

      XML-Quellcode

      1. <DataTrigger Binding="{Binding Alert}" Value="True">
      2. <Setter Property="Background" Value="Red"></Setter>
      3. </DataTrigger>


      MultiTrigger und MultiDataTrigger sind im Grunde dasselbe wie die beiden Triggerarten oben nur das mehrere Bedingungen angegeben werden können. Ein MultiDataTrigger würde dann nur greifen wenn alle Bedingungen erfüllt sind.

      XML-Quellcode

      1. <DataTemplate.Triggers>
      2. <MultiDataTrigger>
      3. <MultiDataTrigger.Conditions>
      4. <Condition Binding="{Binding Path=Picture}" Value="{x:Null}" />
      5. <Condition Binding="{Binding Path=Title}" Value="Waterfall" />
      6. </MultiDataTrigger.Conditions>
      7. <MultiDataTrigger.Setters>
      8. <Setter TargetName="viewImage" Property="Source" Value="/Images/noImage.png"/>
      9. <Setter TargetName="viewImage" Property="Opacity" Value="0.5" />
      10. <Setter TargetName="viewText" Property="Background" Value="Brown" />
      11. </MultiDataTrigger.Setters>
      12. </MultiDataTrigger>
      13. </DataTemplate.Triggers>


      EventTrigger schließlich sind Trigger welche auf ein Event reagieren können.
      Beispiel: Der Hintergrund eines Buttons soll sich ändern wenn sich die Maus über den Button fährt. EventTrigger werden Beispielsweise oft in Storyboards verwendet um z.b. eine Animation zu starten.

      XML-Quellcode

      1. <Border Name="border1" Width="100" Height="30"
      2. BorderBrush="Black" BorderThickness="1">
      3. <Border.Background>
      4. <SolidColorBrush x:Name="MyBorder" Color="LightBlue" />
      5. </Border.Background>
      6. <Border.Triggers>
      7. <EventTrigger RoutedEvent="Mouse.MouseEnter">
      8. <BeginStoryboard>
      9. <Storyboard>
      10. <ColorAnimation Duration="0:0:1"
      11. Storyboard.TargetName="MyBorder"
      12. Storyboard.TargetProperty="Color" To="Gray" />
      13. </Storyboard>
      14. </BeginStoryboard>
      15. </EventTrigger>
      16. </Border.Triggers>
      17. </Border>


      Da Animationen wiederum ein oder mehrere Kapiteln füllen würde, ich darauf aber nicht näher eingehen werde da ich der meinung bis das diese eigentlich relativ selten gebraucht werden, hier ein Link wo Ihr alle Wissenswertes über Animationen gut erklärt bekommt. WPF Basic Animations
      Wer sich hier durchklickt kommt bald zu den Easing Functions, zu den Key Frame Animations und schließlich zu den PathAnimations

      In folgendem Video zeige ich euch nun wie wir mit Triggern umgehen und wie wir das Verhalten von Controls beeinflussen können ohne viele, viele Codezeilen schreiben zu müssen. Selbst wenn wir Converter schreiben bestehen diese meist nur aus 1-5 Zeilen Code. Eine saubere und übersichtliche Sache.



      Viel Spaß mit dem Video, gerne könnt ihr mir ein Like und/oder ein Kommentar hinterlassen und wieder den SupportThread verwenden um Fragen zu stellen.
      Bis zum nächsten Video

      Liebe Grüße
      Sascha
      Dateien
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ () aus folgendem Grund: Anhänge eingefügt und Links korrigiert.

      2.1.1.3

      Controls eine neue Optik verpassen, inkl. deren Verhalten




      In den vorigen drei Kaptiteln haben wir erfahren wie wir mittels Styles, Triggern und Templates Controls anpassen. In jedem dieser Kapitel haben wir mehr oder weniger entweder das Aussehen oder das Verhalten eines Controls beeinflussen können. Nun ist es an der Reihe das erlernte wissen umzusetzen und ein Control komplett zu überarbeiten.

      Ich habe mir die Checkbox ausgesucht, einfach weil dies ein Control ist welchen von Haus aus nicht unbedingt sehr gut aussieht und meisst als erstes abgeändert werden möchte.
      Was wollen wir erreichen:

      Jeder kennt eine Checkbox:


      Diese ist nicht sonderlich schön. Ich dachte mir, wir machen eine Kopie des SwitchButton aus dem MobileOS Android:


      In folgendem Video erkläre ich euch wie Ihr eine Checkbox in genau dieser Optik erstellt. Ohne eine Zeile VB Code. Inkl. Animation des "Knopfes". Also das wenn die Checkbox ihren State ändert oder der User darauf klickt der Knopf nach links oder nach rechts "slidet".
      Wie Ihr euch sicher vorstellen könnt kann all dies über ein ControlTemplate erledigt werden.

      Viel spass mit dem Video:



      Für die die mit einem Video nicht viel anfangen habe ich natürlich wieder das Projekt angehängt.


      Viel Spaß mit dem Video, gerne könnt ihr mir ein Like und/oder ein Kommentar hinterlassen und wieder den SupportThread verwenden um Fragen zu stellen.
      Bis zum nächsten Video

      Liebe Grüße
      Sascha
      Dateien
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()

      2.1.2

      XAML Namespaces




      In diesem Thema widmen wir uns den XAML-Namespaces.
      Wir klären was es mit den Namespaces auf sich hat und wie wir diese Verwenden und eigene Namespaces definieren.

      Was ist ein XAML-Namespace?
      Wie in XML-Namespaces ist ein XAML-Namespace eine Erweiterung dieses Konzepts.
      Eine etwas technische Beschreibung des Begriffs gibt es auf MSDN unter folgenden Link: msdn.microsoft.com/de-de/library/ms747086(v=vs.110).aspx
      Ich möchte es allerdings etwas verständlicher beschreiben. Im Grunde kann man sich einen Namespaceimport vorstellen wie in VB einen "Import" im Code. Eine ganz gute Beschreibung findet man auch hier im Forum vom User @ErfinderDesRades unter folgendem Link: XAML Syntax
      Im Grunde kann jeder CLR Namespace auch in XAML eingebunden werden. Beispiel:

      In VB.Net kann auch ein Import eines Namespace mit einem Präfix definiert werden.

      VB.NET-Quellcode

      1. Imports FileImport = System.IO
      2. Module Module1
      3. Sub Main()
      4. If FileImport.File.Exists(path) Then
      5. 'Do something
      6. End If
      7. End Sub
      8. End Module


      Solch ein Import im Code könnte wie folgt in XAML aussehen:

      XML-Quellcode

      1. xmlns:FileImport="clr-namespace:System.IO;assembly=mscorlib"


      Der Sinn eines Imports des System.IO Namespace sei jetzt mal dahingestellt, damit soll nur gezeigt werden das wirklich jeder beliebige Namespace eingebunden werden könnte wie folgender Screenshot zeigt:



      In einem Window sehen wir auch immer einen Namespace (im obigen Beispiel in der zweiten Zeile) welcher keinen Präfix hat.

      XML-Quellcode

      1. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"


      Dieser Namespace wird als „Default“ gesehen. Es muss also kein Präfix vorangestellt werden. Beispielsweise befinden sich alle WPF Controls in diesem Namespace wie z.b. ein Button weshalb man in XAML einfach <Button/> schreiben kann um einen Button zu erstellen.
      Jetzt fällt uns an diesem Beispiel aber auch auf das wir Namespaces über den CLR Namen eingebunden haben und andere wiederum mit einer URL. Aber was hat die URL zu bedeuten?
      Dazu später mehr...

      Erstellung eigener Namespaces

      Wir erstellen uns im Projekt eine Klasse mit dem Namen FahrzeugeVm sowie eine Klasse Fahrzeug.
      Die FahrzeugeVm-Klasse enthält ein Property mit dem Namen Fahrzeuge vom Typ ObservableCollection(Of Fahrzeug).
      Fahrzeuge kann also viele Instanzen von Fahrzeug enthalten. Beide Klassen befinden sich im Namespace ViewModel unseres Projekts.
      Ich setzte jetzt mal voraus das jeder Namespaces in VB.Net kennt und weis was es mit Namespaces auf sich hat und wie diese funktionieren. Ansonsten kann dies hier in den Microsoft Docs nachgelesen werden.

      Zur Vereinfachung habe ich hier beide Klassen in eine Datei geschrieben.

      VB.NET-Quellcode

      1. Imports System.Collections.ObjectModel
      2. Namespace ViewModel
      3. Public Class FahrzeugeVm
      4. Public Sub New()
      5. Fahrzeuge = New ObservableCollection(Of Fahrzeug) From {
      6. New Fahrzeug() With {.Marke = "Volkswagen", .Modell = "Passat", .LeistungPs = 128},
      7. New Fahrzeug() With {.Marke = "Seat", .Modell = "Ibiza", .LeistungPs = 89},
      8. New Fahrzeug() With {.Marke = "Audi", .Modell = "A3", .LeistungPs = 150}}
      9. End Sub
      10. Public Property Fahrzeuge As ObservableCollection(Of Fahrzeug)
      11. End Class
      12. Public Class Fahrzeug
      13. Public Property Marke As String
      14. Public Property Modell As String
      15. Public Property LeistungPs As Integer
      16. Public Overrides Function ToString() As String
      17. Return $"{Marke} {Modell} hat {LeistungPs} PS"
      18. End Function
      19. End Class
      20. End Namespace


      In einem Window möchten wir die Fahrzeuge in einer ListBox darstellen damit wir alle Fahrzeuge sehen können. Wir erstellen uns in unserem Fenster eine ListBox.

      XML-Quellcode

      1. <Window x:Class="MainWindow"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      6. xmlns:local="clr-namespace:_2_1_2_2_XamlNamespaces"
      7. mc:Ignorable="d"
      8. Title="MainWindow" Height="150" Width="200">
      9. <Grid>
      10. <ListBox SelectedIndex="0"/>
      11. </Grid>
      12. </Window>


      Als nächstes möchten wir für unser Window einen Datencontext (DataContext) festlegen damit wir die ListBox darauf binden und sohin die Fahrzeuge anzeigen lassen können.
      Wir fügen also erstmal unseren Namespace ViewModel in dem XAML Namespaces an und vergeben einen Präfix. Ich entscheide mich jetzt mal für den Präfix vm.

      XML-Quellcode

      1. xmlns:vm="clr-namespace:_2_1_2_2_XamlNamespaces.ViewModel"


      Dann sieht unser XAML Code folgendermaßen aus:

      XML-Quellcode

      1. <Window x:Class="MainWindow"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      6. xmlns:local="clr-namespace:_2_1_2_2_XamlNamespaces"
      7. xmlns:vm="clr-namespace:_2_1_2_2_XamlNamespaces.ViewModel"
      8. mc:Ignorable="d" Title="MainWindow" Height="150" Width="200">
      9. <Grid>
      10. <ListBox SelectedIndex="0"/>
      11. </Grid>
      12. </Window>


      Auch hier haben wir IntelliSense wenn wir zwischendurch mal Kompiliert haben.
      Einfach: xmlns:vm= eingeben und es erscheint eine Liste von verfügbaren Namespaces.
      Hier muss der Namespace auch nicht mühselig rausgesucht werden. Einfach beginnen „viewmodel“ einzugeben und schon werden die Ergebnisse gefiltert. Siehe Video zu diesem Kapitel.



      Praxistip: Habt ihr mal einen Namespace oder ein Property nicht in der IntelliSense kann dies daran liegen dass zwischenzeitlich nicht kompiliert wurde. Hin und wieder mal das komplette Projekt zu kompilieren
      (im Menu Erstellen -> Projektmappe erstellen oder mit der Tastenkombination STRG + SHIFT + B kann oft „wunder“ wirken.



      Jetzt haben wir unseren eigenen Namespace in XAML eingebunden und können nun unseren DataContext für unser Window setzen, denn unsere FahrzeugeVm-Klasse möchten wir nun als Grundlage für unser Fenster verwenden.
      Das machen wir indem wir den DataContext unseres Fensters setzen mit:

      XML-Quellcode

      1. <Window.DataContext>
      2. <vm:FahrzeugeVm/>
      3. </Window.DataContext>


      Ab jetzt hat das Fenster, und durch die Vererbung alle dem Fenster untergeordneten Objekte unsere FahrzeugeVm Klasse als Datenkontext und können somit auf dessen Eigenschaften zugreifen was in unserem Fall ja „nur“ die Eigenschaft „Fahrzeuge“ ist.
      Unser Window sieht nun wie folgt aus:

      XML-Quellcode

      1. <Window x:Class="MainWindow"
      2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      6. xmlns:local="clr-namespace:_2_1_2_2_XamlNamespaces"
      7. xmlns:vm="clr-namespace:_2_1_2_2_XamlNamespaces.ViewModel"
      8. mc:Ignorable="d" Title="MainWindow" Height="150" Width="200">
      9. <Window.DataContext>
      10. <vm:FahrzeugeVm/>
      11. </Window.DataContext>
      12. <Grid>
      13. <ListBox SelectedIndex="0"/>
      14. </Grid>
      15. </Window>


      Wenn wir nun unsere ListBox an unser Property in der FahrzeugeVm Klasse binden möchten müssen wir nur das ItemsSource Property der ListBox an das Property Fahrzeuge in unserem DatenContext Binden.

      Es wird euch sicher aufgefallen sein das wir hier kein IntelliSense hatten als wird Das Binding eingegeben haben. Das liegt daran das wir zwar den DatenContext für die Laufzeit eingegeben haben aber keinen für die DesignTime (Entwicklungszeit).
      Dies kommt zwar noch genauer in einem späteren Kapitel aber ich zeige es hier schon mal schnell vor:
      Innerhalb des Window-Knoten geben wir wie folgt den DesigntTime-DataContext an:

      XML-Quellcode

      1. d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type={x:Type vm:FahrzeugeVm}}"


      Wie man sieht machen wir auch hier wieder Gebrauch von unserem Namespace.
      Jetzt haben wir auch zum Binden die IntelliSense zur Verfügung. Starten wir nun unser Programm sehen wir die Einträge in der ListBox:



      Vorhin in diesem Kapitel haben wir ja bereits gesehen das es auch Namespaces gibt welche mit einer URI statt mit dem CLR Namespace angegeben werden, dies kann mehrere Vorteile haben.
      Der wohl größte ist das man hierbei mehrere Namespaces zusammenführen kann.

      Beispiel: Der per Default ohne Präfix in jedem Window vorhandene Namespace enthält alle Controls welche es für die WPF von Microsoft Seite gibt. Aber ihr könnt euch vorstellen dass sich nicht alle Controls in ein und demselben CLR Namespace befinden.
      Damit man nicht X Namespaces in jedem Window immer und immer wieder einbinden muss gibt es diese Möglichkeit. Beim Namespace http://schemas.microsoft.com/winfx/2006/xaml/presentation beispielsweise sind z.b. unter anderem die Namespaces System.Windows und System.Windows.Controls eingebunden.
      Geben wir die URL in den Browser ein bekommen wir entweder eine 404 Antwort (Die Seite kann nicht gefunden werden) oder wir bekommen einfach einen Text das die Seite nicht verfügbar ist.
      Aber wie weis die WPF was man mit der URL meint, und viel wichtiger, woher kommt die URL?

      Dies wird klarer, wenn wir selbst versuchen einen solchen Namespace zu definieren. Im folgenden Video werden wir das soeben gelesene nochmals durchgehen und auch Namespaces mit URI definieren und einsetzen.

      Viel spass mit dem Video:



      Für die die mit einem Video nicht viel anfangen habe ich natürlich wieder das Projekt angehängt.


      Viel Spaß mit dem Video, gerne könnt ihr mir ein Like und/oder ein Kommentar hinterlassen und wieder den SupportThread verwenden um Fragen zu stellen.
      Bis zum nächsten Video

      Liebe Grüße
      Sascha
      Dateien
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      2.1.3 Resourcen

      2.1.2

      Resourcen



      Was sind Resourcen und was bringen sie mir

      Grundsätzlich gibt es in einer Anwendung zwei Arten von Resourcen. Die XAML Resourcen und die Anwendungsresourcen.
      Eine Resource ist ein Objekt welches an unterschiedlichen Stellen einer Anwendung erneut verwendet werden kann. Die Anwendungsresourcen kennt Ihr sicher aus WinForms auch, dies sind nicht ausführbare Datendateien die eine Anwendung zur Laufzeit benötigt wie z.b. ein Icon oder Bild.
      Ein Beispiel für XAML Resourcen sind Brushes oder Styles. Diese können an einer Stelle definiert werden und stehen anschließend zur Verfügung. In XAML besitzt jedes Framework-Element (FrameworkElement oder FrameworkContentElement) eine Resources-Eigenschaft. Alle Elemente welche von FrameworkElement erben besitzen somit auch die Resources Eigenschaft. Hier ist interessant zu wissen das Resourcen "vererbt" werden. Vererben ist zwar nicht ganz das richtige Wort jedoch kann man sich darunter schon mal ungefähr etwas vorstellen. Angenommen wir haben ein Window. In diesem Window befindet sich ein DockPanel mit zwei Controls.
      Einer Statusleiste unten und einem StackPanel oben.
      Definieren wir Resourcen innerhalb des StackPanels kann das StackPanel sowie auch alle darunter liegenden Elemente auf diese Resourcen zugreifen. Sowohl die Statusbar als auch das DockPanel kann nicht darauf zugreifen da sich die Resourcen ja unterhalb dieses Elements befinden.

      Folgender XAML Code setzt diesen Test um:

      XML-Quellcode

      1. <DockPanel LastChildFill="True">
      2. <StatusBar DockPanel.Dock="Bottom">
      3. <Label Content="Status"/>
      4. </StatusBar>
      5. <StackPanel Margin="10">
      6. <StackPanel.Resources>
      7. <LinearGradientBrush x:Key="MyTestBackgroundBrush">
      8. <GradientStop Color="LightBlue"/>
      9. <GradientStop Color="Blue" Offset="1"/>
      10. </LinearGradientBrush>
      11. </StackPanel.Resources>
      12. <CheckBox Content="MeineCheckbox" Background="{StaticResource MyTestBackgroundBrush}"/>
      13. <Button Content="MainButton" Background="{StaticResource MyTestBackgroundBrush}"/>
      14. </StackPanel>
      15. </DockPanel>


      Folgendes Window wird im Designer angezeigt:


      Versuchen wir nun Beispielsweise dem Label in der Statusbar diese Resource zuzuweisen zeigt hier bereits der Designer einen Fehler dass die gesuchte Resource nicht gefunden werden kann.



      Das lässt sich allerdings trotz angezeigtem Fehler kompilieren und starten. Warum?
      Es kann ja gut sein das diese Resource zur Laufzeit zur Verfügung steht; Zum einen können Resourcen auch im Code erstellt werden zum anderen können XAML Files auch zur Laufzeit dynamisch nachgeladen werden oder es werden Assemblys geladen welche Resourcen enthalten. Wird dies allerdings nicht gemacht quittiert uns das der Debugger mit einer schönen 'XamlParseException'.


      Hier auch die Info: Angabe eines Werts für "System.Windows.StaticResourceExtension" führte zu einer Ausnahme."

      Selbst wenn wir nun auf "Continue" klicken wird das Debugging beendet. Also wo könnten wir nun die Resource definieren um diese sowohl in den Controls des Stackpanels als auch in den Controls der Statusbar zur Verfügung zu haben? Sowohl die Statusbar als auch das Stackpanel befinden sich im DockPanel. Also ist das Dockpanel der "tiefste" Punkt an welchem diese Resource definiert werden kann damit all die genannten Controls auch auf diese Resource zugreifen können.
      Ist eine Resource in den Resourcen des Window definiert gilt diese folglich für alle Elemente welches sich in diesem Window befinden. Spätestens hier fragen sich schon einige wo den der "höchste" Punkt für Resourcen wäre.

      Denn immer wieder erneut alles im Window zu definieren ist ja nicht unbedingt das Gelbe vom Ei. Der höchste Punkt an sich sind die verwalteten Resourcen des Betriebssystems. Denn in MS Windows gibt es seit Windows XP auch verschiedene Themes. Für unser Programm wäre es allerdings die Application.xaml welche sich im Hauptknoten unserer Anwendung befindet.



      Dieses File beinhaltet im Grunde nur ein leeres ResourceDictionary.
      Alle Resources, Styles und DataTemplates welche hier definiert werden sind von der ganzen Anwendung zugänglich. Wir haben ja bereits gelernt das wenn beispielsweise für einen Style kein Key sondern lediglich ein TargetType angegeben wird dieser für alle Elemente dieses Typs gelten.
      Haben wir z.b. einen Style ohne Angabe eines Keys welcher im Setter das Margin eines Buttons auf "15" festgelegt gilt dieses Margin für alle Elemente darunter. Ist dieses Style in der Application.xaml angegeben gilt dieses für alle Button des Projekts.

      Aber Moment mal!
      "Für dieses Projekt" würde doch bedeuten das, wenn ich Beispielsweise Controls oder Usercontrols in eine DLL auslagere diese das Margin nicht übernehmen richtig?
      Ja, teilweise.

      Erstelle ich in einer Assembly ein Control mit einem Button bekommt dieser Button erstmal diesen Style nicht zugeordnet. Packe ich dieses Usercontrol allerdings dann innerhalb meiner App (in welcher das Margin definiert ist) in ein Window oder wiederum in ein UserControl bekommt der Button sehr wohl das Margin da dieser sich nun innerhalb der Anwendung befindet wo im Style ein Margin für alle Buttons definiert ist.
      Folgende Vorgehensweise zeigt das Verhalten wie ich finde ganz gut.
      Wir haben ein UserControl erstellt welches wir gerne immer wieder verwenden möchten um Kundendaten anzuzeigen. Da wir es in mehreren Projekten verwenden möchte erstellen wir und ein neues Projekt vom Typ "WPF Benutzersteuerelementbibliothek" am besten innerhalb dieser Projektmappe. Hier erstellen wir uns nun ein UserControl mit dem Namen "uclCustomerData".

      Hier die Projektmappe wie diese nun aussieht:



      Und hier unser UserControl:



      Einigen wird nun sicher auffallen das hier kein Binding auf das Text-Property der TextBoxen erfolgt ist.
      Die Eingegebenen Daten können also nie irgendwohin übertragen werden. Da es hier in diesem Kapitel allerdings rein um Resourcen geht wollte ich darauf jetzt verzichten.
      Wir sehen in den Grid-Resourcen einen Style welcher auf einer Resource basiert. Und zwar erbt dieser Style von dem für eine TextBox definiertem Style. Wir nehmen also, falls eine Ebene vor unserem UserControl einen Style für eine TextBox definiert hat, diesen her und ändern Ihn ab. In unserem Fall überschreiben wir den Setter für die Eigenschaft "Margin". Hier greifen wir also bereits auf Resourcen zu. Um das UserControl nun in unserem Hauptprojekt verwenden zu können benötigen wir vom Hauptprojekt einen Verweis auf das Projekt mit unserem UserControl. Diesen fügen wir hinzu.

      Nun können wir das UserControl in unserem "MainWindow" der Hauptapplikation einfügen:



      Wir möchten allerdings, dass in unserer Anwendung alle TextBoxen mit einem grauem Hintergrund versehen werden. Also definieren wir in unseren Application.xaml-Resourcen das dies der Fall sein soll.



      Sehen wir uns nun unser MainWindow nochmals an sehen wir das diese Änderung nicht nur auf eine TextBox auswirkt welche direkt in unserer Anwendung definiert worden wäre sondern auch auf eine TextBox welche sich in einem UserControl befindet welches in einer völlig anderen Bibliothek definiert wurde.



      Was wenn wir aber ein UserControl erstellen wo wir dieses Verhalten NICHT haben möchten?
      - Dann sagen wir der WPF einfach nicht das es den Style von den TextBox-Resourcen erben soll und nehmen das BasedOn raus (im folgenden Screenshot zum besseren vergleich auskommentiert und darunter neu definiert):



      Nun erbt es den Style nicht mehr! Mir ist klar dass ich jetzt wieder ein wenig in den Styles Bereich gekommen bin, nur ist es wichtig zu verstehen das im Grunde auch ein Style eine Resource ist und wie die Vererbung funktioniert. Kommen wir nun wieder zurück zum wesentlichen.

      Unterschied zwischen StaticResource und DynamicResource

      In XAML gibt es zwei Arten eine Resource zuzuweisen. Als StaticResource oder als DynamicResource.
      Die Unterschiede zeige ich hier mal anhand einer Tabelle:

      Statische Resource
      Dynamische Resource
      Syntax:
      <object property="{StaticResource key}" .../>
      <object>
      <object.property>
      <StaticResource ResourceKey="key" .../>
      </object.property>
      </object>
      Syntax:
      <object property="{DynamicResource key}" .../>
      <object>
      <object.property>
      <DynamicResource ResourceKey="key" .../>
      </object.property>
      </object>
      Der Wert wird beim kompilieren zugewiesen und zur Laufzeit fest einkompiliert.
      Der Wert wird zur Laufzeit in dem Moment geladen wenn er benötigt wird.
      Werteänderungen zur Laufzeit werden ignoriert.
      Der aktuelle Wert wird beim laden des Objekts zugewiesen. Hat sich der Wert vorher geändert wird der geänderte Wert zugewiesen.
      Für die gesamte Lebensdauer der Applikation wird immer der selbe Wert verwendet

      Optimal wenn sich ein Wert nicht ändert oder nicht geändert werden soll.
      Optimal wenn sich der Wert über Code Behind zur Laufzeit ändern lassen soll.


      Sehen wir uns das ganze mal anhand eines simplen Beispiels an.
      Wir definieren in einer Window-Resource eine Color und einen SolidColorBrush.

      XML-Quellcode

      1. <Window.Resources>
      2. <Color x:Key="myRedBackgroundBrush">Red</Color>
      3. <SolidColorBrush x:Key="myBackgroundBrush" Color="{StaticResource myRedBackgroundBrush}"/>
      4. </Window.Resources>


      Damit man nun den großen Unterschied zwischen einer Statischen und einer Dynamischen Resource sieht erstelle ich zwei Buttons in einem StackPanel.

      XML-Quellcode

      1. <StackPanel Name="mystackPanel" Margin="10">
      2. <Button Content="Statische Resource"
      3. Background="{StaticResource myBackgroundBrush}"
      4. Margin="5"/>
      5. <Button Content="Dynamische Resource"
      6. Background="{DynamicResource myBackgroundBrush}"
      7. Margin="5"/>
      8. </StackPanel>


      Demnach sehen beide Buttons - bis auf den Content – gleich aus.



      Wo der Unterschied liegt erkennen wir wenn wir nun mit folgendem CodeBehind die Color des SolidColorBrush mit dem Namen „myBackgroundBrush“ auf LightBlue ändern.

      VB.NET-Quellcode

      1. Class MainWindow
      2. Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
      3. mystackPanel.Resources("myBackgroundBrush") = Brushes.LightBlue
      4. End Sub
      5. End Class


      Folgendes Ergebnis erscheint uns zur Laufzeit:



      Ich habe mich absichtlich dafür entschieden die Resource über die Resourcen des StackPanels zu ändern und zu zeigen dass man auf die Resourcen auch über ein untergeordnetes Objekt Zugriff bekommt.
      Die Resource „myBackgroudBrush“ wurde in den Window-Resourcen definiert, trotzdem greife ich über das StackPanel darauf zu und ändere diese.

      Diesmal gibt es zum Abschluss eines Kapitels mal kein Video.
      Ich denke hier konnte ich alles Nötige ohne Video vermitteln.
      Probiert es aus und spielt mit Resourcen herum.

      Erstellt auch gerne mal ein UserControl in einem anderen Projekt und seht ob es sich evtl. verändert wenn Ihr es in ein anderes Projekt mit Projektweiten Resourcen einbindet.

      Gerne könnt ihr mir trotzdem ein Like und/oder ein Kommentar auf YouTube hinterlassen oder meinen Kanal abonnieren.
      Verwendet auch gerne wieder den
      SupportThread um Fragen zu stellen.

      Bis zum mal mit mit einem sehr interessantem Thema. BINDING !!
      Das Kapitel wird etwas länger und wieder in mehreren Teilen Online gehen da dies das wohl wichtigste Thema in der WPF ist. ;)

      Liebe Grüße
      Sascha
      Dateien
      • 2.1.3.pdf

        (1,33 MB, 14 mal heruntergeladen, zuletzt: )
      If _work = worktype.hard Then Me.Drink(Coffee)
      Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Nofear23m“ ()