Элемент управления вкладкой WPF и выбор MVVM

У меня есть TabControl в MVVM приложение WPF, Он определяется следующим образом.

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

Таким образом, каждое содержимое элемента табуляции имеет свой собственный вид, связанный с ним. Каждый из этих представлений имеет атрибут MEF [Export] и связан с соответствующим регионом через обнаружение открытий, поэтому приведенный выше код - это все, что мне нужно иметь нагрузку управления вкладкой и переключаться между ними. Все они ссылаются на один и тот же общий объект ViewModel позади них, и поэтому все взаимодействуют без проблем.

Моя проблема в том, что когда пользователь переходит к родительскому окну, я хочу, чтобы элемент управления табуляции по умолчанию был вторым элементом табуляции. Это легко сделать при первом загрузке окна, указав в XAML IsSelected="True" в TabItem номер 2. Это менее легко сделать, когда пользователь переходит от экрана, а затем возвращается к нему.

Я думал о наличии свойства SelectedItem={Binding SelectedTabItem} в элементе управления вкладкой, поэтому я мог программно установить выбранную вкладку в ViewModel, но проблема в том, что я не знаю объектов TabItem в ViewModel, поскольку они объявлены выше в только XAML, поэтому у меня нет объекта TabItem для перехода к свойству setter.

Одна из моих идей заключалась в том, чтобы сделать дочерние представления (которые составляют содержимое каждого из элементов табуляции выше) имеют стиль на уровне UserControl их XAML, что-то вроде следующего.

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

Я знаю, что бит findcanestor неверен; Я просто поставил его там, чтобы указать свои намерения, но я не уверен в точном синтаксисе. В основном для каждого UserControl есть триггер, который прослушивает свойство в ViewModel (не уверен, как бы я различал каждый отдельный UserControl, поскольку, очевидно, они не могут все прослушивать одно и то же свойство или все они будут выбирать одновременно, когда свойство установлено на Правда, но наличие свойства для каждого usercontrol кажется уродливым), а затем находит свой родительский контейнер TabItem и устанавливает значение IsSelected равным true.

Я на правильном пути с решением здесь? Можно ли делать то, что я обдумываю? Есть ли более аккуратное решение?

Ответ 1

Если вы посмотрите страницу TabControl Class в MSDN, вы найдете свойство под названием SelectedIndex, которое является int. Поэтому просто добавьте свойство int в вашу модель представления и Bind к свойству TabControl.SelectedIndex, а затем вы можете выбрать любую вкладку, которая вам нравится в любое время, из модели просмотра:

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

ОБНОВЛЕНИЕ → >

Настройка вкладки "запуск" еще проще с помощью этого метода:

В модели просмотра:

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

public int SelectedIndex { get; set; } // Implement INotifyPropertyChanged here

Ответ 2

В приведенном ниже примере кода будет создана динамическая вкладка с использованием 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>

Модальный класс

TabCategoryItem представляет каждый элемент табуляции. В двух свойствах TabHeader отобразит заголовок закладки, а TabContent содержит содержимое/элемент управления для заполнения каждой вкладки.

Public Class TabCategoryItem

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

Класс VM

Public Class vmClass

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

Следующий код заполнит и свяжет содержимое. Я создаю две вкладки tab1 и tab2. Обе вкладки будут содержать текстовые поля. Вы можете использовать любой UIelement вместо текстовых полей.

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

Ответ 3

Просто FYI, Я прошел ту же проблему, когда динамически добавляю вкладки с использованием источника ObservableCollection, но последняя добавленная вкладка не выбрана. Я сделал то же самое, что сказал Шеридан, чтобы выбрать Tab в соответствии с SelectedIndex. Теперь добавлена ​​последняя добавленная вкладка, но она не фокусировалась. Поэтому, чтобы сфокусировать Tab, мы должны добавить свойство привязки IsAsync True.

<TabControl ItemsSource="{Binding Workspaces}" Margin="5" SelectedIndex="{Binding TabIndex, Mode=OneWay,UpdateSourceTrigger=PropertyChanged, IsAsync=True}">