Contrôle de l'onglet WPF et sélection des MVVM

j'ai un TabControl dans un MVVMWPF application. Elle est définie comme suit.

<TabControl Style="{StaticResource PortfolioSelectionTabControl}" SelectedItem="{Binding SelectedParameterTab}" >
    <TabItem Header="Trades" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Margin="0,10,0,5" Name="NSDetailTradeRegion" cal:RegionManager.RegionName="NSDetailTradeRegion" />
    </TabItem>
    <TabItem Header="Ccy Rates" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Margin="0,10,0,5" Name="NSDetailCcyRegion" cal:RegionManager.RegionName="NSDetailCcyRegion" />
    </TabItem>
    <TabItem Header="Correlations / Shocks" Style="{StaticResource PortfolioSelectionTabItem}">
        <ContentControl Name="NSDetailCorrelationRegion" cal:RegionManager.RegionName="NSDetailCorrelationRegion" />
    </TabItem>
    <TabItem Header="Facility Overrides" Style="{StaticResource PortfolioSelectionTabItem}" IsEnabled="False">
        <ContentControl Name="NSDetailFacilityOverrides" cal:RegionManager.RegionName="NSDetailFacilityOverrides" />
    </TabItem>
</TabControl>

ainsi chaque élément de l'onglet Contenu a sa propre vue associée avec elle. Chacun de ces points de vue a l' MEF[Export] attribut et est associé à la région concernée par la découverte de la vue, de sorte que le code ci-dessus est tout ce dont j'ai besoin pour avoir la charge de contrôle de l'onglet et basculer entre eux. Ils font tous référence au même objet ViewModel partagé derrière eux et donc tous interagissent de façon transparente.

Mon problème est que lorsque l'utilisateur accède à la fenêtre parent, je veux le contrôle onglet par défaut pour la deuxième onglet de l'élément. Cela est assez facile à faire lorsque la fenêtre est chargée pour la première fois, en spécifiant dans XAML IsSelected="True" dans le tableau 2. Il est moins facile à faire lorsque l'utilisateur quitte l'écran et revient vers elle.

j'ai pensé avoir un SelectedItem={Binding SelectedTabItem} propriété sur le contrôle de l'onglet, de sorte que je pourrais programmatically définir le sélectionné tab dans le ViewModel, mais le problème est que je ne connais pas les objets TabItem dans le ViewModel car ils sont déclarés ci-dessus dans le XAML seulement, donc je n'ai pas D'objet TabItem à passer à la propriété setter.

une idée que j'ai eu était de faire les vues de l'enfant (qui forment le contenu de chacun des éléments de l'onglet ci-dessus) ont un style sur le niveau UserControl de leur XAML, quelque chose le long de ce qui suit.

<Style TargetType={x:Type UserControl}>
    <Style.Triggers>
        <DataTrigger Property="IsSelected" Value="True">
             <Setter Property="{ElementName={FindAncestor, Parent, typeof(TabItem)}, Path=IsSelected", Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

je sais que le morceau findancestor n'est pas correct; je viens de le dire là pour préciser mon intention, mais je ne suis pas sûr de la syntaxe exacte. Fondamentalement pour chaque UserControl d'avoir un déclencheur qui écoute une propriété sur le ViewModel (pas sûr comment je distinguerais chaque UserControl différent comme évidemment ils ne peuvent pas tous écouter la même propriété ou ils choisiraient tous simultanément quand la propriété est définie à True, mais avoir une propriété pour chaque usercontrol semble laid) et trouve ensuite son conteneur de TabItem parent et fixe la valeur sélectionnée à vrai.

Suis-je sur la bonne voie avec une solution ici? Est-il possible de faire ce que je suis en méditant? Est-il une solution plus propre?

17
demandé sur Peter Mortensen 2013-11-08 14:49:33

3 réponses

Si vous regardez l' TabControl Classe page sur MSDN, vous trouverez une propriété appelée SelectedIndex qui est int. Donc, il suffit d'ajouter un int propriété dans votre modèle de vue et Bind la TabControl.SelectedIndex propriété et ensuite vous pouvez sélectionner l'onglet que vous voulez à tout moment à partir du modèle de vue:

<TabControl SelectedIndex="{Binding SelectedIndex}">
    ...
</TabControl>

mise à JOUR >>>

définir un onglet' startup ' est encore plus facile en utilisant cette méthode:

en vue modèle:

private int selectedIndex = 2; // Set the field to whichever tab you want to start on

public int SelectedIndex { get; set; } // Implement INotifyPropertyChanged here
39
répondu Sheridan 2017-04-22 01:35:02

l'exemple de code ci-dessous va créer un onglet dynamique en utilisant MVVM.

XAML

<TabControl Margin="20" x:Name="tabCategory"
                ItemsSource="{Binding tabCategory}"
                SelectedItem="{Binding SelectedCategory}">

    <TabControl.ItemTemplate>
        <DataTemplate>
            <HeaderedContentControl Header="{Binding TabHeader}"/>
        </DataTemplate>
    </TabControl.ItemTemplate>

    <TabControl.ContentTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding TabContent}" />
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

Classe Modale

TabCategoryItem représente chaque élément de l'onglet. Sur les deux propriétés, TabHeader affichera une légende d'onglet et TabContent contient le contenu/de contrôle à remplir dans chaque onglet.

Public Class TabCategoryItem

    Public Property TabHeader As String
    Public Property TabContent As UIElement
End Class

Classe VM

Public Class vmClass

    Public Property tabCategory As ObjectModel.ObservableCollection(Of TabCategoryItem)
    Public Property SelectedCategory As TabCategoryItem
End Class

le code ci-dessous remplira et reliera le contenu. Je suis création de deux onglets, tab1 et tab2. Les deux onglets contiennent des zones de texte. Vous pouvez utiliser N'importe quel élément à la place des boîtes de texte.

Dim vm As New vmClass

vm.tabCategory = New ObjectModel.ObservableCollection(Of TabCategoryItem)

'VM.tabCategory colection will create all tabs

vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab1", .TabContent = new TextBlock().Text = "My first Tab control1"})
vm.tabCategory.Add(New TabCategoryItem() With {.TabHeader = "Tab2", .TabContent = new TextBlock().Text = "My first Tab control2"})

mywindow.DataContent = vm
9
répondu Rosh 2016-06-12 12:06:51

juste pour info, Je suis passé par le même problème où j'ajoute des onglets dynamiquement en utilisant ObservableCollection source mais le dernier onglet ajouté ne sont pas sélectionnés. J'ai fait les mêmes changements que ce que Sheridan a dit de sélectionner L'onglet selon SelectedIndex. Maintenant le dernier onglet ajouté est sélectionné, mais il ne se concentrait pas. Donc pour focaliser l'onglet nous devons ajouter la propriété de liaison isasync True.

<TabControl ItemsSource="{Binding Workspaces}" Margin="5" SelectedIndex="{Binding TabIndex, Mode=OneWay,UpdateSourceTrigger=PropertyChanged, IsAsync=True}">
7
répondu Sudhir 2014-02-12 08:14:45