FIX: TabControl in WPF Shows wrong tab if Active Tab is Invisible

You want to dynamically show or hide tabs in WPF. Say, some only show up if a certain option is set. Well, it shows anyway if its the first tab. Stupid. Here is an attached property to fix that.

Based on code from http://www.codeproject.com/Articles/349140/WPF-TabControl-focus-behavior-with-invisible-tabs

 

using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
namespace Seekford
{/// <summary>Behaviors for TabControl.
    /// </summary>
    public class TabControlBehavior
    {
        /// <summary>Whether to focus the first visible tab.
        /// </summary>
        /// <remarks>Setting the FocusFirstVisibleTab
        /// attached property to true will focus the next visible tab when
        /// the current selected tab's Visibility property is set to Collapsed or Hidden.</remarks>
        public static readonly DependencyProperty FocusFirstVisibleTabProperty =
            DependencyProperty.RegisterAttached("FocusFirstVisibleTab", typeof(bool),
                typeof(TabControlBehavior),
                new FrameworkPropertyMetadata(OnFocusFirstVisibleTabPropertyChanged));
        /// <summary>Gets the focus first visible tab value of the given element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <returns></returns>
        public static bool GetFocusFirstVisibleTab(TabControl element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (bool)element.GetValue(FocusFirstVisibleTabProperty);
        }
        /// <summary>Sets the focus first visible tab value of the given element.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="value">if set to <c>true</c> [value].</param>
        public static void SetFocusFirstVisibleTab(TabControl element, bool value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(FocusFirstVisibleTabProperty, value);
        }
        /// <summary>Determines whether the value of the dependency property <c>IsFocused</c> has change.
        /// </summary>
        /// <param name="d">The dependency object.</param>
        /// <param name="e">The  instance containing the event data.</param>
        private static void OnFocusFirstVisibleTabPropertyChanged(
                DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var tabControl = d as TabControl;
            if (tabControl != null)
            {
                // Attach or detach the event handlers.
                tabControl.IsSynchronizedWithCurrentItem = true;
                if ((bool)e.NewValue)
                {
                    var collection = tabControl.Items as INotifyCollectionChanged;
                    if (collection != null)
                    {
                        collection.CollectionChanged +=
                          new NotifyCollectionChangedEventHandler(TabControl_Items_CollectionChanged);
                    }
                }
                else
                {
                    // Disable the attached behavior.
                    var collection = tabControl.Items as INotifyCollectionChanged;
                    if (collection != null)
                    {
                        collection.CollectionChanged -=
                          new NotifyCollectionChangedEventHandler(TabControl_Items_CollectionChanged);
                    }
                    // Detach handlers from the tab items.
                    foreach (var item in tabControl.Items)
                    {
                        TabItem tab = item as TabItem;
                        if (tab != null)
                        {
                            tab.IsVisibleChanged -=
                              new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
                        }
                    }
                }
            }
        }
        /// <summary>Handles the CollectionChanged event of the TabControl.Items collection.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see
        ///    cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/>
        ///    instance containing the event data.</param>
        static void TabControl_Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Attach event handlers to each tab so that when the Visibility property changes of the selected tab,
            // the focus can be shifted to the next (or previous, if not next tab available) tab.
            var collection = sender as ItemCollection;
            if (collection != null)
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                    case NotifyCollectionChangedAction.Remove:
                    case NotifyCollectionChangedAction.Replace:
                        // Attach event handlers to the Visibility and IsEnabled properties.
                        if (e.NewItems != null)
                        {
                            foreach (var item in e.NewItems)
                            {
                                TabItem tab = item as TabItem;
                                if (tab != null)
                                {
                                    tab.IsVisibleChanged +=
                                      new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
                                }
                            }
                        }
                        // Detach event handlers from old items.
                        if (e.OldItems != null)
                        {
                            foreach (var item in e.OldItems)
                            {
                                TabItem tab = item as TabItem;
                                if (tab != null)
                                {
                                    tab.IsVisibleChanged -=
                                      new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
                                }
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Reset:
                        // Attach event handlers to the Visibility and IsEnabled properties.
                        foreach (var item in collection)
                        {
                            TabItem tab = item as TabItem;
                            if (tab != null)
                            {
                                tab.IsVisibleChanged +=
                                  new DependencyPropertyChangedEventHandler(TabItem_IsVisibleChanged);
                            }
                        }
                        break;
                    case NotifyCollectionChangedAction.Move:
                    default:
                        break;
                }
                // Select the first element if necessary.
                if (collection.Count > 0 && collection.CurrentItem == null)
                {
                    foreach (var item in collection)
                    {
                        if ((item as TabItem).Visibility == Visibility.Visible)
                        {
                            collection.MoveCurrentTo(item);
                            break;
                        }
                    }
                }
            }
        }
        /// <summary>Handles the IsVisibleChanged event of the tab item.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see
        ///   cref="System.Windows.DependencyPropertyChangedEventArgs"/>
        ///   instance containing the event data.</param>
        static void TabItem_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            TabItem tab = sender as TabItem;
            if (tab != null)
            {
                //ensure active tab is visible
                TabControl tabControl = tab.Parent as TabControl;
                if (tabControl != null)
                {
                    bool goforward = true;
                    while (tabControl.SelectedItem == null || (!(tabControl.SelectedItem as TabItem).IsVisible))
                    {
                        if (goforward)
                        {
                            goforward = tabControl.Items.MoveCurrentToNext();
                        }
                        else
                        {
                            if (!tabControl.Items.MoveCurrentToPrevious())
                                break;//nothing is visible
                        }
                    }
                }
            }
        }
    }
}

 

Happy Coding

Leave a Reply

Your email address will not be published. Required fields are marked *