Search code examples
c#wpfdatatemplate

Add tabs to tab control from same DataTemplate but they shouldn't be connected to each other


In my wpf program i would like to have tabs that would be generated from array or list. I would like to edit files with each tab. Each tab would have corresponding folder with same name, so all tabs should look same (this is why I used DataTemplates), since files in all directories are all generated with same names but their content is different. I have code that generates tabs from array and adds names to tabs.

public class MainWindowViewModel
    {
        public ObservableCollection<TabViewModel> Tabs { get; set; }

        public MainWindowViewModel()
        {
            this.Tabs = new ObservableCollection<TabViewModel>();

            var location = System.AppDomain.CurrentDomain.BaseDirectory.ToString();

            string[] folderList = new string[] { "Folder1", "Folder2" };

            foreach (string folder in folderList)
            {
                this.Tabs.Add(new TabViewModel(folder));

                string newLocation = location + folder + "\\";//i would like to point tab to this directory

            }
        }
    }

    public class TabViewModel
    {
        public string Name { get; set; }

        public TabViewModel(string name)
        {
            this.Name = name;
        }
    }
<TabControl ItemsSource="{Binding Tabs}">
    <TabControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name}" />
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate >
            <TextBox x:Name="fileTextBox"/>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

But I have 2 problems:

  1. I put textbox in content of a tab and if I create more than 1 tab they all share same content in textbox. I would need to make separate instance for each tab.
  2. When tab is created I would need to assign directory(which i stored in newLocation string) to it and then display file (e.g. sample.txt) in textbox (fileTextBox).

Solution

    1. you're experiencing this behavior because, when binding by ItemsSource, all TabControl items are 'optimized' by sharing a panel to render the content (see this question)

    2. I suggest you use the INotifyPropertyChanged interface in your view model and add the file data in the TabViewModel (I added the Location property but you can also add a Content property)

      public class MainWindowViewModel
      {
      public ObservableCollection<TabViewModel> Tabs { get; set; }
      
      public MainWindowViewModel()
      {
          this.Tabs = new ObservableCollection<TabViewModel>();
      
          var location = System.AppDomain.CurrentDomain.BaseDirectory.ToString();
      
          string[] folderList = new string[] { "Folder1", "Folder2" };
      
          foreach (string folder in folderList)
          {
              string newLocation = location + folder + "\\";//i would like to point tab to this directory
      
              this.Tabs.Add(new TabViewModel(folder, newLocation));
      
          }
      }
      
      public class TabViewModel : INotifyPropertyChanged
      {
      private string _name;
      public string Name
      {
          get { return _name; }
          set
          {
              if (_name == value)
                return;
              _name = value;
              OnPropertyChanged();
          }
      }
      
      private string _location;
      public string Location
      {
          get { return _location; }
          set
          {
              if (_location == value)
                return;
              _location = value;
              OnPropertyChanged();
          }
      }
      
      public TabViewModel(string name, string location)
      {
          this.Name = name;
          this.Location = location;
      }
      
      public event PropertyChangedEventHandler PropertyChanged;
      
      protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
      {
          PropertyChangedEventHandler handler = PropertyChanged;
          if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
      }
      }
      

    XAML: notice the use of UpdateSourceTrigger to update the viewModel when the textbox content changes

    <TabControl  ItemsSource="{Binding Tabs}">
    <TabControl.ItemTemplate>
      <DataTemplate>
        <TextBlock Text="{Binding Name}" />
      </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
      <DataTemplate >
          <TextBox x:Name="fileTextBox" Text="{Binding Location, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
      </DataTemplate>
    </TabControl.ContentTemplate>