WPF Using TabItem and binding ViewModels

291 Views Asked by At

My goal is have each TabItem linked to a specific viewmodel. Furthermore, after transvering through each TabItem, the user input should not be reset. I am finding solutions for this and came across a potential solution but my testing failed me.

I have searched for answers and chose to do the following as it seems it applies the MVVM concept and it looks neat! However, I have a XAML binding error. I tried to replicate Jakob Christensen's answer provided in this link. I tried to debug and I think the issue is with the type of ObservableCollection that is created. It's an object type.

Thank you for helping!

Error enter image description here

This is the XAML code for my view

<TabControl>
    <TabItem DataContext="{Binding TabList[0]}" x:Name="Tab1" Header="Tab1" Margin="-2,-2,-2,2" >
            <Grid>
                <TextBox x:Name ="EnterNum1"  Margin="300,100,300,300" Text="{Binding test1, Mode =TwoWay}"/>
                <Button Name="RunBtn1" Command="{Binding Path=RunBtn1, Mode=TwoWay}" Content="RUN" HorizontalAlignment="Right" Width="180" Height="40" FontSize="18"/>
            </Grid>
        </TabItem>
    <TabItem DataContext="{Binding TabList[1]}" x:Name="Tab2" Header="Tab2" >
        <Grid>
            <TextBox x:Name ="EnterNum2"  Margin="300,100,300,300" Text="{Binding test2, Mode =TwoWay}" Grid.Column="1"/>
            <Button Name="RunBtn2" Command="{Binding Path=RunBtn2, Mode=TwoWay}" Content="RUN" HorizontalAlignment="Right" Width="180" Height="40" FontSize="18"/>
        </Grid>
    </TabItem>
</TabControl>

This is the XAML.cs for my view

public ObservableCollection<object> TabList { get; set; }
    public ImportData()
    {
        InitializeComponent();
        TabList = new ObservableCollection<object>();
        TabList.Add(new SampleViewModel1());
        TabList.Add(new SampleViewModel2());
    }
1

There are 1 best solutions below

0
On

You should first set DataContext to TabControl or Window, then you can bind the TabItem to the viewmodel(TabList) list item

XAML

 <TabControl>
        <TabItem DataContext="{Binding TabList[0]}" x:Name="Tab1" Header="Tab1" Margin="-2,-2,-2,2" >
            <Grid>
                <TextBox x:Name ="EnterNum1"  Margin="300,100,300,240" Text="{Binding Test1, Mode =TwoWay}"/>
                <Button Name="RunBtn1" Command="{Binding Path=RunBtn1, Mode=TwoWay}" Content="RUN" HorizontalAlignment="Right" Width="180" Height="40" FontSize="18"/>
            </Grid>
        </TabItem>
        <TabItem DataContext="{Binding TabList[1]}" x:Name="Tab2" Header="Tab2" >
            <Grid>
                <TextBox x:Name ="EnterNum2"  Margin="300,100,300,240" Text="{Binding Test2, Mode =TwoWay}" Grid.Column="1"/>
                <Button Name="RunBtn2" Command="{Binding Path=RunBtn2, Mode=TwoWay}" Content="RUN" HorizontalAlignment="Right" Width="180" Height="40" FontSize="18"/>
            </Grid>
        </TabItem>
    </TabControl>

XAML.cs

   public partial class MainWindow : Window
    {
        
        public MainWindow()
        {
            InitializeComponent();

            AllViewModel allViewModel = new AllViewModel();
            allViewModel.TabList = new ObservableCollection<object>();
            allViewModel.TabList.Add(new SampleViewModel1());
            allViewModel.TabList.Add(new SampleViewModel2());

            this.DataContext = allViewModel;
        }
    }

    public class AllViewModel : INotifyPropertyChanged
    {
        private ObservableCollection<object> tabList;

        public ObservableCollection<object> TabList 
        { 
            get => tabList;
            set { tabList = value;RaiseChange("TabList"); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

    public class SampleViewModel1 : INotifyPropertyChanged
    {
        private string test1 = "test1";
        private ICommand runBtn1;

        public string Test1
        {
            get => test1;
            set { test1 = value;RaiseChange("Test1"); }
        }

        public ICommand RunBtn1 
        { 
            get => runBtn1;
            set { runBtn1 = value;RaiseChange("RunBtn1"); }
        }

        public SampleViewModel1()
        {
            
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

    public class SampleViewModel2 : INotifyPropertyChanged
    {
        private string test2 = "test2";
        private ICommand runBtn2;

        public string Test2
        {
            get => test2;
            set { test2 = value; RaiseChange("Test2"); }
        }

        public ICommand RunBtn2
        {
            get => runBtn2;
            set { runBtn2 = value; RaiseChange("RunBtn2"); }
        }

        public SampleViewModel2()
        {

        }

        public event PropertyChangedEventHandler PropertyChanged;

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

run result

enter image description here