Get the selected tab in the view model (wpf)
Asked Answered
P

4

5

I have one main view which has a tab control. When a tab is selected, it calls the appropriate view to display. I have a function in view model which has to know which tab was selected to preform an operation. How do I achieve this? How will the view model know which tab is selected?

Placeman answered 3/12, 2014 at 3:50 Comment(3)
I think you can just bind the selected property of the tab control to a property in your viewmodel and access it there. It will know what is selected because... that's what model binding does :)Shear
I included an example below.Shear
SelectedIndex is a bad way to handle this in some cases. If you have a Model that represents a tab (a collection of these models are bound to ItemsSource of the tab control) then simply bind SelectedItem to your ViewModel. Translating back and forth via SelectedIndex is a waste of time.Skerry
S
7

Quite simply:

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

The model is defined here, declaratively, as a data context. The selectedindex property is bound to the model so any time it changes, the propery it is mapped to on the view model will also change

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));
        }
    }
}

This implements INotifyPropertyChanged so the view will register with it. In the handler here, I output the value of Selected to show the as you change them.

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));
    }
}

This gets the viewmodel and then shows us that the properties were in fact updated.

Shear answered 3/12, 2014 at 4:19 Comment(1)
My requirement was slightly different, but this solution is just what the doctor ordered, all the same. Nice one!Roccoroch
A
2

In View, you put SelectedIndex property on 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>

In ViewModel, you declare a public property name SelectedIndexTab and OnTabSelectionChanged() method to operate.

public int SelectedIndexTab { get; set; }

In this example, I use Caliburn to catch SelectionChange event of TabControl.

Apodosis answered 3/12, 2014 at 4:9 Comment(1)
Hi @apo, I am using this solution but I don't know why, on selecting the last tab event OnTabSelectionChanged() is getting called infinitely.Ichthyology
G
0

You can use the SelectionChanged event provided by the Selector base class. The SelectionChangedEventArgs will contain the newly selected (and deselected) items. Alternatively, you can bind the SelectedItem of the Selector Base class to a property in your ViewModel, and then perform some logic in the setter.

Generally though, it's considered a violation of MVVM to pass view-specific objects to your ViewModels - it tightly couples the UI framework (WPF in this case) to the more generic ViewModel logic. A better route is to put event handlers in your UI code-behind which in turn act on the view-model appropriately, but without passing View objects as parameters.

Genu answered 3/12, 2014 at 4:1 Comment(3)
that's correct - gather the information you need in that handler, and then pass it to a method on your DataContext (VM)Genu
Thanks. I started with mvvm a few days ago and am still struggling.Placeman
your welcome! MVVM definitely takes some getting used to - the details of what data belongs where is not trivial.Genu
C
0

Here is a simple example.

You should be binding the ItemsSource of the Tab to your ObservableCollection, and that should hold models with information about the tabs that should be created.

Here are the VM and the model which represents a tab page:

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

Here`s the View and VM binding

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

Source:link

Crossman answered 3/12, 2014 at 4:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.