53

I would like to know how to select a specific TabItem in a WPF TabControl.

I tried these bellow but nothing work!

MyTabControl.SelectedIndex = x

MyTabControl.SelectedItem = MyTabItem

MyTabControl.SelectedValue = MyTabItem

MyTabItem.IsSelected = True
Cœur
  • 37,241
  • 25
  • 195
  • 267
Pierre Toutant
  • 531
  • 1
  • 4
  • 3

9 Answers9

61

As @Chris says, any of the first three things should work and as @Phyxx says, it doesn't always really work. The problem is some subtle thing about the order of property changes. To work around it you need to let the WPF invoke your tab-selection code in its own time:

Dispatcher.BeginInvoke((Action)(() => MyTabControl.SelectedIndex = x));

This does just what Phyxx' timer does, but in a slightly less extreme way.

Adrian Ratnapala
  • 5,485
  • 2
  • 29
  • 39
  • 1
    But **why** is it so? The suggestion worked great (+1) but I can't stop fearing that doing it that way only hides an issue that's buried behind. When deciding between me doing something blatantly wrong and Microsoft's developer doing something blatantly wrong, I'd go with the safest horse - me. – Konrad Viltersten Jan 20 '15 at 22:52
  • 4
    My guess: the MS devels are wrong, but not "blatantly". WPF is a complicated and fuzzy API, and there seems to be no "one right way" to use it. As a result, you will certainly create a situation that MS didn't test for expect, or protect against. In this case we are somehow setting a field either too late (so it gets ignored) or too early (so it gets overwritten). Nobody, not even Microsoft, knows what the right time is. – Adrian Ratnapala Jan 21 '15 at 10:14
  • 2
    If it calms your nerves - it is perfectly normal to call the dispatcher for some WPF UI actions, it's actually the best way of making sure it happens at the right time, in the right environment. This is especially true for multi-threaded applications, or when you use Tasks. Adrian's answer should be marked as "the answer" - it is the most correct one. – Digifaktur Jan 26 '15 at 11:08
  • 2
    @Digifaktur, that almost calms my nerves too. I was too harsh on Microsoft; GUI frameworks are hard because everything potentially gets notified about everything else, in who-knows-what order. I am fine with `Dispatcher.*Invoke` for shunting code between threads, but I get nervous when I have to blindly use it within a single thread just because I don't understand what's happening. – Adrian Ratnapala Jan 26 '15 at 15:53
  • Good points/discussion all around, +1. I don't like the idea of `Dispatcher`, either, which is like threading to a different instruction path when it should just be able to take the same instruction in the mainline. But it's possible, like Adrian said - it gets called too soon, and later commands overwrite it - it gets called too late, and the receiver doesn't know what to do. Sounds like a poor design on MS' part to have that kind of disconnect, but guess it's what we're stuck with. – vapcguy Sep 22 '16 at 18:39
  • This discussion has a parallel for graphics programming in the browser: Sometimes, calling `setTimeout` with a delay of `0` is just the best option for what you want to accomplish, even though JavaScript is single-threaded. When you understand how JavaScript's event scheduler works, it actually all makes sense. I'd bet somebody at MS does actually know why this is needed. – jpaugh Aug 13 '19 at 19:14
  • This whole discusion though its kinda old opened my eyes (+1). I feel like the wpf UI is way smarter than I thought. Good news is I dont have to understand it in detail as long as I dont try to micro manage it like a control freak. I simply dispatch my orders now and let it do its thing when and how it seems fiting. – Dom Jan 05 '23 at 09:42
32

All your examples except the third one are correct and will work. The problem must be at another location. Maybe you reset the item after setting or your code never is called?

Valid

MyTabControl.SelectedIndex = x   
MyTabControl.SelectedItem = MyTabItem    
MyTabItem.IsSelected = True 

Invalid

MyTabControl.SelectedValue = MyTabItem 
sampathsris
  • 21,564
  • 12
  • 71
  • 98
HCL
  • 36,053
  • 27
  • 163
  • 213
17

Loop through the TabItems and for the tab to be selected, set

tabItem.IsSelected = true

If there are any other place due to binding changing you will see problem. Otherwise, the above code should work.

