1

I want to change a complete TabControl based on the ListItem selection. So, I generate a Dictionary with Key(list item) and Value(TabControl) in a loop. Each TabControl contains 10 TabItems.

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        ListboxContainer listBoxContainer = new ListboxContainer();
        TabControlContainer tabControlContainer = new TabControlContainer();

        Dictionary<string, TabControl> tabControlDict = new Dictionary<string, TabControl>();

        public MainWindow()
        {
            InitializeComponent();

            listBoxContainer.ListBoxItems = new string[] { "TabControl1", "TabControl2", "TabControl3" };

            DataContext = listBoxContainer;

            // Generate tabcontrols per listbox item
            foreach (var key in listBoxContainer.ListBoxItems)
            {
                TabControl tabControl = new TabControl();
                TabItem tabItem = new TabItem();

                for (int i = 0; i < 10; i++)
                {
                    tabItem.Header = $"Header: {i}";
                }

                tabControl.Items.Add(tabItem);

                tabControlDict.Add(key, tabControl);
            }

            tabControlContainer.TabControls = tabControlDict.Values.ToArray();


        }

    }

    public class TabControlContainer : INotifyPropertyChanged
    {
        private TabControl[] _tabControls;

        public TabControl[] TabControls
        {
            get => _tabControls;
            set
            {
                if (value != _tabControls)
                {
                    _tabControls = value;
                    NotifyPropertyChanged("TabControls");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class ListboxContainer : INotifyPropertyChanged
    {
        private string[] _listBoxitems;

        public string[] ListBoxItems
        {
            get => _listBoxitems;
            set
            {
                if (value != _listBoxitems)
                {
                    _listBoxitems = value;
                    NotifyPropertyChanged("ListBoxItems");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I tried to bind the stackpanel to the tabcontrols, but I can't get it to work.

Here is the MainWindow.xaml code.

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <DockPanel>
        <ListBox x:Name="Listbox" ItemsSource="{Binding ListBoxItems}"/>
        <StackPanel x:Name="TabControlContainer">
            <ItemsControl ItemsSource="{Binding TabControls}">
                <!--<ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>-->
            </ItemsControl>
        </StackPanel>
    </DockPanel>
</Window>

So, basically, I want to achieve a Master/Detail view, where Master is the listbox and Detail is its belonding TabControl.

I know, that I can manually add/delete children of the Stackpanel, based on list item selection, but it seems to me not the MVVM way.

PythonNoob
  • 914
  • 1
  • 7
  • 15
  • 1
    What you have isn't mvvm, You should be binding collections of viewmodels rather than controls. Those should then be templated out into UI. With none of that listbox container stuff. – Andy Apr 27 '19 at 15:43

1 Answers1

1

You want a ListBox and a TabControl.

Both controls you have there are itemscontrols.

They both inherit from Selector so they have SelectedItem.

Bind a collection of viewmodels to your listbox, call these something meaningful. PersonViewModel or FruitViewModel or something. Don't call them listboxitem or anything that is the same name or anything like the name of a UI control. One per row. But let's call them RowVM as in a viewmodel holding the data for a listbox row.

If you bind to SelectedItem of that ListBox you get an instance of RowVM.

You can bind that from your listbox to a property in your WindowViewModel. You can also bind to that property from some other control.

Let's go back to meaningful names though.

Maybe a Person has Shoes. A collection of Shoe that you're going to show in that tabcontrol. Choose a Person in the listbox and you see their shoes in the tabcontrol. So PersonViewModel would have an observablecollection of shoe.

You can bind itemssource of that tabcontrol to Shoes of the selected item of the listbox. Which will then grab that collection of shoes for whichever person you choose in the listbox. That'll be quite a clunky sort of a binding though and binding both to a property in your viewmodel is usually cleaner.

If you bind that listbox to a collectionview, you can alternatively use the current item / notation in your binding.

https://social.technet.microsoft.com/wiki/contents/articles/29859.wpf-tips-bind-to-current-item-of-collection.aspx

And of course that'll just give you the type name because it doesn't know how to display a shoe. But that's no problem because wpf has a whole data templating system. That allows you to give it a template to use for a shoe.

You should be able to google examples of all that stuff but here's one on the tabcontrol specifically:

How do I bind a TabControl to a collection of ViewModels?

I hope that's enough to point you in the right direction. Or at least along a path with less rocks.

Andy
  • 11,864
  • 2
  • 17
  • 20