Reputation: 162
I notice that the TabControl
that has its ItemsSource
property bound to an ObservableCollection
does not select the first item in the collection if you do the following:
TabControl
when the ObservableCollection
has a count of 0 (tab items are not showing)ObservableCollection
, observe that one TabItem
is now present but not selectedIf you add an item to the ObservableCollection
before the TabControl
comes into focus for the first time, the one item in the TabControl
has its TabItem
selected. It only seems that when the tab count hits 0 then increases again does it seem to lose its ability to select the first item.
It seems this is by design. Is there any way to get around this issue?
I have the following tabcontrol definition in XAML as follows:
<TabControl ItemsSource="{Binding Sels}" />
Upvotes: 0
Views: 634
Reputation: 27
In addition to Corentin's answer I just want to add that, depending on the way you fill that ObservableCollection
, you might also just assign the first item to a designated property in the viewmodel.
In my case which is pretty similar to yours I process exported plugins collected from a MEF container in code. When that is finished I just update a property SelectedPlugin
in the viewmodel and in XAML I then do a TwoWay-binding on SelectedItem
of the TabControl
to this property.
This works perfectly and serves exactly your use-case.
Upvotes: 0
Reputation: 4943
If you give the name tabControl
to your TabControl
, then you could subscribe to the changes in its items collection in code-behind:
<TabControl ItemsSource="{Binding Sels}" x:Name="tabControl" />
tabControl.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
and then you can force it to select the first tab if none are selected:
private async void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e) {
await Task.Yield();
if (tabControl.SelectedIndex == -1) {
if (tabControl.Items.Count > 0) {
tabControl.SelectedIndex = 0;
}
}
}
The call to await Task.Yield()
is needed to let WPF finish handling internal TabControl
updates before changing its SelectedIndex
property.
To be extra fancy,
you could put this in an attached DependencyProperty
and in fine have a cleaner XAML and no extra code-behind:
<TabControl ItemsSource="{Binding Sels}" local:AutoSelect.AutoSelectFirstTab="true"/>
Here is the same behaviour but wrapped using WPF attached DependencyProperty
:
public static class AutoSelect {
public static readonly DependencyProperty AutoSelectFirstTabProperty = DependencyProperty.RegisterAttached(
"AutoSelectFirstTab",
typeof(bool),
typeof(AutoSelect),
new PropertyMetadata(false, AutoSelectFirstTabChanged));
public static bool GetAutoSelectFirstTab(TabControl obj) => (bool)obj.GetValue(AutoSelectFirstTabProperty);
public static void SetAutoSelectFirstTab(TabControl obj, bool value) => obj.SetValue(AutoSelectFirstTabProperty, value);
private static void AutoSelectFirstTabChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var tabControl = (TabControl)d;
tabControl.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
async void ItemContainerGenerator_ItemsChanged(object _1, ItemsChangedEventArgs _2) {
await Task.Yield();
if (GetAutoSelectFirstTab(tabControl)) {
if (tabControl.SelectedIndex == -1) {
if (tabControl.Items.Count > 0) {
tabControl.SelectedIndex = 0;
}
}
}
}
}
}
Upvotes: 1