John Watts
  • 8,717
  • 1
  • 31
  • 35
kishhr
  • 191
  • 1
  • 2
15

One thing which hasn't been mentioned above:

The main reason something like this won't work is that the tab items do not have the "Name" property set. Each tab item of the tab control which you want to navigate to programmatically must have its name property set for any of the above code to work.

<tabItem Name="tab1"></tabItem>
user2976441
  • 151
  • 1
  • 2
  • This worked for me. On dynamically created tabs none of the methods the should work did. I added names to all of them and they started working as expected. – Craig Neil Brown Mar 25 '15 at 22:59
  • Yes, this worked for me. Very easy. Just one line: tab1.IsSelected = true; – Miyamoto Musashi Mar 15 '22 at 02:19
  • How can you set TabItem's Name property from within TabControl.ItemTemplate or TabControl.Content template. I was unable to find a way to do so. – Alex Mar 21 '22 at 08:42
8

I have implemented a small MVVM bindings based solution for selecting tab panels pragmatically.

  1. define a property in your view model - Selected int type

  2. bind the property in your view

    <TabControl
        x:Name="TabsCandidate" 
        VerticalAlignment="Stretch" 
        TabStripPlacement="Top"
        SelectedIndex="{Binding Selected}"
    
  3. private int _selected;

    public int Selected
    {
        get { return _selected; }
        set
        {
            _selected = value;
            OnPropertyChanged("Selected");
        }
    }
    
  4. Set the value to Select property, simply the binding will activate the tab panel.

    if you want to navigate from tab panel inside parent tab panels, this solution will simply works, All you need to do is, access the data context of your control and set it

    // set the property value of the view model which points the index of the tab controller.
    ((CandidateViewModel)((System.Windows.FrameworkElement)candidateTab.Content).DataContext).Selected = CandidateLogTabIndex;
    
ˈvɔlə
  • 9,204
  • 10
  • 63
  • 89
Shanjee
  • 491
  • 5
  • 16
1

I tried all the methods that should have worked, but like you nothing actually changed the selected tab. In the end I got it to work by putting the tab selection code in a DispatcherTimer tick.

       DispatcherTimer switchTabTimer = new DispatcherTimer();
       switchTabTimer.Interval = new TimeSpan(0);
       switchTabTimer.Tick += (object timerSender, EventArgs timerE) =>
       {
           myTabControl.SelectedIndex = 0;
           switchTabTimer.Stop();
       };
       switchTabTimer.Start(); 
nabulke
  • 11,025
  • 13
  • 65
  • 114
Phyxx
  • 15,730
  • 13
  • 73
  • 112
1

Try to set the MyTabControl.SelectedIndex = x in the event handler of DataContextChanged or Loaded of your UI. Hope this will work.

Debasis
  • 408
  • 1
  • 3
  • 12
0

if you don't know the index of the tab (hint its not TabIndex) use:

    private async Task ChangeTabTo(TabItem wantedTab) {
        int index = 0;
        for (var i = 0; i < TabControl.Items.Count; i++) {
            var tab = TabControl.Items[i];
            var t = tab as TabItem;
            if (t == null) continue;
            if (t == wantedTab) {
                index = i;
                break;
            }
        }

        await Dispatcher.BeginInvoke((Action)(() => TabControl.SelectedIndex = index));
    }

or modify it to search by name if you don't want to keep a reference to the tab

GreyCloud
  • 3,030
  • 5
  • 32
  • 47
0

I'm throwing my 2 cents on the topic, since it might help someone out. I'm using WPF with Prims framework.

I was unable to select a tab by binding to SelectedItem or SelectedIndex - it didn't work. I was also unable to set TabItem.Name value from within TabControl.ItemTemplate or TabControl.ContentTemplate.

Instead I implemented event-based solution:

  1. Add Name value for my TabControl.
  2. Create an event - in Prism that means define a class that derives from PubSubEvent<T> (T is the type of parameter - in my case that was the ViewModel object bound to the TabItem>.
  3. Publish that event whenever I want to a tab to be selected.
  4. Subscribe to the event within my View.cs class and set the TabControl.SelectedItem programmatically using FindName.
Alex
  • 715
  • 1
  • 8
  • 29