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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15253225/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 07:55:24  来源:igfitidea点击:

How to change Tab from TabControl in WPF without violating the MVVM pattern

c#wpfdesign-patternsmvvmmvvm-light

提问by Joel

My WPF Windows contains a TabControl which displays content on different tabs. A click on the button below executes a method via ICommand interface / Binding. The called method generates text which is intended to be displayed in the second tab.

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

Application Mockup

应用样机

How can I switch to the second tab on button click without violating the MVVM Pattern?

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

I tried to bind the TabItem.IsSelected Property to something in my ViewModel but I wanted to use the other tabs (tab1) as well.

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

Any thoughts?

有什么想法吗?

回答by Joel

I found it out by myself.

我自己发现的。

The key is a two way binding. When the button is clicked it sets the property DisplayXamlTabtrue. The IsSelectedattribute is bound to this variable. if another tab is clicked the binding will set the DisplayXamlTab Property to false.

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

Note: UpdateSourceTrigger=PropertyChangedis also very important

注:UpdateSourceTrigger=PropertyChanged也很重要

Code comes below:

代码如下:

XAML:

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# Property:

C# 属性:

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

回答by sim1

if you're going the MVVM way you're going to create two dependency properties in the code behind:

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

  • ObservableCollection<ItemType> Items;
  • ItemType MySelectedItem;
  • ObservableCollection<ItemType> Items;
  • ItemType MySelectedItem;

Then, bind the TabControl ItemsSource property to the Itemsand bind the SelectedItem property to 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>

When you want to change the selected tab, simply update the MySelectedItem dependecy property

当您想更改选定的选项卡时,只需更新 MySelectedItem 依赖属性

回答by Sheridan

Although this question is fairly old and well answered already, I thought I'd add this additional answer to demonstrate an alternative way of changing the selected TabItemin a TabControl. If you have a view model for each TabItem, then it can be helpful to have an IsSelectedproperty in it to determine whether it is selected or not. It is possible to data bind this IsSelectedproperty with the TabItem.IsSelectedproperty using the ItemContainerStyleproperty:

虽然这个问题已经很老了并且已经得到了很好的回答,但我想我会添加这个额外的答案来演示TabItemTabControl. 如果您为 each 有一个视图模型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>

You can now change the selected TabItemfrom the parent view model like this:

您现在可以TabItem像这样更改从父视图模型中选择的:

MenuItems[0].IsSelected = true;

Note that because this property is data bound to the TabItem.IsSelectedproperty, calling this...:

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

MenuItems[1].IsSelected = true;

... will infact also automatically set the MenuItems[0].IsSelectedproperty to false. so if the view model that you are working with has its IsSelectedproperty set to true, then you can be sure that its related view is selected in the TabControl.

...事实上也会自动将MenuItems[0].IsSelected属性设置为false. 因此,如果您正在使用的视图模型的IsSelected属性设置为 true,那么您可以确保在TabControl.

回答by JerKimball

You can create a binding between the view model and the TabControl.SelectedIndexproperty - i.e., 0 selects the first TabItem, 1 selects the second, etc.

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

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

(alternatively, depending on how you've got things set up, you could bind against SelectedItem...)

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

回答by BTownTKD

You'll likely want to use some sort of "Event Aggregator" pattern (I.e. the Messengerclass in MVVM Light) to broadcast some sort of "navigation" message. Your View - the TabControl - can listen for the specific message, and navigate to Tab2 when the message is received.

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

Alternatively, you can bind the "SelectedItem" property of the TabControl to your ViewModel, and simply call CurrentTab = MySecondTabViewModelfrom within your VM. This is the approachrecommended by @HighPoint in the comments to the OP, but I'm not a fan; see below. Another caveat to this approach is that you need to be familiar with DataTemplates, as you will need to map a view to each ViewModel which you display.

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

I personally like the first approach, because I don't consider it to be a "responsibility" of the ViewModel to handle tab navigation. If you simply alert your View when data changes in your ViewModel, you allow the View to decide whether or not it wants to change tabs.

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