TabControl SelectedTab Binding

  • WPF

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von HamburgerJungeJr.

    TabControl SelectedTab Binding

    Moin,

    Ich versuche gerade im ViewModel herauszubekommen, welcher Tab gerade aktiv ist / diesen zu setzen.

    Ich binde das TabControl so:

    XML-Quellcode

    1. <TabControl ItemsSource="{Binding Tabs}" Grid.Row="0" SelectedItem="{Binding CurrentTab}"/>


    Und im ViewModel:

    C#-Quellcode

    1. private List<TabItem> tabs;
    2. private ListCollectionView tabsCollection;
    3. public MainViewModel() {
    4. tabs = new List<TabItem>();
    5. tabsCollection = (ListCollectionView)CollectionViewSource.GetDefaultView(tabs);
    6. }
    7. public ICollectionView Tabs {
    8. get {
    9. return tabsCollection;
    10. }
    11. }
    12. public TabItem CurrentTab { get; set; }


    Doch CurrentItem/CurrentTab ist immer Null.

    Was mache ich falsch?

    Gruß

    HamburgerJungeJr
    1. TabItem gehören nicht ins Viewmodel. Mach geeignete Viewmodels in die ItemCollection, und binde das TabControl dran
    2. TabControl erbt von Selector - du kannst also IsSynchronisizedWithCurrentItem setzen.
      Und dann kannst du das CurrentItem im Viewmodel vonne CollectionView abrufen - eine gesonderte Property zum dran binden ist nichtmal erforderlich.

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

    Das versteh ich irgendwie nicht.

    Ich habe ein Window. Dieses hat ein ViewModel, mit:
    - Einer Instanz meines DataSets
    - Einer Liste meiner TabItems
    - Commands, um Tabs hinzuzufügen, schließen, Datensatz hinzufügen etc. (Im Ribbon)

    Jedes TabItem hat ein eigenes ViewModel, mit
    - Einer Property, die einen Benutzer darstellt
    - Commands für Buttons auf dem TabItem

    Also zusammengefasst:
    Auf einem TabItem ist eine Übersicht über alle Benutzer und für die Bearbeitung hat jeder Benutzer dann ein eigenes TabItem, sodass ich mehrer Benutzer gleichzeitig öffnen kann.

    Das aktive TabItem brauche ich, um zu wissen, welcher Benutzer gerade offen ist, um dann diesen mit einem RibbonButton zu duplizieren, löschen etc.
    Hier - eine Abwandlung des ParentChildView aus die vier Views (in Wpf)

    Beachte, dass Listbox und TabControl dasselbe Binding und dasselbe DataTemplate verwenden:

    XML-Quellcode

    1. <ListBox Grid.Column="2" ItemsSource="{Binding Path=Directories/Files}">
    2. <FrameworkElement.Resources>
    3. </FrameworkElement.Resources>
    4. <ItemsControl.ItemTemplate>
    5. <DataTemplate>
    6. <Border Margin="2" BorderBrush="Aqua" BorderThickness="1" Padding="2">
    7. <Grid >
    8. <Grid.RowDefinitions>
    9. <RowDefinition/>
    10. <RowDefinition />
    11. <RowDefinition/>
    12. </Grid.RowDefinitions>
    13. <Grid.ColumnDefinitions>
    14. <ColumnDefinition />
    15. <ColumnDefinition/>
    16. </Grid.ColumnDefinitions>
    17. <TextBlock Grid.Row="0" Text="Name:" Margin="0,0,5,0" />
    18. <TextBlock Grid.Row="1" Text="FullName:" Margin="0,0,5,0" />
    19. <TextBlock Grid.Row="2" Text="Length:" Margin="0,0,5,0" />
    20. <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"/>
    21. <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=FullName}" TextWrapping="Wrap"/>
    22. <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Length}"/>
    23. </Grid>
    24. </Border>
    25. </DataTemplate>
    26. </ItemsControl.ItemTemplate>
    27. </ListBox>
    28. <TabControl Grid.Column="4" ItemsSource="{Binding Path=Directories/Files}">
    29. <TabControl.ContentTemplate>
    30. <DataTemplate>
    31. <Border Margin="2" BorderBrush="Aqua" BorderThickness="1" Padding="2">
    32. <Grid >
    33. <Grid.RowDefinitions>
    34. <RowDefinition/>
    35. <RowDefinition/>
    36. <RowDefinition/>
    37. </Grid.RowDefinitions>
    38. <Grid.ColumnDefinitions>
    39. <ColumnDefinition Width="Auto" />
    40. <ColumnDefinition/>
    41. </Grid.ColumnDefinitions>
    42. <TextBlock Grid.Row="0" Text="Name:" Margin="0,0,5,0" />
    43. <TextBlock Grid.Row="1" Text="FullName:" Margin="0,0,5,0" />
    44. <TextBlock Grid.Row="2" Text="Length:" Margin="0,0,5,0" />
    45. <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"/>
    46. <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=FullName}" TextWrapping="Wrap"/>
    47. <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Length}"/>
    48. </Grid>
    49. </Border>
    50. </DataTemplate>
    51. </TabControl.ContentTemplate>
    52. </TabControl>
    Ich habe mich allerdings bischen geirrt: Beim TabControl wirds nicht als ItemTemplate verwendet, sondern als ContentTemplate.

    Ach - jetzt hab ich IsSynchronisizedWithCurrentItem vergessen - kriegst du das selbst noch hin?
    Dateien
    • ParentChild00.zip

      (21,99 kB, 208 mal heruntergeladen, zuletzt: )
    Wenn ich das so mache kann ich doch nur einen Typen (sprich nur Benutzer) in dem Tabcontrol anzeigen. Da ich aber verschiedene Typen (z.B. Benutzer und Lieferanten) habe, fande ich die Idee mit einer List<TabItem> nicht so schlecht, da ich dann für jeden Typen ein anderes Formular haben kann.

    Ich bin aber auch davon ausgegangen, dass ein TabControl nur TabItems haben kann und nicht auch andere Typen. - Wieder etwas gelernt 8o -
    Du kannst auch verschiedene Datentypen unterstützen, indem du für jeden unterschiedlichen Datentyp ein darauf passendes DataTemplate in die Resourcen packst:

    XML-Quellcode

    1. <TabControl Grid.Column="4" DisplayMemberPath="Name" ItemsSource="{Binding Path=Directories/Files}" IsSynchronizedWithCurrentItem="True">
    2. <FrameworkElement.Resources>
    3. <DataTemplate DataType="{x:Type io:FileInfo}">
    4. <Border Margin="2" BorderBrush="Aqua" BorderThickness="1" Padding="2">
    5. <Grid >
    6. <Grid.RowDefinitions>
    7. <RowDefinition/>
    8. <RowDefinition/>
    9. <RowDefinition/>
    10. </Grid.RowDefinitions>
    11. <Grid.ColumnDefinitions>
    12. <ColumnDefinition Width="Auto" />
    13. <ColumnDefinition/>
    14. </Grid.ColumnDefinitions>
    15. <TextBlock Grid.Row="0" Text="Name:" Margin="0,0,5,0" />
    16. <TextBlock Grid.Row="1" Text="FullName:" Margin="0,0,5,0" />
    17. <TextBlock Grid.Row="2" Text="Length:" Margin="0,0,5,0" />
    18. <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=Name}"/>
    19. <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=FullName}" TextWrapping="Wrap"/>
    20. <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=Length}"/>
    21. </Grid>
    22. </Border>
    23. </DataTemplate>
    24. </FrameworkElement.Resources>
    25. </TabControl>
    Der TabPage-Bereich eines TabControls ist ein ContentPresenter, und der sucht sich anhand der DataType-Property sein Datatemplate dann selber raus.
    Weiters habe ich nun den DisplayMember gesetzt, womit festgelegt ist, was auf den Reitern displayed wird.
    Das funzt natürlich nur mit Datentypen, die über eine Property dieses Namens verfügen.

    Wpf ist OOP: Guck dir im ObjectBrowser die Vererbungs-Hierarchie an, dann weißt du, wie Listbox, ItemsControl, Selector, TabControl, Datagrid, ListView, Treeview und vlt. sogar noch paar mehr miteinander zusammenhängen, was die gemeinsam haben, und was nicht.

    Architektur-Vorschlag: Mach für verschiedene Datensatz-typen jeweils ein schickes UserControl zurecht, und im MainWindow diese dann in DataTemplates einpacken.
    Ups! Womit wir zu einem Sch... - Problem kommen: In Wpf kann man eingeschachtelte Datentypen überhaupt nicht formulieren ;( . Und typisierte Datarows sind numal eingeschachtelte Datentypen.

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

    Ich denke ich habe es jetzt hinbekommen.
    Ist nur leider etwas Umständlich, da ich für jede Tabelle eine eigene Klasse erstellen muss, um mit DataType arbeiten zu können.

    Ich bekomme immer wieder diese Fehlermeldung im Direktfenster:
    Spoiler anzeigen
    System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=TabStripPlacement; DataItem=null; target element is 'TabItem' (Name=''); target property is 'NoTarget' (type 'Object')

    und das immer 4 mal.
    Dateien
    Nein im Moment noch nicht.

    Zum Testen habe ich einach die zwei Buttons unten gebaut, die mir unterschiedliche Datarows erzeugen.

    Zum Anfang läuft das auch alles, nur mit zunehmender Tabanzahl taucht die Meldung im Direktfenster auf, das Programm läuft aber weiterhin ohne Probleme.
    Es ist auch noch ein ziemlicher Rohbau, um auszuprobieren ob meine Vorstellungen überhaupt umsetzbar sind.
    Normal verschlechtern Binding-Mismatches nur die Performance - abstürzen tut davon nix. Sieht mir nach einem Bug im TabControl aus - da scheint intern iwas ans übergeordnete TC binden zu wollen, und findets nicht, weiß Kuckuck warum.


    Die Basisklasse kannste dir sparen, und stattdessen eine ObservableCollection(Of Object) nehmen - da kannste alles reinschmeißen.
    Und bei 'ner OC brauchste auch nicht nach jedem Add ein CollectionView.Refresh() hinterherzuschicken.