DataTemplate für TabControl bzw. Tabcontrol.ContentTemplate

  • WPF

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

    DataTemplate für TabControl bzw. Tabcontrol.ContentTemplate

    Hallo zusammen,

    ich mein altes Projekt etwas umgearbeitet. Habe mich (so hoffe ich) an die Anweisungen von Sascha vom vorherigen Projekt angelehnt, aber zur Übung neu erstellt (selbst schreiben prägt ein).

    Es gibt nach wie vor die Checkboxen zum anklicken, einen Bereich für die gewählten Produkte und darunter jetzt neu ein TabControl. Das funktioniert auch alles soweit gut. Im TabControl habe ich meine Produktliste gebunden und ein TabControl.ContentTemplate mit einem DataTemplate versehen. Jetzt möchte ich aber, dass diese DataTemplates je nach gewähltem Produkt, anders aufgebaut sind (mit Buttons, Textboxen, Checkboxen und dergleichen). Also bei Produkt 1 soll der Inhalt des Tabs anders aussehen als bei Produkt 2.

    Soweit ich gelernt habe, kann das in verschiedenen DataTemplates dargestellt werden. Alleine das Wie ist mir noch nicht klar. Und wie bring ich einem bestimmten Tab bei, dass er "sein" Template aufrufen muss (ich vermute mit Binding, weiss aber net wie).

    Ich hoffe, ich habe mich verständlich ausgedrückt. Wenn nicht, bitte fragen. Danke im Voraus für Eure Kommentare und Anregungen.

    Im Anhang das Projekt.

    Gruß

    Oli

    Edit: Datei ohne bin-Ordner hochgeladen
    Dateien

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „BlackTears“ ()

    Hallo

    Ich werde mal drüber sehen. Bitte editiere deinen Beitrag nochmals und lade ein Zip File hoch welches bereinigt ist. Sprich, ohne bin Ordner damit keine ausführbare exe vorhanden ist, da dies im Forum nicht erwünscht ist.
    Kannst auch in VS Projektmappe bereinigen klicken.

    Grüße
    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. ##

    BlackTears schrieb:

    aber zur Übung neu erstellt (selbst schreiben prägt ein)

    Sehr gut, in dem Punkt gebe ich dir völlig recht.

    BlackTears schrieb:

    Also bei Produkt 1 soll der Inhalt des Tabs anders aussehen als bei Produkt 2.

    Naja, so ganz genau geht das nicht. Wie soll dein Programm oder besser die WPF wissen welches Produkt du wie angezteigt haben willst. Ich nehme mal salopp an das es sich um Verschiedene Produktarten handelt.
    Also Beispielsweise Elektroprodukt, Haushaltsprodukt, Kleidung usw.
    Nur ne Annahme. Also muss du in diese Richtung gehen. Die WPF kann nicht wissen das du das Produkt mit dem Namen XY so und so angezeigt haben willst. Das geht zwar auch mitrtels Triggern und wenn du das so machen möchtest könnte ich dir ein Beispiel machen. Das ist aber eher schlecht. Was wenn ein Produkt unbenannt wird. Oder du machst es mit dem Titel der Produktkategorie. Was ist wenn diese unbenannt wird.

    In den meissten Fällen kann man hier eine Basisklasse schaffen und die verschiedenen Produktklassen davon ableiten lassen. und anschliessend kann man der WPF (richtig von dir) mittels DataTemplates über die Eigenschaft TargetType beibringen das diese doch bitte eine Instanz der Klasse X bitte so und so rendern soll und eine Instanz der Klasse Y aber anders.

    Ich habe dein Beispiel mal so umgebaut das es die Basisklasse BauteilBase gibt. Diese hält alle Eigenschaften welche JEDES Produkt besitzt. Eine Klasse DefaultBauteil. Diese Klasse erbt von BauteilBase und besitzt sonst keinerlei weitere Eigenschaften. Default eben. Eine weitere Klasse war ich so frei hinzuzufügen - die ElektronikBauteil - welche eine weitere Eigenschaft Volt besitzt und dies einfach mal zu demonstrieren.
    Beim erstellen der Produktliste erstelle ich nun eine Reihe von DefaultBauteil wie du, nur ist dazwischen ein ElektronikBauteil:

    VB.NET-Quellcode

    1. With Checkboxliste
    2. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod1", "Pictures/Prod1.png")))
    3. .Add(New Produktlisteneintrag(New ElektronikBauteil("Elektronik1", "Pictures/Prod2.png", 12.2)))
    4. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod3", "Pictures/Prod3.png")))
    5. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod4", "Pictures/Prod4.png")))
    6. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod5", "Pictures/Prod5.png")))
    7. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod6", "Pictures/Prod6.png")))
    8. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod7", "Pictures/Prod7.png")))
    9. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod8", "Pictures/Prod8.png")))
    10. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod9", "Pictures/Prod9.png")))
    11. .Add(New Produktlisteneintrag(New DefaultBauteil("Prod10", "Pictures/Prod10.png")))
    12. .Add(New Produktlisteneintrag(New DefaultBauteil("Eagle", "Pictures/Eagle.png")))
    13. .Add(New Produktlisteneintrag(New DefaultBauteil("Dog", "Pictures/Dog.png")))
    14. .Add(New Produktlisteneintrag(New DefaultBauteil("Cat", "Pictures/Cat.png")))
    15. .Add(New Produktlisteneintrag(New DefaultBauteil("Spider", "Pictures/Spider.png")))
    16. End With


    Und der WPF habe ich gesagt das alles von von BauteilBase erbt immer nur mit einem Bild gerendert werden soll. Außer es handelt sich um ein Elektronikbauteil. Dann mit mach mir auch noch einen TextBlock darunter.

    XML-Quellcode

    1. <TabControl ItemsSource="{Binding Produktliste}">
    2. <TabControl.ContentTemplate>
    3. <DataTemplate>
    4. <ContentControl Content="{Binding}">
    5. <ContentControl.Resources>
    6. <DataTemplate DataType="{x:Type models:BauteilBase}">
    7. <Image Source="{Binding Produktbild}" Width="42" Height="42" Stretch="UniformToFill"/>
    8. </DataTemplate>
    9. <DataTemplate DataType="{x:Type models:ElektronikBauteil}">
    10. <StackPanel>
    11. <Image Source="{Binding Produktbild}" Width="42" Height="42" Stretch="UniformToFill"/>
    12. <TextBlock HorizontalAlignment="Center" Text="{Binding Volt,StringFormat={}{0} Volt}"/>
    13. </StackPanel>
    14. </DataTemplate>
    15. </ContentControl.Resources>
    16. </ContentControl>
    17. </DataTemplate>
    18. </TabControl.ContentTemplate>
    19. </TabControl>


    Ich hoffe ich konnte das so erklären das man verstehen kann wie dieser Mechanismus funktioniert.
    Anbei das Projekt
    Dateien
    • CheckboxTest.zip

      (2,3 MB, 1 mal heruntergeladen, zuletzt: )
    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. ##

    Hallo Sascha,

    danke, danke, danke. Das ist genau die Richtung, die ich brauche. Ich dachte mir schon, dass da noch was fehlt. Mit der Strukturierung eines Projektes bin ich leider auch noch nicht so vertraut, aber das kommt noch. Wenn ich z.B. den Ordner Models anders benennen würde (z. B. Produktmodule), dann müsste ich in der XAML auch den Aufruf ändern, oder?

    XML-Quellcode

    1. <DataTemplate DataType="{x:Type Produktmodule:BauteilBase}">


    Also für jede Produktgruppe eine Klasse, damit in der XAML mittels DataType darauf zugegriffen werden kann. Müsste ich dann an dieser Stelle

    XML-Quellcode

    1. <DataTemplate DataType="{x:Type models:BauteilBase}">


    eigentlich statt BauteilBase das DefaultBauteil aufrufen?

    Soweit komm ich damit super klar, aber eine Frage bleibt dennoch. Was macht diese Zeile bzw. was wird hier gebunden:

    XML-Quellcode

    1. <ContentControl Content="{Binding}">


    Ich werde mein Projekt entsprechend ausarbeiten und hier wieder vorstellen. Kann allerdings etwas dauern, da wir über Ostern Deine schöne Heimatstadt besuchen werden. Aber spätestens Dienstag mach ich mich mit Hochdruck dran.

    Bis dahin wünsche ich Euch, und speziell Dir Sascha, ein frohes Osterfest. Danke, dass es dieses Forum gibt und Menschen, die sich in Ihrer Freizeit mit den Problemen anderer Menschen beschäftigen.

    Gruß

    Oli
    Hallo Oli

    BlackTears schrieb:

    Wenn ich z.B. den Ordner Models anders benennen würde (z. B. Produktmodule), dann müsste ich in der XAML auch den Aufruf ändern, oder?

    Nicht zwingend. Der Ordnername hat erstmal nichts mit dem Namespace zu tun. Allerdings hält man die Namespaces normalerweise syncron mit der Ordnerstruktur.
    Änderst du den Namespace in den codedateien der Model-Klassen musst du den Import im XAML ganz oben auch anpassen. Wie du den Import benennst ist allerdings dann wieder egal.

    BlackTears schrieb:

    eigentlich statt BauteilBase das DefaultBauteil aufrufen?

    Ich habe das absichtlich so gemacht. Warum? Alle Produktklassen erben vn BauteilBase. Jetzt wo du nur zwei Produktarten hast könntest du BeuteilBase auf DefaultBauteil ändern. Ich mache es aber immer deshalb so damit ich ein Sicherheitsnetz habe. erstelle ich eine neue Klasse für eine Neue Produktart und vergesse ein DataTemplate zu erstellen dann greift auf jeden Fall das Template für die Basisklasse (weil die neue Klasse erbt ja von dieser) und es wird mir somit zumindest etwas angezeigt. Anders würde die WPF nun nicht fündig werden und hätte KEIN DataTemplate zum anzeigen parat. Hoffe das was verständlich.

    BlackTears schrieb:

    Was macht diese Zeile bzw. was wird hier gebunden

    Berechtigte Frage. Das DataTemplate hat als DatenContext in diesem Moment eine Instanz eines Produkts. Da wird nun den Content des ContentControls nicht auf eine bestimmte Eigenschaft eines Produkts binden wollen sondern auch wieder gleich auf das ganze Produkt macht man das einfach so das man das Binding direkt so weitergibt.

    BlackTears schrieb:

    Bis dahin wünsche ich Euch, und speziell Dir Sascha, ein frohes Osterfest.

    Danke, wünsche ich dir auch. Viel spaß in Wien.

    Grüße
    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. ##

    Neu

    Hallo Sascha,

    ich habe mir Dein Beispiel mal zerpflückt und komm jetzt soweit gut damit zurecht. Danke dafür erstmal. Jetzt stoss ich allerdings anderweitig wieder an meine Grenzen. In der XAML erstelle ich ja für jeden Eintrag in der Produktliste ein Template in dem auch ein Button liegt:

    Spoiler anzeigen

    XML-Quellcode

    1. <ListBox>
    2. <ItemsControl ItemsSource="{Binding Produktliste}">
    3. <ItemsControl.ItemTemplate>
    4. <DataTemplate>
    5. <Border Padding="5" BorderBrush="Black" BorderThickness="1">
    6. <Grid Width="60">
    7. <Grid.RowDefinitions>
    8. <RowDefinition Height="40"/>
    9. <RowDefinition Height="*"/>
    10. </Grid.RowDefinitions>
    11. <Button>
    12. <Image Source="{Binding Produktbild}" Stretch="Uniform"/>
    13. </Button>


    Den verschiedenen Buttons möchte ich jetzt ein Klick-Ereignis mitgeben, das in der TabControl das entsprechende TabItem aktiv schaltet, also anzeigt. Hab es mal so versucht:

    Spoiler anzeigen

    XML-Quellcode

    1. <Button Click="aktivesProdukt">
    2. <Image Source="{Binding Produktbild}" Stretch="Uniform"/>
    3. </Button>


    In der Sub hab ich mir einfach zum testen mal eine MessageBox ausgeben lassen. Hat auch gut funktioniert. Allerdings weiss ich nicht, welcher Button geklickt wurde, ergo kann ich auch das entsprechende TabItem nicht ansprechen. Dann hab ich noch versucht wie bei den Checkboxen mit AddHandler ein Event zu starten, bin aber auch hier nicht weiter gekommen.

    Kannst Du mir da einen Ansatz verraten, wie ich das ganze am Besten bewerkstellige? :S ?( ?( :S

    Anbei mein aktueller Code.

    Gruß Oli
    Dateien

    Neu

    Hallo

    BlackTears schrieb:

    Kannst Du mir da einen Ansatz verraten, wie ich das ganze am Besten bewerkstellige?


    Also am saubersten macht man es nicht mit einem Click Ereignisshandler sondern mittels Command. Das wäre der WPF Like Weg.
    OK, so gehts auch solange man mit der Code-Behind Klasse als DatenContext arbeitet. Ist dies mal nicht mehr der Fall steht man wieder an.

    Du hast ja im sender den Button. Frage doch den DataContext des Buttons ab um an die Daten zu kommen.

    Grüße
    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. ##