如何在不违反MVVM模式的情况下从WPF中的TabControl更改Tab

时间:2013-03-06 16:48:44

标签: c# wpf design-patterns mvvm mvvm-light

我的WPF Windows包含一个TabControl,它显示不同选项卡上的内容。单击下面的按钮可通过ICommand接口/ Binding执行方法。被调用的方法生成要在第二个选项卡中显示的文本。

Application Mockup

如何在不违反MVVM模式的情况下切换到按钮单击时的第二个选项卡?

我尝试将TabItem.IsSelected属性绑定到我的ViewModel中的某些内容,但我也想使用其他标签(tab1)。

有什么想法吗?

5 个答案:

答案 0 :(得分:13)

我自己发现了。

关键是双向绑定。单击该按钮时,它将属性DisplayXamlTab设置为true。 IsSelected属性绑定到此变量。如果单击另一个选项卡,绑定将DisplayXamlTab属性设置为false。

注意:UpdateSourceTrigger=PropertyChanged也很重要

代码如下:

XAML:

        <TabItem Header="XAML" IsSelected="{Binding DisplayXamlTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <Grid Background="#FFE5E5E5">
                <TextBox x:Name="TxtXamlOutput" IsReadOnly="True" Text="{Binding XamlText, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
            </Grid>
        </TabItem>

C#属性:

private bool displayXamlTab;
public bool DisplayXamlTab
{
    get { return this.displayXamlTab; }
    set
    {
        this.displayXamlTab = value;
        this.RaisePropertyChanged("DisplayXamlTab");
    }
}

答案 1 :(得分:12)

如果您要使用MVVM方式,那么您将在后面的代码中创建两个依赖项属性:

  • ObservableCollection<ItemType> Items;
  • ItemType MySelectedItem;

然后,将TabControl ItemsSource属性绑定到 Items ,并将SelectedItem属性绑定到 MySelectedItem

    <TabControl ItemsSource="{Binding Items}"
        SelectedItem="{Binding MySelectedItem, Mode=TwoWay}">
<TabControl.ItemTemplate>
    <DataTemplate>
        <... here goes the UI to display ItemType ... >
    </DataTemplate>
  </TabControl.ItemTemplate>
</TabControl>

如果要更改所选选项卡,只需更新MySelectedItem依赖属性

即可

答案 2 :(得分:2)

虽然这个问题相当陈旧且已经得到很好的解答,但我想我会添加这个额外的答案来演示更改TabItem中所选TabControl的替代方法。如果您有每个TabItem的视图模型,那么在其中包含IsSelected属性以确定它是否被选中会很有帮助。可以使用IsSelected属性将此TabItem.IsSelected属性与ItemContainerStyle属性进行数据绑定:

<TabControl ItemsSource="{Binding MenuItems}" TabStripPlacement="Top">
    <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}"> 
            <StackPanel Orientation="Horizontal">
                <Image Source="{Binding ImageSource}" Margin="0,0,10,0" />
                <TextBlock Text="{Binding HeaderText}" FontSize="16" />
            </StackPanel>
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
            <ContentControl Content="{Binding ViewModel}" />
        </DataTemplate>
    </TabControl.ContentTemplate>
    <TabControl.ItemContainerStyle>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="IsSelected" Value="{Binding IsSelected}" />
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

您现在可以从父视图模型更改选定的TabItem,如下所示:

MenuItems[0].IsSelected = true;

请注意,因为此属性是绑定到TabItem.IsSelected属性的数据,所以调用此...:

MenuItems[1].IsSelected = true;

...实际上还会自动将MenuItems[0].IsSelected属性设置为false。因此,如果您正在使用的视图模型将其IsSelected属性设置为true,那么您可以确保在TabControl中选择了其相关视图。

答案 3 :(得分:1)

您可以在视图模型和TabControl.SelectedIndex属性之间创建绑定 - 即,0选择第一个TabItem,1选择第二个,等等。

<TabControl DataContext="..." SelectedIndex="{Binding SomeVmProperty}" ...

(或者,根据您设置的方式,您可以绑定SelectedItem ...)

答案 4 :(得分:0)

您可能希望使用某种“Event Aggregator”模式(即MVVM Light中的Messenger类)来广播某种“导航”消息。您的视图 - TabControl - 可以侦听特定消息,并在收到消息时导航到Tab2。

或者,您可以将TabControl的“SelectedItem”属性绑定到ViewModel,只需从VM中调用CurrentTab = MySecondTabViewModel即可。这是@HighPoint在对OP的评论中推荐的approach,但我不是粉丝;见下文。此方法的另一个警告是您需要熟悉DataTemplates,因为您需要将视图映射到您显示的每个ViewModel。

我个人喜欢第一种方法,因为我不认为它是ViewModel处理标签导航的“责任”。如果您只是在ViewModel中更改数据时提醒View,则允许View决定是否要更改标签。