Noob001
Noob001

Reputation: 103

How to bind a TabItem Header to a TextBox in FrameworkElementFactory?

I tried to create a TabItem with a TextBox in it from this article.

I found out that the TabItem Header is NOT bind to the TextBox.

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    TabItem ti = new TabItem();
    DataTemplate tabItemTemplate = new DataTemplate();

    tabItemTemplate.DataType = typeof(TabItem);
    FrameworkElementFactory textBoxFactory = new FrameworkElementFactory(typeof(TextBox));
    textBoxFactory.SetBinding(TextBox.TextProperty, new Binding("."));
    textBoxFactory.SetValue(NameProperty, "textBox");
    textBoxFactory.SetValue(BorderThicknessProperty, new Thickness(0));
    //textBoxFactory.SetValue(IsEnabledProperty, false);
    tabItemTemplate.VisualTree = textBoxFactory;
    ti.Header = "Test!";
    ti.HeaderTemplate = tabItemTemplate;
    ti.MouseDoubleClick += TabItem_MouseDoubleClick;
    tabControl.Items.Add(ti);
}

private void TabItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    MessageBox.Show((sender as TabItem).Header.ToString());
}

enter image description here enter image description here

Could somebody please be so kind and teach me how to bind it correctly?

Much appreciated!

Upvotes: 0

Views: 149

Answers (2)

EldHasp
EldHasp

Reputation: 7918

Complementing @BionicCode's answer

The problem with your original binding is that you are pointing to the current Data Context.
Your binding is equivalent to an empty Binding "new Binding ();".
Thus, you get a binding not to a property, but to a source (The default source is the current Data Context).
But the binding can only change the property of the source, not the source itself.
The Header template is applied to the contents of the TabItem's Header property.
Therefore, to get the same value, but not as a source of the binding, but as a property in the Path of the binding, you need to go up to the TabItem level.
@BionicCode in his answer showed you examples of such bindings.

I will offer another option for integration into your code:

        private static readonly DataTemplate headerTemplate = (DataTemplate)XamlReader.Parse
            (@"
<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
              xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
    <TextBox x:Name='textBox'
             Text='{Binding Header,
                            RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabItem}},
                            UpdateSourceTrigger=PropertyChanged}'
             BorderThickness='0'/>
</DataTemplate>
            ");

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            TabItem ti = new TabItem();
            ti.Header = "Test!";
            ti.HeaderTemplate = headerTemplate;
            ti.MouseDoubleClick += TabItem_MouseDoubleClick;
            tabControl.Items.Add(ti);
        }

Upvotes: 1

BionicCode
BionicCode

Reputation: 28988

Your Binding configuration is wrong.

new Binding(".") is missing the Binding.Source.
It should be:

var binding = new Binding("Header") { Source = ti };

Example of using XAML

The following example replicates your C# code

<TabControl>
  <TabItem Header="Test" MouseDoubleClick="TabItem_MouseDoubleClick" >
    <TabItem.HeaderTemplate>
      <DataTemplate>
        <TextBox x:Name="textBox" 
                 Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem}, Path=Header}" />
       </DataTemplate>
     </TabItem.HeaderTemplate>
   </TabItem>
 </TabControl>

Upvotes: 1

Related Questions