在视图模型中获取选定的选项卡(WPF)

5

我有一个主视图,其中包含一个选项卡控件。当选择一个选项卡时,它会调用相应的视图进行显示。我在视图模型中有一个函数,需要知道选择了哪个选项卡才能执行操作。我该如何实现这一点?视图模型如何知道哪个选项卡被选中?


我认为你可以将选项卡控件的selected属性绑定到视图模型中的一个属性,并在那里访问它。它会知道哪个选项卡被选中,因为...这就是模型绑定的作用 :) - Sinaesthetic
谢谢。我会尝试的。 - nan
我包含了下面的一个例子。 - Sinaesthetic
在某些情况下,使用SelectedIndex处理可能不是一个好方法。如果您有一个代表标签的模型(这些模型的集合绑定到选项卡控件的ItemsSource上),那么只需将SelectedItem绑定到您的ViewModel即可。通过SelectedIndex来回翻译是浪费时间的。 - user1228
4个回答

7

简而言之:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication2"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:TestViewModel x:Key="MainViewModel"/>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <TabControl DataContext="{StaticResource MainViewModel}" 
                    SelectedIndex="{Binding Selected}" 
                    Grid.Row="0" 
                    x:Name="TestTabs">
            <TabItem Header="Section 1"/>
            <TabItem Header="Section 2"/>
            <TabItem Header="Section 3"/>
        </TabControl>
        <Button Content="Check 
                Selected Index" 
                Grid.Row="1" 
                x:Name="TestButton" 
                Click="TestButton_OnClick"/>
    </Grid>
</Window>

这里以数据上下文的形式声明定义了该模型。selectedindex属性绑定到模型,因此每当它发生更改时,映射到视图模型上的属性也会相应更改。

class TestViewModel : INotifyPropertyChanged
{
    private int _selected;
    public int Selected
    {
        get { return _selected; }
        set
        {
            _selected = value;
            OnPropertyChanged("Selected");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string property)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
}

这个实现了INotifyPropertyChanged接口,所以视图会注册它。在这里的处理器中,我输出了Selected的值以展示它们的变化。
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void TestButton_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = TestTabs.DataContext as TestViewModel;
        MessageBox.Show(string.Format("You selected tab {0}", vm.Selected));
    }
}

这会获取视图模型,然后显示属性已经成功更新。


我的需求略有不同,但这个解决方案正是所需要的,一样棒极了。干得好! - AndyUK

2
在View中,您可以将SelectedIndex属性放在TabControl上:
xmlns:cal="http://www.caliburnproject.org"

<TabControl cal:Message.Attach="[SelectionChanged] = [OnTabSelectionChanged()]"
                    SelectedIndex="{Binding SelectedIndexTab}">
            <TabItem Header="Tab 1"/>
            <TabItem Header="Tab 2"/>
</TabControl>

在ViewModel中,你可以声明一个公共属性名为SelectedIndexTab和一个OnTabSelectionChanged()方法来操作。
public int SelectedIndexTab { get; set; }

在这个例子中,我使用Caliburn来捕获TabControl的SelectionChange事件。

嗨@apo,我正在使用这个解决方案,但我不知道为什么,在选择最后一个选项卡时,事件OnTabSelectionChanged()会被无限调用。 - Ajendra Prasad

0

这里有一个简单的例子。

您应该将选项卡的ItemsSource绑定到ObservableCollection,它应该包含有关应创建的选项卡的信息的模型。

这是表示选项卡页面的VM和模型:

public class ViewModel
{
    public ObservableCollection<TabItem> Tabs { get; set; }
    public ViewModel()
    {
        Tabs = new ObservableCollection<TabItem>();
        Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
        Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
    }
}
public class TabItem
{
    public string Header { get; set; }
    public string Content { get; set; }
}

这是视图和 VM 绑定。
<Window x:Class="WpfApplication12.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <ViewModel
        xmlns="clr-namespace:WpfApplication12" />
</Window.DataContext>
<TabControl
ItemsSource="{Binding Tabs}">
<TabControl.ItemTemplate>
    <!-- this is the header template-->
    <DataTemplate>
        <TextBlock
            Text="{Binding Header}" />
    </DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
    <!-- this is the body of the TabItem template-->
    <DataTemplate>
        <----- usercontrol namespace goes here--->
    </DataTemplate>
</TabControl.ContentTemplate>

来源:链接


0
您可以使用 Selector 基类提供的 SelectionChanged 事件。SelectionChangedEventArgs 将包含新选择(和取消选择)的项。或者,您可以将 Selector 基类的 SelectedItem 绑定到 ViewModel 中的属性,然后在 setter 中执行一些逻辑。
一般而言,在 MVVM 中,将视图特定对象传递给 ViewModel 被认为是违反了 MVVM 的原则 - 它将 UI 框架(在此情况下为 WPF)与更通用的 ViewModel 逻辑紧密耦合起来。更好的方法是将事件处理程序放在 UI 代码后面,然后相应地对 view-model 进行操作,但不要将视图对象作为参数传递。

没错 - 在该处理程序中收集你所需的信息,然后将其传递给你的DataContext(VM)上的一个方法。 - Andrew
谢谢。我几天前开始接触MVVM,但仍在努力中。 - nan
不客气!MVVM肯定需要一些时间来适应——哪些数据属于哪里的细节并不是微不足道的。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接