1

So I currently have a Window with a TabControl. The MainWindow has its own ViewModel and all the TabItems have their own ViewModels also.

I can easily change tabs from the MainWindow ViewModel through a bound SelectedIndex property. What I would like to do is change to another tab from code that runs within ANOTHER tab viewmodel. Since the Tabs are not part of the MainWindowViewModel, I am looking for a clean way to change the Tab without resorting to code behind to do it.

There are also cases, where I might need to change the tab from something such as a message prompt. I thinking my only way is to create and event and subscribe to that from MainWindowViewModel.

David Bentley
  • 824
  • 1
  • 8
  • 27
  • *Since the Tabs are not part of that ViewModel* that's a code smell. The easiest way to do it is to bind a collection to ItemsSource on the tab control that contains each page view model, then bind to SelectedItem and put whatever page view model should be the selected tab in that property. That's the easiest, simplest, most useful and reliable way to do it. Why aren't you doing it that way? (Here's a working prototype for you https://stackoverflow.com/a/5651542/1228) –  Jan 17 '18 at 21:27
  • @Will I think you completely miss understood my question. I am not generating new viewmodels. Each tab has its own pre-established viewmodel and I was looking for an easy way to set the `SelectedIndex` data binding from the MainWindow viewmodel from a tab viewmodel. I actually already found a simple way to do it that I am about to post. – David Bentley Jan 17 '18 at 21:34

2 Answers2

2

So I solved this with an EventAggregator.

public static class IntAggregator
{
    public static void Transmit(int data)
    {
        if (OnDataTransmitted != null)
        {
            OnDataTransmitted(data);
        }
    }

    public static Action<int> OnDataTransmitted;
}

First ViewModel sends data.

public class ModifyUsersViewModel : INotifyPropertyChanged
{
    private void change_tab(int data)
    {
        IntAggregator.Transmit(data);
    }
}

Second ViewModel receives data and then does something with it.

public class MainWindowViewModel : INotifyPropertyChanged
{
    private int _Tab_SelectedIndex = 0;
    public int Tab_SelectedIndex
    {
        get
        {
            return _Tab_SelectedIndex;
        }
        set
        {
            _Tab_SelectedIndex = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Tab_SelectedIndex"));
        }
    }

    public MainWindowViewModel()
    {
        IntAggregator.OnDataTransmitted += OnDataReceived;
    }

    private void OnDataReceived(int data)
    {
        Tab_SelectedIndex = data;
    }
}
David Bentley
  • 824
  • 1
  • 8
  • 27
0

Rather than trying to bind to SelectedIndex, if the TabItems have their own view models, then you can create a property for each of those view models: IsSelected and then bind the TabItem.IsSelected property to that:

<TabItem IsSelected="{Binding IsSelected}">

This prevents the view models from needing to know the index of their corresponding TabItem, something I would argue is a detail that should be specific to the view and something the view model should not concern itself with. What if you add another TabItem or want to change the order? Now you've got changes to make in the view models for something that could be just simple change to the view.

Dave M
  • 2,863
  • 1
  • 22
  • 17
  • This still does not accomplish what I need. Since that property binding will only be associated with its own datacontext, you still need some sort of EventAggregator or messaging system to have one tab open another tab. – David Bentley Jan 18 '18 at 14:36