I am creating a Text Editor Type app. I can have multiple editors open via tabs. In my first try, I used simple TextBox
es to edit text. Everything worked ok. Then I created a UserControl
encapsulating the text box + buttons to perform text manipulation eg. bold/italic etc. I found out that when I open different tabs, they all contain the same content. eg. In Tab1 i enter "hello world" that will appear in all tabs. There is "no separation" even though they are in different tabs
<Window.Resources>
<DataTemplate DataType="{x:Type vm:EditorTabViewModel}">
<me:MarkdownEditor />
</DataTemplate>
</Window.Resources>
I then as a test, tried a textbox and the usercontrol together to see if I have the same problem.
<Window.Resources>
<DataTemplate DataType="{x:Type vm:EditorTabViewModel}">
<StackPanel>
<me:MarkdownEditor Text="{Binding Content}" Height="360" />
<TextBox Text="{Binding Content}" Height="360" />
</StackPanel>
</DataTemplate>
</Window.Resources>
I then discovered a few strange things. With a new document, where content should be nothing, my MarkdownEditor
had "System.Windows.Controls.Grid" in its text box as it a Grid was bound to the text. Text simple TextBox
works as expected. Also I still had the same problem with all UserControl
s in the app having the same content.
The XAML for the UserControl
<UserControl x:Class="MarkdownEditMVVM.Controls.MarkdownEditor.MarkdownEditor" ...>
<Grid>
<ToolBar Grid.Row="0">
<Button Command="{x:Static local:Commands.PreviewCommand}">
<Image Source="../../Images/16/zoom.png" />
</Button>
<!-- more buttons -->
</ToolBar>
<TextBox Grid.Row="1" x:Name="txtEditor" AcceptsReturn="True" Text="{Binding Path=Text, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</UserControl>
Update
I discovered that this has something to do with the way I have usercontrols rendered in the tabs where tabs are bound to ObservableCollection<TabViewModel>
Suppose I have a TabViewModel
even just a empty class
public class TabViewModel {}
Then in my Window
public partial class Window1 : Window
{
protected ObservableCollection<TabViewModel> _tabs;
protected ICollectionView _tabsCollectionView;
public Window1()
{
InitializeComponent();
this.DataContext = this;
_tabs = new ObservableCollection<TabViewModel>();
_tabs.Add(new TabViewModel());
_tabs.Add(new TabViewModel());
_tabsCollectionView = CollectionViewSource.GetDefaultView(_tabs);
}
public ICollectionView Tabs
{
get { return _tabsCollectionView; }
}
}
XAML
<TabControl ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I have created a simple Visual Studio Project hosted @mediafire illustrating this problem. I have a feeling it got something to do with the TabViewModel
s or the Tab Data Templates
Update 2
I further tested the user control problem by adding another tab control, this time without the TabViewModel
<TabControl Grid.Column="1">
<TabItem Header="Tab 1">
<TextBox />
</TabItem>
<TabItem Header="Tab 2">
<TextBox />
</TabItem>
</TabControl>
It all worked ok. update also posted to mediafire
Update 3
Made yet another finding. This time, I tested with a binding. things worked fine ...
<TabControl Grid.Column="2" ItemsSource="{Binding Tabs}" IsSynchronizedWithCurrentItem="True">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabTitle}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Update also posted @mediafire
Update 4
Ok now I did a more complete set of tests
- Row 0, Column 0:
TextBox
, Tabs bound toObservableCollection<TabViewModel>
. No Bindings forTextBox
. Problem observed - Row 0, Column 1: TextBox, Normal TabItems, Tabs not bound to
ObservableCollection<TabViewModel>
. No Binding forTextBox
. No problems - Row 0, Column 2: TextBox, Tabs bound to
ObservableCollection<TabViewModel>
. No Bindings forTextBox
. No Problems - Row 1, Column 0:
UserControl
, Tabs bound toObservableCollection<TabViewModel>
. No Bindings forUserControl
. Problem observed - Row 1, Column 2:
UserControl
, Tabs bound toObservableCollection<TabViewModel>
. Bindings forUserControl
. Problem observed. Text bindings not working
Update @mediafire
Big Edit in response to updates
I can get your mediafire example to work correctly by taking the following steps:
Text
from your user control - you don't need itChange your
ContentTemplate
on theTabControl
to the below. This causes theUserControl.DataContext
property to be set to the tab itemDataContext
Change your
UserControl
to the below. This binds theText
property to theUserControl.DataContext.Text
property.Remove the line
this.DataContext = this
from the constructor ofUserControl1
- this will obviously replace theDataContext
for the user control.This caused the tabs to be correctly bound with the expected values in the sample app you uploaded
Original Answer
You can have multiple instances of a
UserControl
- that isn't your problem here.The reason you are seeing the "System.Windows.Controls.Grid" text is because in your
UserControl
you are binding theText
property tothis.DataContext.Text
instead ofthis.Text
- the property of yourUserControl
.I think what you want to do is change the
TextBox
binding in your user control to:Note: this relies on the
me
namespace being set up to point to wherever yourMarkDownEditor
is locatedYou might be interested in the Writer sample application of the WPF Application Framework (WAF). It shows how to implement a text processing application with multiple tab support by applying the MVVM pattern.