Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WTF WPF TabControl?

I believe this to be a bug in WPF (v4.0 if it matters), but it is late, and maybe I'm missing something.

I am binding to a fake example for illustrative purposes:

    <x:Array x:Key="SampleItems" Type="sys:String">
        <sys:String>Foo</sys:String>
        <sys:String>Bar</sys:String>
        <sys:String>Baz</sys:String>
    </x:Array>

This works and displays three tabs with the same header and content:

<TabControl ItemsSource="{StaticResource SampleItems}">
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="Header" Value="{Binding}" />
                    <Setter Property="Content" Value="{Binding}" />
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>

However, this throws an exception with the message "Error 10 Specified element is already the logical child of another element. Disconnect it first.":

<TabControl ItemsSource="{StaticResource SampleItems}">
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="Header">
                <Setter.Value>
                    <!-- Anything here causes this problem. -->
                    <TextBlock Text="{Binding}"/>
                </Setter.Value>
            </Setter>
            <Setter Property="Content" Value="{Binding}" />
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

It is important to note that this is reproducible with any text in either TextBlock. In fact, I can replace the header TextBlock with any XAML and get this message. I'm at a loss to explain this. Any ideas, or is this just a bug?

The problem appears in the VS designer, but here is a portion of the relevant stack trace at runtime as well:

   at System.Windows.FrameworkElement.ChangeLogicalParent(DependencyObject newParent)
   at System.Windows.FrameworkElement.AddLogicalChild(Object child)
   at System.Windows.Controls.HeaderedContentControl.OnHeaderChanged(Object oldHeader, Object newHeader)
   at System.Windows.Controls.HeaderedContentControl.OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.StyleHelper.ApplyStyleOrTemplateValue(FrameworkObject fo, DependencyProperty dp)
   at System.Windows.StyleHelper.InvalidateContainerDependents(DependencyObject container, FrugalStructList`1& exclusionContainerDependents, FrugalStructList`1& oldContainerDependents, FrugalStructList`1& newContainerDependents)
   at System.Windows.StyleHelper.DoStyleInvalidations(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle)
   at System.Windows.StyleHelper.UpdateStyleCache(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, Style& styleCache)
   at System.Windows.FrameworkElement.OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
   at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
   at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
   at System.Windows.Controls.ItemsControl.ApplyItemContainerStyle(DependencyObject container, Object item)
   at System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.PrepareItemContainer(DependencyObject container, Object item)
   at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IItemContainerGenerator.PrepareItemContainer(DependencyObject container)
   at System.Windows.Controls.Panel.GenerateChildren()
like image 264
Jerry Bullard Avatar asked Mar 18 '26 21:03

Jerry Bullard


1 Answers

Basically what you are doing is assigning the very same instance of a TextBlock to each TabItem. On the first iteration, the TextBlock is added to the first TabItem. On the second iteration, the very same TextBlock is added to the visual tree. The error message you see is trying to tell you that the TextBlock can't have two parents (there is a joke there somewhere).

You can set a template for these, however. A template instructs the TabItem to create a new set of whatever visual you want per item created.

<TabControl ItemsSource="{StaticResource SampleItems}">
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="HeaderTemplate">
                <Setter.Value>
                    <DataTemplate>
                         <TextBlock Text="{Binding}"/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Content" Value="{Binding}" />
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>
like image 65
Anderson Imes Avatar answered Mar 20 '26 19:03

Anderson Imes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!