Is there a workaround for this tabcontrol/tabitem bug
Unless I'm mistaken (which I hope I am) there is a bug with the TabControl
related to the visibility of TabItems
.
Here's XAML that reproduces the bug.
<UserControl x:Class="TabControl_bug.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsof开发者_高级运维t.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<sdk:TabControl>
<sdk:TabItem Header="tabItem1" Visibility="Collapsed">
<TextBlock Text="TabItem1 which should not be visible" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</sdk:TabItem>
<sdk:TabItem Header="tabItem2">
<TextBlock Text="TabItem2 which should be visible" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</sdk:TabItem>
</sdk:TabControl>
</Grid>
</UserControl>
When you run this code, you'll see that TabItem2 is not "selected" and so the content that is displayed is
TabItem 1 which should not be visible
Once you select the tab, then of course the TabItem2 content is displayed, and there's no way of getting back to tabItem1, but the issue is in the inital display.
If I set the SelectedIndex
property to 1 the correct content is displayed. Howerver I don't know in the XAML which the of the Tabs should be selected first.
What workarounds are possible for this issue. Ideally the tabcontrol have preselected it's first visable tabitem.
I've found next solution.For your sample in MainPage constructor:
tabControl.SetValue(TabControl.SelectedContentProperty, null);
Also you can do that on Loaded event.
Unfortunately, TabControl.SelectedContent property doesn't have public setter so you can set SelectedContentProperty directly.
EDIT:
Behavior for this functionality:
public class UnselectContentBehavior : Behavior<TabControl>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
AssociatedObject.SetValue(TabControl.SelectedContentProperty, null);
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= OnLoaded;
}
}
Using sample:
<sdk:TabControl x:Name="tabControl">
<sdk:TabItem Header="tabItem1" Visibility="Collapsed">
<TextBlock Text="TabItem1 which should not be visible" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</sdk:TabItem>
<sdk:TabItem Header="tabItem2">
<TextBlock Text="TabItem2 which should be visible" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</sdk:TabItem>
<i:Interaction.Behaviors>
<b:UnselectContentBehavior/>
</i:Interaction.Behaviors>
</sdk:TabControl>
I came across this question when trying to solve the problem described here. I found a reasonable solution which covers both cases. An attached property "SelectOnlyVisibleTabs" is set to true in the TabControl to correct its behavior. To change the visibility of the TabItems during runtime, a attached "Visibility" property also prevents the TabControl's misbehavior. The complete solution:
public static class TabControlExtensions
{
/// <summary>
/// Use this property on a TabControl to correct the behavior
/// of selecting Collapsed TabItems.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static bool GetSelectOnlyVisibleTabs(DependencyObject obj)
{
return (bool)obj.GetValue(SelectOnlyVisibleTabsProperty);
}
public static void SetSelectOnlyVisibleTabs(DependencyObject obj, bool value)
{
obj.SetValue(SelectOnlyVisibleTabsProperty, value);
}
public static readonly DependencyProperty SelectOnlyVisibleTabsProperty =
DependencyProperty.RegisterAttached("SelectOnlyVisibleTabs", typeof(bool), typeof(TabControlExtensions), new PropertyMetadata(false, SelectOnlyVisibleTabsChanged));
public static void SelectOnlyVisibleTabsChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var tabControl = sender as TabControl;
if (tabControl == null) return;
if ((bool)args.NewValue)
{
tabControl.SelectionChanged += TabControl_SelectionChanged;
CorrectSelection(tabControl);
}
else
{
tabControl.SelectionChanged -= TabControl_SelectionChanged;
}
}
private static void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
var tabControl = sender as TabControl;
if (tabControl == null) return;
CorrectSelection(tabControl);
}
public static void CorrectSelection(TabControl tabControl)
{
var selected = tabControl.SelectedItem as UIElement;
if (selected == null) return;
// If the selected element is not suposed to be visible,
// selects the next visible element
if (selected.Visibility == System.Windows.Visibility.Collapsed)
tabControl.SelectedItem = tabControl.Items.OfType<UIElement>()
.Where(e => e.Visibility == System.Windows.Visibility.Visible)
.FirstOrDefault();
}
}
public static class TabItemExtensions
{
/// <summary>
/// Use this property in a TabItem instead of the original "Visibility" to
/// correct the behavior of a TabControl when a TabItem's Visibility changes.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static Visibility GetVisibility(DependencyObject obj)
{
return (Visibility)obj.GetValue(VisibilityProperty);
}
public static void SetVisibility(DependencyObject obj, Visibility value)
{
obj.SetValue(VisibilityProperty, value);
}
public static readonly DependencyProperty VisibilityProperty =
DependencyProperty.RegisterAttached("Visibility", typeof(Visibility), typeof(TabItemExtensions), new PropertyMetadata(Visibility.Visible, VisibilityChanged));
public static void VisibilityChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var tabItem = sender as TabItem;
if (tabItem == null) return;
var visibility = (Visibility)args.NewValue;
if (tabItem.Visibility == visibility) return;
tabItem.Visibility = visibility;
if (visibility == Visibility.Visible) return;
// Finds the tab's parent tabcontrol and corrects the selected item,
// if necessary.
var tabControl = tabItem.Ancestors().OfType<TabControl>().FirstOrDefault();
if (tabControl == null) return;
TabControlExtensions.CorrectSelection(tabControl);
}
}
The usage:
<sdk:TabControl local:TabControlExtensions.SelectOnlyVisibleTabs="True">
<sdk:TabItem Header="tabItem1" Visibility="Collapsed">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="TabItem1 which should not be visible (1)" />
</sdk:TabItem>
<sdk:TabItem Header="tabItem2">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="TabItem2 which should be visible (2)" />
</sdk:TabItem>
<sdk:TabItem DataContext="{Binding ViewModel}"
Header="tabItem3"
local:TabItemExtensions.Visibility="{Binding MyProperty,
Converter={StaticResource BoolToVisibilityConverter}}">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="TabItem with binded Visibility (3)" />
</sdk:TabItem>
</sdk:TabControl>
精彩评论