DataTemplate für TabControl bzw. Tabcontrol.ContentTemplate

  • WPF

Es gibt 22 Antworten in diesem Thema. Der letzte Beitrag () ist von BlackTears.

    Hallo Sascha,

    erstmal wiederholt ein Danke an Dich für Deine Mühe und Geduld.

    Ich hab es kurz überflogen und glaube, damit kann ich arbeiten. Ich glaube ich habe auch noch Defizite was die Architektur von Programmen angeht. Das MVVM-Pattern muss ich mir noch etwas vertiefen.

    Ist es normal so, dass ein Ordner mit View´s, einer mit ViewModels und einer mit Models erstellt wird? In dem Ordner Views sind nur XAML-Dateien, wenn ich das richtig verstanden habe. In Models kommen hier bei mir die Bauteilklassen (Bauteil, Bauteil_1, Bauteil_2 und Standardbauteil), also die Models. Und im Ordner ViewModel letztlich die Datei MainViewModel und weitere. Ich hoffe, ich habe das so richtig gelesen. Wobei ErfinderDesRades in seinem Tutorial schreibt, dass die Trennung zwischen Model und Viewmodel nicht immer sinnvoll ist. Ich würde jetzt mit einer Trennung anfangen, da ich noch nicht so viel Programmiererfahrung habe, oder ist das eher die Ausnahme das zu trennen?


    Zu Deinen Fragen:
    1. Ich nahm an, dass ich die ItemsControl in eine ListBox legen muss, damit alles sauber nebeneinander angeordnet wird. Außen rum liegt ja ein StackPanel und da dachte ich, dass dann alles untereinander angeordnet wird. Wie bei den Checkboxen. Wenn nicht notwendig auch gut, aber warum werden die Checkboxen untereinander und die anderen Templates nebeneinander angeordnet?
    2. Hab ich glaub ich verstanden, ich werde mir es aber noch genauer zerpflücken.

    3. Hierzu habe ich mir Dein Tutorial angesehen und weiss nicht, ob ich es ganz verstanden habe.

      XML-Quellcode

      1. ​Command="{Binding DataContext.Klick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"

      Das Command wird an die Klick-Property des DataContext gebunden, der DataContext ist festgelegt in der MainWindow.xaml.vb mit

      VB.NET-Quellcode

      1. ​Me.DataContext = New MainViewModel

      Und um rauszufinden welcher Button geklickt wurde muss die RelativeSource angegeben werden, in dem Fall das ItemsControl, weil da die Produktliste zu finden ist. Hoffe das ist nicht ganz verkehrt.

    Hach, es gibt so herrlich viel zu lernen.

    Danke und Gruß

    ​Oli
    Hallo

    OK, ich versuche mal das durch zu gehen.

    BlackTears schrieb:

    Ist es normal so, dass ein Ordner mit View´s, einer mit ViewModels und einer mit Models erstellt wird? In dem Ordner Views sind nur XAML-Dateien, wenn ich das richtig verstanden habe. In Models kommen hier bei mir die Bauteilklassen (Bauteil, Bauteil_1, Bauteil_2 und Standardbauteil), also die Models. Und im Ordner ViewModel letztlich die Datei MainViewModel und weitere. Ich hoffe, ich habe das so richtig gelesen. Wobei ErfinderDesRades in seinem Tutorial schreibt, dass die Trennung zwischen Model und Viewmodel nicht immer sinnvoll ist. Ich würde jetzt mit einer Trennung anfangen, da ich noch nicht so viel Programmiererfahrung habe, oder ist das eher die Ausnahme das zu trennen?

    Nach der reinen lehre werden sogar die Projekte getrennt da MVVM darauf abzielt das es Testbar ist (UnitTests(ist aber wieder ein anderes Thema)), was soviel bedeutet das Model-View-ViewModel alles in einzelne Projekte innerhalb einer Projektmappe kommen. Das ViewModel kennt nur Model aber keine View, Die View kennt nur das ViewModel aber kein Model und das Model kennt garnix von beiden.
    Was ein Model, ein ViewModel nd ein View ist hast du soweit richtig erkannt.
    Was @ErfinderDesRades sein Tutorial angeht ist das richtig, es ist nicht immer sinnvoll diese Trennung zu vollziehen und dem Pattern korrekt zu folgen. Das macht wirklich nur bei komplexeren Projekten sinn wo ich dann von der Trennung profitiere, sei es wegen der Übersichtlichkeit, Cross-Plattform fähigkeit und vorallem für UnitTests bzw. IntergrationTests.
    Es bedeutet mehraufwand und man muss wirklich wissen um was es geht, das ist ein langer Lernprozess und mit vielen Hürden verbunden.
    Aber ACHTUNG. Sobald man dem Pattern nicht folgt ist es in dem Moment kein MVVM mehr und kann auch nicht mehr als MVVM bezeichnet werden. Sobald ich mir einen Verweis auf die View ins ViewModel hole wie in den Tutorials ist es kein MVVM mehr. Es ist im besten Fall eine "Architektur im MVVM Stil". Das möchte ich nur klarstellen. Gut, genug der Worte. Erst wenn Binding, DependencyProperties, AttachedProperties, Converter, Templates und der gleichen wirklich sitzt, würde ich mir Gedanken um MVVM machen, und auch dann nur bei größeren Projekten.

    BlackTears schrieb:

    aber warum werden die Checkboxen untereinander und die anderen Templates nebeneinander angeordnet?

    Für die Produktliste hast du für das ItemsControl ein ItemsPanelTemplate definiert.

    XML-Quellcode

    1. <ItemsControl.ItemsPanel>
    2. <ItemsPanelTemplate>
    3. <WrapPanel MaxWidth="750"/>
    4. </ItemsPanelTemplate>
    5. </ItemsControl.ItemsPanel>

    Und hast der WPF somit gesagt: Passt auf, ich will das du mir die Items (also die Produkte) in ein WrapPanel klatscht. Die WPF geht also her und sagt: OK, ich habe nun x Items bekommen, die packe ich nun nacheinander in ein WrapPanel. Da du für dieses WrapPanel keine Orientation angegeben hast und der Default-Wert für diese Eigenschaft "Horizontal" ist werden die Items Horizontal nebeneinander aufgereiht.

    Bei den Checkboxen hast du kein ItemsPanelTemplate angegeben. Somit greift das StandardTemplate welches Microsoft implementiert hat. Dieses ist ein StackPanel (OK, genau genommen ein VirtualizingStackPanel, aber das ist jetzt nicht Thema) und dieses StackPanel hat als Defaultwert für die Orientation "Vertikal", weshalb die Checkboxen Vertikal untereinander aufgestapelt werden.

    Der unterschied zwischen WrapPanel und Stackpanel ist das ein WrapPanel einen "umbruch" auch unterstützt, wobei ein Stackpanel IMMER weiter anordnet, und wenn kein Platz mehr ist interessiert es das StackPanel nicht, es macht einfach munter weiter.

    BlackTears schrieb:

    Das Command wird an die Klick-Property des DataContext gebunden, der DataContext ist festgelegt in der MainWindow.xaml.vb mit

    VB.NET-Quellcode

    Me.DataContext = New MainViewModel


    Und um rauszufinden welcher Button geklickt wurde muss die RelativeSource angegeben werden, in dem Fall das ItemsControl, weil da die Produktliste zu finden ist. Hoffe das ist nicht ganz verkehrt.

    Nicht so ganz, gehen wir das mal durch:

    XML-Quellcode

    1. Command="{Binding DataContext.Klick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"

    FindAncestor bedeutet umgefähr soviel wie "Finde im ElementTree (Der UI Baum in der WPF) das nächste Element von einem gewissen Typ".
    In deinem Fall gibst du an das du ein ItemsControl suchst. Man könnte nun noch die anzahl der zu suchenen Ebenen angeben und so aber egal erstmal.
    Der ElementTree wird immer nur nach oben in der Hirarchie durchsucht. (Bubbling)

    So, nun haben wird noch DataContext.Klick. Wird die WPF also fündig und findet ein ItemsControl bindet Sie nun den Command an dessen DataContext (was in deinem Fall eine Instanz von MainViewModel ist) und innerhalb dieser Instanz dann auf ein Property mit dem Namen "Klick". Das ist so notwendig weil der Button innerhalb des DataTemplates eines ItemsControl definiert ist.
    Das ItemsControl ist an Produktliste gebunden welches sich zwar auch in "MainViewModel" befindet, aber der Button ist ja in einem DataTemplate des ItemsControl. Und ein einzelnes Item in dieser Auflistung hat als DatenKontext ja nicht mehr das "MainViewModel" sondern representiert ja ein Item - also ein Produkt - und hat somit das Produkt als DatenKontext. Weshalb nun ja kein Klick-Property zur verfügung steht da dieses in "Produkt" nicht vorhanden ist.

    So, ich denke das war wirklich ausführlich, ich wüsste nicht wie ich das sonst schreiben könnte, sowas erklären ist echt schwieriger als man denkt. Deshalb - Binding immer und immer üben, dann kommt man dahinter.

    Na dann, gute Nacht
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

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

    Hallo Sascha,

    danke für ausführlichen Erläuterungen.

    Das mit dem WrapPanel hätte ich auch selber rausfinden können, aber manchmal sieht man den Wald vor lauter Bäumen nicht. Das mit der Architektur nehm ich so mit. Momentan hab ich eher kleinere Projekte, werde aber versuchen mir eine gewisse Struktur anzugewöhnen.

    Das mit dem Command ist jetzt viel klarer, dank deiner Erklärung. Es wird also erst nach der relative Quelle gesucht und dann an den Datenkontext gebunden. Wenn mans weiß, ist es eigentlich ziemlich klar.

    Somit würde ich den Thread erstmal schließen. Aber nicht ohne mich nochmals zu bedanken für Deinen Einsatz und Deine Geduld mit mir.

    Gruß Oli