Mobile Development at U2U Consult

Based in Brussels, Belgium, U2U Consult has been offering consulting & development services in the EMEA region for over 10 years. We offer mobile development on all platforms from Apple to Android & Microsoft.

Windows Presentation Foundation's Routed Events can lead to unexpected or at least nonintuitive behavior when using TabControls that contain ListViews and/or ComboBoxes. A routed event generally bubbles from the control that raised it, up the whole element tree until the root. On its way up it invokes handlers on multiple listeners. This makes a lot of sense in the ButtonBase.Click Event: if a button is clicked, then its containing element is also clicked. By design, the Selector.SelectionChanged Event is such a routed event. TabItem, ListBox, and ComboBox all inherit from Selector, so if you put them in a hierarchy they will register on each other's events. A ComboBox that appears via a template in a ListBox will raise the SelectionChanged event of that ListBox - even if the user didn't select a new ListBoxItem. If you put that ListBox in a TabControl, then the SelectionChanged on that TabControl will also be fired - even if the user didn't select a new TabItem. Enough talking: let's build a small demo. First we build a Window with a TabControl that has a ComboBox in its first TabItem:

Switching from one tab to another behaves nicely. Unfortunately, clicking in the ListBox now also triggers the SelectionChanged event from the TabControl itself, resulting in unexpected behavior:

Before diving into solutions, let's make it worse by implementing a very popular pattern. When the user navigates to a new TabItem, we populate a ListBox, and programatically select its first item. In theory there's nothing wrong with this, in practice it creates an infinite loop (well, it's not really infinite: it stops when you're out of stack space):

We can solve this by letting the child controls prevent the event from propagating -via the Handled property- like this:

Of course, not all child controls will have an event handler or even need one (e.g. a ComboBox bound to a property of a Business Entity). Moreover, the parent control can not depend on the implementation of his children. Fortunately the parent control can decide to ignore all bubbled events from child controls like this:

In practice this means that you should program this check on OriginalSource not only in every TabControl (because its TabItems can contain ListBoxes and ComboBoxes), but also in every ListBox (because its template can contain ComboBoxes).