Zugriff auf TabControl aus ViewModel zum Umschalten der Tab-Seiten

  • WPF

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

    Zugriff auf TabControl aus ViewModel zum Umschalten der Tab-Seiten

    Hallo Forum,

    mit freundlicher Unterstützung von @Nofear23m bin ich bei meinem Projekt schon ein ganzen Stück weiter gekommen.

    Leider weiss ich grad bei einem Punkt nicht weiter. Und zwar möchte ich, wenn auf einen bestimmten Button geklickt wird, die zugehörige Seite in dem TabControl anzeigen lassen.

    Die XAML:

    Spoiler anzeigen

    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:Checkbox_Test"
    7. xmlns:pkl="clr-namespace:Checkbox_Test.Produktklasse"
    8. mc:Ignorable="d"
    9. Title="ProFriCon" Height="450" Width="800">
    10. <StackPanel>
    11. <ItemsControl ItemsSource="{Binding Checkboxliste}">
    12. <ItemsControl.ItemTemplate>
    13. <DataTemplate>
    14. <CheckBox Content="{Binding neuesProdukt}" IsChecked="{Binding IsChecked}"/>
    15. </DataTemplate>
    16. </ItemsControl.ItemTemplate>
    17. </ItemsControl>
    18. <!--<ListBox>-->
    19. <ItemsControl ItemsSource="{Binding Produktliste}">
    20. <ItemsControl.ItemTemplate>
    21. <DataTemplate>
    22. <Border Padding="5" BorderBrush="Black" BorderThickness="1">
    23. <Grid Width="60">
    24. <Grid.RowDefinitions>
    25. <RowDefinition Height="40"/>
    26. <RowDefinition Height="*"/>
    27. </Grid.RowDefinitions>
    28. <Button Command="{Binding DataContext.Klick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
    29. CommandParameter="{Binding}">
    30. <Image Source="{Binding Produktbild}" Stretch="Uniform"/>
    31. </Button>
    32. <StackPanel Grid.Row="1" Orientation="Horizontal">
    33. <Button x:Name="btnBackward" Width="30" Height="30" HorizontalAlignment="Left"
    34. Command="{Binding DataContext.Klick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
    35. CommandParameter="{Binding}">
    36. <Image Source="Pictures/Zurueck_56x50.png" Stretch="UniformToFill" />
    37. </Button>
    38. <Button x:Name="btnForward" Width="30" Height="30" HorizontalAlignment="Right"
    39. Command="{Binding DataContext.Klick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
    40. CommandParameter="{Binding}">
    41. <Image Source="Pictures/Vorwaerts_56x50.png"/>
    42. </Button>
    43. </StackPanel>
    44. </Grid>
    45. </Border>
    46. </DataTemplate>
    47. </ItemsControl.ItemTemplate>
    48. <ItemsControl.ItemsPanel>
    49. <ItemsPanelTemplate>
    50. <WrapPanel MaxWidth="750"/>
    51. </ItemsPanelTemplate>
    52. </ItemsControl.ItemsPanel>
    53. </ItemsControl>
    54. <!--</ListBox>-->
    55. <TabControl ItemsSource="{Binding Produktliste}" x:Name="tcBauteile">
    56. <TabControl.ContentTemplate>
    57. <DataTemplate>
    58. <ContentControl Content="{Binding}">
    59. <ContentControl.Resources>
    60. <DataTemplate DataType="{x:Type pkl:Standardbauteil}">
    61. <Image Source="{Binding Produktbild}" Width="42" Height="42" Stretch="UniformToFill"/>
    62. </DataTemplate>
    63. <DataTemplate DataType="{x:Type pkl:Bauteil_1}">
    64. <StackPanel>
    65. <Image Source="{Binding Produktbild}" Width="42" Height="42" Stretch="UniformToFill"/>
    66. <Button Content="{Binding Button}"/>
    67. </StackPanel>
    68. </DataTemplate>
    69. <DataTemplate DataType="{x:Type pkl:Bauteil_2}">
    70. <ListBox>
    71. <UniformGrid Columns="4">
    72. <Image Source="{Binding Produktbild}" Width="42" Height="42" Stretch="UniformToFill"/>
    73. <Button Content="{Binding Button}"/>
    74. <Button Content="{Binding Button}"/>
    75. <Button Content="{Binding Button}"/>
    76. </UniformGrid>
    77. </ListBox>
    78. </DataTemplate>
    79. </ContentControl.Resources>
    80. </ContentControl>
    81. </DataTemplate>
    82. </TabControl.ContentTemplate>
    83. </TabControl>
    84. </StackPanel>
    85. </Window>


    TabControl ist ganz unten (Zeile 57). Dem TC hab ich einen fixen Namen zugewiesen, auf den ich zugreifen möchte.

    Der Aufruf soll in meinem MainViewModel stattfinden, und zwar in der ​Private Sub Klick_Execute(obj As Object) (Zeile 36).

    Spoiler anzeigen

    VB.NET-Quellcode

    1. ​Imports System.Collections.ObjectModel
    2. Imports Checkbox_Test.Produktklasse
    3. Public Class MainViewModel
    4. Public Sub New()
    5. With Checkboxliste
    6. .Add(New Produktlisteneintrag(New Standardbauteil("Prod1", "Pictures/Prod1.png")))
    7. .Add(New Produktlisteneintrag(New Bauteil_1("Prod2", "Pictures/Prod2.png", "Button Bauteil 1 Prod 2")))
    8. .Add(New Produktlisteneintrag(New Bauteil_1("Prod3", "Pictures/Prod3.png", "Button Bauteil 1 Prod 3")))
    9. .Add(New Produktlisteneintrag(New Bauteil_2("Prod4", "Pictures/Prod4.png", "Button Bauteil 2 Prod 4")))
    10. .Add(New Produktlisteneintrag(New Bauteil_2("Prod5", "Pictures/Prod5.png", "Button Bauteil 2 Prod 5")))
    11. .Add(New Produktlisteneintrag(New Standardbauteil("Prod6", "Pictures/Prod6.png")))
    12. .Add(New Produktlisteneintrag(New Standardbauteil("Prod7", "Pictures/Prod7.png")))
    13. .Add(New Produktlisteneintrag(New Bauteil_1("Prod8", "Pictures/Prod8.png", "Button Bauteil 1 Prod 8")))
    14. .Add(New Produktlisteneintrag(New Bauteil_1("Prod9", "Pictures/Prod9.png", "Button Bauteil 1 Prod 9")))
    15. .Add(New Produktlisteneintrag(New Bauteil_2("Prod10", "Pictures/Prod10.png", "Button Bauteil 2 Prod 10")))
    16. .Add(New Produktlisteneintrag(New Bauteil_2("Eagle", "Pictures/Eagle.png", "Button Bauteil 2 Eagle")))
    17. .Add(New Produktlisteneintrag(New Bauteil_1("Dog", "Pictures/Dog.png", "Button Bauteil 1 Dog")))
    18. .Add(New Produktlisteneintrag(New Bauteil_2("Cat", "Pictures/Cat.png", "Button Bauteil 2 Cat")))
    19. .Add(New Produktlisteneintrag(New Standardbauteil("Spider", "Pictures/Spider.png")))
    20. End With
    21. For Each eintrag As Produktlisteneintrag In Checkboxliste
    22. AddHandler eintrag.EintragCheckedChanged, AddressOf EintragCheckedChanged
    23. Next
    24. Klick = New RelayCommand(AddressOf Klick_Execute, AddressOf KlickCanExecute)
    25. End Sub
    26. Public Property Checkboxliste As ObservableCollection(Of Produktlisteneintrag) = New ObservableCollection(Of Produktlisteneintrag)
    27. Public Property Produktliste As ObservableCollection(Of Bauteil) = New ObservableCollection(Of Bauteil)
    28. Public Property Klick As ICommand
    29. Private Sub Klick_Execute(obj As Object)
    30. 'Mach was
    31. End Sub
    32. Private Function KlickCanExecute(obj As Object) As Boolean
    33. 'Return true wenn der Command ausgeführt werden darf oder false wenn nicht
    34. Return obj IsNot Nothing AndAlso TryCast(obj, Bauteil) IsNot Nothing
    35. End Function
    36. Private Sub EintragCheckedChanged(Haken As Boolean, neuesBild As Bauteil)
    37. If Haken Then
    38. Produktliste.Add(neuesBild)
    39. Else
    40. Produktliste.Remove(neuesBild)
    41. End If
    42. End Sub
    43. End Class



    Wie bekomme ich jetzt Zugriff auf das TabControl? Wenn ich z.B. in o.g. ​Klick_execute den direkten Zugriff versuche mit ​MainWindow.tcBauteile.SelectedIndex = 0 dann kommt eine Fehlermeldung:



    Dann habe ich versucht mir eine Instanz von MainWindow zu erzeugen, um auf tcBauteile zugreifen zu können:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. ​Private Sub Klick_Execute(obj As Object)
    2. Dim Pos As Integer
    3. Dim mm As New MainWindow
    4. For i = 0 To Produktliste.Count - 1
    5. If Produktliste(i).Produktname = DirectCast(obj, Bauteil).Produktname Then
    6. Pos = i
    7. End If
    8. Next
    9. mm.tcBauteile.SelectedIndex = Pos
    10. End Sub


    Leider ändert es aber dann den Tab nicht. Was mach ich da falsch?

    Das komplette Projekt ist als Anhang beigefügt.

    Gruß Oli
    Dateien
    • Checkbox_Test.zip

      (2,45 MB, 83 mal heruntergeladen, zuletzt: )
    Hallo

    BlackTears schrieb:

    Dann habe ich versucht mir eine Instanz von MainWindow zu erzeugen

    Ne, dinge der view haben in einem ViewModel wenn möglich nichts zu suchen. Binding!

    Das TabControl besitzt die eigenschaft SelectedItem. Diese gilt es zu Binden.
    Due erstellst dir also in deinem ViewModel wo die Produktliste ist ein Property SelectedProduct As Bauteil. Vergiss aber nicht im Setter PropertyChanged zu werfen.

    Dieses Property bindest du an SelectedItem des TabControls.

    Im code des Klick-Commands must du dann nur SelectedItem setzen und der Selektierte Tab wechselt.
    Um im Command die Info zu bekommen von welchem Button aus geklickt wurde kannst du CommandParameter verwenden.

    Probier das mal und wenn du probleme hast sag bescheid.

    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. ##

    Und mal wieder: Herzlichen Dank Sascha.

    Ich hab es hinbekommen:

    In der XAML einfach

    XML-Quellcode

    1. <TabControl ItemsSource="{Binding Produktliste}" SelectedItem="{Binding SelectedProdukt}">


    Dann die Property ergänzt:

    VB.NET-Quellcode

    1. Public Property SelectedProdukt As Bauteil
    2. Get
    3. SelectedProdukt = _SelectedProdukt
    4. End Get
    5. Set(value As Bauteil)
    6. _SelectedProdukt = value
    7. RaisePropertyChanged()
    8. End Set
    9. End Property


    In der Klasse noch Implements INotifyPropertyChanged setzen und die Schnittstelle implementieren Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged. Jetzt noch RaisePropertyChanged aus Deinem Video abgetippt:

    VB.NET-Quellcode

    1. Protected Overridable Sub RaisePropertyChanged(<CallerMemberName> Optional ByVal prop As String = "")
    2. RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(prop))
    3. End Sub


    Noch die beiden Imports ergänzt: Imports System.ComponentModel für das o.g. Implements INotifyPropertyChanged und Imports System.Runtime.CompilerServices, da sonst das <CallerMemberName> in der RaisePropertyChanged nicht funktioniert.

    Ich hoffe, ich habe das richtig umgesetzt.

    Gruß Oli

    Edit: Hab ich vergessen -> in der Click_Execute noch SelectedProdukt = DirectCast(obj, Bauteil) hinzugefügt und schon klappt´s. :thumbsup:
    Richtig. Perfekt umgesetzt.

    Klingt jetzt nach viel umbau. Jedoch hat man normalerweise INotifyPropertychanged sowieso in einer Basisklasse von welcher man erbt und muss das ganze nicht erst machen.

    Im Grunde nur 1 Property und los gehts. Du siehst, Binding ist wirklich super und vorallem ist es immer Syncron, die WPF macht dir den rest.
    Also immer brav alles was möglich ist über Binding regeln.

    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. ##