I have a WPF .NET Core 5.0 project which has a TabControl
that I can add a new TabItem
by clicking on a Button
represented as [+].
MainWindow.cs:
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApp1
{
public partial class MainWindow : Window
{
int TabIndex = 1;
ObservableCollection<TabVM> Tabs =
new ObservableCollection<TabVM>();
public MainWindow()
{
InitializeComponent();
var tab1 = new TabVM()
{
Header = $"Tab {TabIndex}"
};
Tabs.Add(tab1);
AddNewPlusButton();
MyTabControl.ItemsSource = Tabs;
MyTabControl.SelectionChanged +=
MyTabControl_SelectionChanged;
}
private void MyTabControl_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
var pos = MyTabControl.SelectedIndex;
if (pos != 0 && pos == Tabs.Count - 1) //last tab
{
var tab = Tabs.Last();
ConvertPlusToNewTab(tab);
AddNewPlusButton();
}
}
}
void ConvertPlusToNewTab(TabVM tab)
{
//Do things to make it a new tab.
TabIndex++;
tab.Header = $"Tab {TabIndex}";
tab.IsPlaceholder = false;
}
void AddNewPlusButton()
{
var plusTab = new TabVM()
{
Header = "+",
IsPlaceholder = true
};
Tabs.Add(plusTab);
}
class TabVM : INotifyPropertyChanged
{
string _Header;
public string Header
{
get => _Header;
set
{
_Header = value;
OnPropertyChanged();
}
}
bool _IsPlaceholder = false;
public bool IsPlaceholder
{
get => _IsPlaceholder;
set
{
_IsPlaceholder = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string property = "")
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(property));
}
}
private void OnTabCloseClick(object sender, RoutedEventArgs e)
{
var tab = (sender as Button).DataContext as TabVM;
if (Tabs.Count > 2)
{
var index = Tabs.IndexOf(tab);
if (index == Tabs.Count - 2)//last tab before [+]
{
MyTabControl.SelectedIndex--;
}
Tabs.RemoveAt(index);
}
}
}
}
XAML:
<TabControl x:Name="MyTabControl">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header, Mode=OneWay}"/>
<Button Click="OnTabCloseClick"
Width="20"
Padding="0"
Margin="8 0 0 0"
Content="X">
<Button.Style>
<Style TargetType="Button"
x:Key="CloseButtonStyle">
<Setter Property="Visibility"
Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsPlaceholder}"
Value="True">
<Setter Property="Visibility"
Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Resources>
<ContentControl x:Key="TabContentTemplate">
<Grid>
<Label Content="Enter your text here:"
HorizontalAlignment="Left"
Margin="30,101,0,0"
VerticalAlignment="Top"
Width="298"
FontSize="18"/>
<RichTextBox HorizontalAlignment="Left"
Height="191"
Margin="8,135,0,0"
VerticalAlignment="Top"
Width="330">
<FlowDocument/>
</RichTextBox>
</Grid>
</ContentControl>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPlaceholder}"
Value="True">
<Setter Property="Content"
Value="{x:Null}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsPlaceholder}"
Value="False">
<Setter Property="Content"
Value="{StaticResource TabContentTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
In the XAML
I set a Grid
in the ContentControl
TabContentTemplate
, but any control of the Grid
like that RichTextBox
if I change its text so it reflects to the RichTextBox
of all tabs. How to add a Grid
there without reflecting its control values to the other tabs?
The gif below shows the problem, whatever I type in the RichTextBox
reflects in the other tabs.