62

I have a tabbed GUI with each tab containing a Frame. In one of these Frames there is a DataGrid. When the user selects this tab, I need my datagrid sorted, so I'm using the TabControl SelectionChanged event to trigger the sort. However, this event triggers every time an item is selected from the DataGrid, even though the tabs themselves remain untouched.

I've tried number of different events: GotFocus for a TabItem RequestBringIntoView for a TabItem

but they all seem to suffer from this problem. What is causing this?

Anders
  • 1,401
  • 3
  • 16
  • 20

4 Answers4

105

The TabControl.SelectionChanged is the same event as a ComboBox.SelectionChanged

It originates from Selector.SelectionChanged.

So, if you do not mark your event as handled in your event handler, it will bubble up the tree, and eventually arrive at your TabControl, which is causing this "firing too often" issue.

Mark your event as handled in your SelectionChanged of your ComboBox/ListBox/ListView/any other Selector you use in your DataGrid like so:

private void MyComboBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    e.Handled = true;
}

And this inconvenience will go away ;).

Arcturus
  • 26,677
  • 10
  • 92
  • 107
  • 17
    He would then have to handle every selector on every one of his tab pages. It's much easier to just look and see if e.OriginalSource is a Tab Control. – Bryan Anderson Sep 07 '10 at 15:17
  • 1
    True, but it would be cleaner though, if he just marked his event as Handled when the selector is done with it. :) – Arcturus Sep 07 '10 at 15:19
  • 15
    Thanks so much for pointing out the problem. As for the solution, I ended up adding "if (e.OriginalSource is System.Windows.Controls.TabControl)" to my TabControl.SelectionChanged event so I wouldn't have to create an event handler for my Datagrid. – Anders Sep 07 '10 at 15:24
  • 13
    FYI if you're encountering this: don't just check OriginalSource's type - check to make sure OriginalSource actually refers to your particular TabControl: "if (ReferenceEquals(e.OriginalSource, this.myTabControl)". If you don't, then all child tab controls will activate your event handler code. – HiredMind Aug 06 '13 at 15:12
  • I've tried the solution from this answer, but the TabControl event is firing before its childs controls event. – Marlon Nov 13 '13 at 21:03
  • 1
    Are you sure you're not using the Preview event? – Arcturus Nov 14 '13 at 06:58
  • Thanks! This problem had me scratching my head ;) – Freek Sanders Feb 06 '18 at 14:35
  • A more reliable option is to name the component and then check `e.OriginalSource.Name`. That way, it will work even if you nest TabControls. – Michael Scheper Oct 23 '19 at 20:51
  • In my case it doesn't help for some reason, the callstack listed some internals which I can't find where a call to them originates from. Also, a duplicate SelectionChanged firing only happens every other time. – Sasha Jun 02 '23 at 05:17
29
     private void tabControlName_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.Source is TabControl) //if this event fired from TabControl then enter
            {
                if (tabItemName.IsSelected)
                {
                    //Do your job here
                }
            }
        }
Towhid
  • 1,920
  • 3
  • 36
  • 57
4

If you have added a handler with AddHandler in a parent element, all selection changes will fire the SelectionChanged-event. In this case, you can give your TabControl a name and then check in the EventHandler if the name of the OriginalSource is the name of your TabControl.

HCL
  • 36,053
  • 27
  • 163
  • 213
  • 1
    In the OnSelectedChanged handler, ``if (Equals(sender, e.OriginalSource)) { /* do the work */ }`` then all child events will not enter the conditional block – Supawat Pusavanno Nov 14 '17 at 07:28
2

Another good approch is adding a handler to the tabControl.Items.SelectionChanged:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
  ItemCollection view = tabControl.Items;
  view.CurrentChanged += new EventHandler(view_CurrentChanged);
}

void view_CurrentChanged(object sender, EventArgs e)
{
  throw new NotImplementedException();
}

Maybe is not the xamly way, but is less pain as it only fires when an item is changed.

Shimmy Weitzhandler
  • 101,809
  • 122
  • 424
  • 632