WPF选项卡控件和MVVM选择

NZJ*_*mes 19 wpf tabcontrol prism mvvm

我在MVVM WPF应用程序中有一个TabControl .它的定义如下.

<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>
Run Code Online (Sandbox Code Playgroud)

因此,每个标签项内容都有自己的视图与之关联.这些视图中的每一个都具有MEF [Export]属性,并通过视图发现与相关区域相关联,因此上面的代码就是我需要让标签控件加载并在它们之间切换.它们都引用了它们后面的相同共享ViewModel对象,因此所有对象都无缝地交互.

我的问题是,当用户导航到父窗口时,我希望选项卡控件默认为第二个选项卡项.通过IsSelected="True"在TabItem编号2 中的XAML 中指定,在首次加载窗口时这很容易做到.当用户离开屏幕然后返回到屏幕时,这样做不太容易.

我想SelectedItem={Binding SelectedTabItem}在tab控件上有一个属性,所以我可以在ViewModel中以编程方式设置所选的选项卡,但问题是我不知道ViewModel中的TabItem对象,因为它们仅在XAML中声明,所以我没有TabItem对象传递给setter属性.

我有一个想法是让子视图(形成上面每个标签项的内容)在他们的XAML的UserControl级别上有一个样式,如下所示.

<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>
Run Code Online (Sandbox Code Playgroud)

我知道发现位不正确; 我只是把它放在那里指定我的意图,但我不确定确切的语法.基本上每个UserControl都有一个触发器来监听ViewModel上的属性(不知道我将如何区分每个不同的UserControl,因为显然他们不能全部监听相同的属性,或者当属性设置为是的,但是每个usercontrol都有一个属性看起来很难看,然后找到它的父TabItem容器并将IsSelected值设置为true.

我是否在正确的轨道上找到解决方案?有可能做我正在思考的事吗?有更整洁的解决方案吗?

She*_*dan 43

如果你看一下TabControl的MSDN页面,你会发现一个叫做财产SelectedIndex这是一个int.因此,只需将一个int属性添加到视图模型中,Bind然后添加到TabControl.SelectedIndex属性中,然后您就可以随时从视图模型中选择您喜欢的任何选项卡:

<TabControl SelectedIndex="{Binding SelectedIndex}">
    ...
</TabControl>
Run Code Online (Sandbox Code Playgroud)

更新>>>

使用此方法设置"启动"选项卡更加容易:

在视图模型中:

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

public int SelectedIndex { get; set; } // Implement INotifyPropertyChanged here
Run Code Online (Sandbox Code Playgroud)

  • -1为不礼貌,+1为一个好的答案.SO是一个很好的地方,可以找到非常具体问题的明确答案.我认为人们可以"总结"MSDN页面上已有的内容.当你知道你的问题是什么时,它往往比MSDN更具可读性,更好,更快. (4认同)
  • -1因为不礼貌而且使用不当MVVM.将所选页面索引绑定为**int**是*not type safe*.使用**enum**和**IValueConverter**将更符合良好的MVVM实践 (4认同)
  • 我不能指出用户可以在MSDN上快速轻松地找到这些答案吗? (2认同)

小智 10

下面的代码示例将使用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>
Run Code Online (Sandbox Code Playgroud)

模态类

TabCategoryItem表示每个选项卡项.在两个属性上, TabHeader将显示选项卡标题,TabContent包含要填充每个选项卡的内容/控件.

Public Class TabCategoryItem

    Public Property TabHeader As String
    Public Property TabContent As UIElement
End Class
Run Code Online (Sandbox Code Playgroud)

VM类

Public Class vmClass

    Public Property tabCategory As ObjectModel.ObservableCollection(Of TabCategoryItem)
    Public Property SelectedCategory As TabCategoryItem
End Class
Run Code Online (Sandbox Code Playgroud)

以下代码将填充并绑定内容.我正在创建两个选项卡,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
Run Code Online (Sandbox Code Playgroud)


小智 9

仅供参考,我经历了同样的问题,我使用ObservableCollection源动态添加标签但最后添加的标签没有被选中.Sheridan说按照SelectedIndex选择Tab,我做了相同的更改.现在最后添加了Tab被选中,但它没有得到集中.因此要集中Tab,我们必须添加set Binding IsAsync属性True.

<TabControl ItemsSource="{Binding Workspaces}" Margin="5" SelectedIndex="{Binding TabIndex, Mode=OneWay,UpdateSourceTrigger=PropertyChanged, IsAsync=True}">
Run Code Online (Sandbox Code Playgroud)