WTF WPF TabControl?
-
04-10-2019 - |
Question
Je crois que ce bogue dans WPF (v4.0 si elle importe), mais il est en retard, et peut-être que je me manque quelque chose.
Je suis liaison à un exemple de faux à titre d'exemple:
<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>
Cela fonctionne et affiche trois onglets avec le même en-tête et le contenu:
<TabControl ItemsSource="{StaticResource SampleItems}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding}" />
<Setter Property="Content" Value="{Binding}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
Cependant, cela jette une exception avec le message « Erreur 10 élément spécifié est déjà l'enfant logique d'un autre élément Déconnecter le premier. ».
<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>
Il est important de noter que c'est reproductible avec tout texte dans les deux TextBlock. En fait, je peux remplacer l'en-tête TextBlock avec tout XAML et ce message. Je suis à une perte pour expliquer. Toutes les idées, ou est-ce juste un bug?
Le problème apparaît dans le concepteur VS, mais voici une partie de la trace de la pile correspondant à l'exécution ainsi:
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()
La solution
En fait ce que vous faites est d'attribuer la même instance d'un TextBlock à chaque TabItem. A la première itération, le TextBlock est ajouté à la première TabItem. Sur la deuxième itération, le même TextBlock est ajouté à l'arbre visuel. Le message d'erreur que vous voyez essaie de vous dire que le TextBlock ne peut pas avoir les deux parents (il y a une blague il y a quelque part).
peut définir un modèle pour ceux-ci, cependant. Un modèle indique au TabItem de créer un nouvel ensemble de ce que vous voulez visuel par article créé.
<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>