File: Microsoft\Windows\Controls\Ribbon\RibbonTab.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_dxtfdo3u_wpftmp.csproj (System.Windows.Controls.Ribbon)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
 
#region Using declarations
 
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
#if RIBBON_IN_FRAMEWORK
using System.Windows.Controls.Ribbon.Primitives;
using Microsoft.Windows.Controls;
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon
#else
namespace Microsoft.Windows.Controls.Ribbon
#endif
{
#else
    using Microsoft.Windows.Automation.Peers;
    using Microsoft.Windows.Controls.Ribbon.Primitives;
#endif
 
    #endregion
 
    [StyleTypedProperty(Property = "HeaderStyle", StyleTargetType = typeof(RibbonTabHeader))]
    public class RibbonTab : HeaderedItemsControl
    {
        #region Constructor
        
        static RibbonTab()
        {
            Type ownerType = typeof(RibbonTab);
 
            IsEnabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsEnabledChanged)));
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(RibbonGroupsPanel)))));
            HeaderProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnHeaderChanged)));
            VisibilityProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(Visibility.Visible, new PropertyChangedCallback(OnVisibilityChanged), new CoerceValueCallback(CoerceVisibility)));
            HeaderTemplateProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged), new CoerceValueCallback(CoerceHeaderTemplate)));
            HeaderTemplateSelectorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));
            HeaderStringFormatProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));
            FocusableProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false));
            EventManager.RegisterClassHandler(ownerType, KeyTipService.ActivatingKeyTipEvent, new ActivatingKeyTipEventHandler(OnActivatingKeyTipThunk));
            EventManager.RegisterClassHandler(ownerType, KeyTipService.KeyTipAccessedEvent, new KeyTipAccessedEventHandler(OnKeyTipAccessedThunk));
            KeyTipService.KeyTipProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnKeyTipChanged)));
#if RIBBON_IN_FRAMEWORK
            AutomationProperties.IsOffscreenBehaviorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(IsOffscreenBehavior.FromClip));
#endif
        }
 
        #endregion
 
        #region Properties
 
#if RIBBON_IN_FRAMEWORK
        protected internal override bool HandlesScrolling
#else
        protected override bool HandlesScrolling
#endif
        {
            get
            {
                return true;
            }
        }
 
        /// <summary>
        ///     The name collection representing the order in which groups should be reduced.
        /// </summary>
        [TypeConverter(typeof(StringCollectionConverter))]
        public StringCollection GroupSizeReductionOrder
        {
            get { return (StringCollection)GetValue(GroupSizeReductionOrderProperty); }
            set { SetValue(GroupSizeReductionOrderProperty, value); }
        }
 
        /// <summary>
        ///     Dependency property backing GroupSizeReductionOrder
        /// </summary>
        public static readonly DependencyProperty GroupSizeReductionOrderProperty =
                DependencyProperty.Register(
                            "GroupSizeReductionOrder",
                            typeof(StringCollection),
                            typeof(RibbonTab),
                            new FrameworkPropertyMetadata(null));
 
        /// <summary>
        ///     Boolean indicating whether this RibbonTab is selected
        /// </summary>
        public bool IsSelected
        {
            get { return (bool)GetValue(IsSelectedProperty); }
            set { SetValue(IsSelectedProperty, value); }
        }
 
        /// <summary>
        ///     Dependency property backing IsSelected
        /// </summary>
        public static readonly DependencyProperty IsSelectedProperty =
                Selector.IsSelectedProperty.AddOwner(typeof(RibbonTab),
                        new FrameworkPropertyMetadata(false,
                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.Journal,
                                new PropertyChangedCallback(OnIsSelectedChanged)));
 
        /// <summary>
        ///     Name of the ContextualTabGroupHeader to which this RibbonTab belongs.
        /// </summary>
        public object ContextualTabGroupHeader
        {
            get { return GetValue(ContextualTabGroupHeaderProperty); }
            set { SetValue(ContextualTabGroupHeaderProperty, value); }
        }
 
        /// <summary>
        ///     Using a DependencyProperty as the backing store for ContextualTabGroupHeader.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty ContextualTabGroupHeaderProperty =
            DependencyProperty.Register("ContextualTabGroupHeader", typeof(object), typeof(RibbonTab), new UIPropertyMetadata(null, new PropertyChangedCallback(OnContextualTabGroupHeaderChanged)));
 
        private static readonly DependencyPropertyKey ContextualTabGroupPropertyKey =
            DependencyProperty.RegisterReadOnly("ContextualTabGroup", typeof(RibbonContextualTabGroup), typeof(RibbonTab), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyHeaderPropertyChanged)));
 
        public static readonly DependencyProperty ContextualTabGroupProperty = ContextualTabGroupPropertyKey.DependencyProperty;
 
        public RibbonContextualTabGroup ContextualTabGroup
        {
            get { return (RibbonContextualTabGroup)GetValue(ContextualTabGroupProperty);  }
            internal set { SetValue(ContextualTabGroupPropertyKey, value);  }
        }
 
        public Style HeaderStyle
        {
            get { return (Style)GetValue(HeaderStyleProperty); }
            set { SetValue(HeaderStyleProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for HeaderStyle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HeaderStyleProperty =
            DependencyProperty.Register("HeaderStyle", typeof(Style), typeof(RibbonTab), new FrameworkPropertyMetadata(null, OnNotifyHeaderPropertyChanged, CoerceHeaderStyle));
 
        /// <summary>
        ///     DependencyProperty for Ribbon property.
        /// </summary>
        public static readonly DependencyProperty RibbonProperty =
            RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonTab));
 
        /// <summary>
        ///     This property is used to access Ribbon
        /// </summary>
        public Ribbon Ribbon
        {
            get { return RibbonControlService.GetRibbon(this); }
        }
 
        /// <summary>
        ///     Boolean indicating if this RibbonTab is a contextual tab.
        /// </summary>
        internal bool IsContextualTab
        {
            get
            {
                return ContextualTabGroupHeader != null;
            }
        }
 
        /// <summary>
        ///     Property which returns the corresponding RibbonTabHeader for this RibbonTab
        /// </summary>
        internal RibbonTabHeader RibbonTabHeader
        {
            get
            {
                Ribbon ribbon = Ribbon;
                if (ribbon != null)
                {
                    int index = ribbon.ItemContainerGenerator.IndexFromContainer(this);
                    if (index >= 0)
                    {
                        RibbonTabHeaderItemsControl headerItemsControl = ribbon.RibbonTabHeaderItemsControl;
                        if (headerItemsControl != null)
                        {
                            return headerItemsControl.ItemContainerGenerator.ContainerFromIndex(index) as RibbonTabHeader;
                        }
                    }
                }
                return null;
            }
        }
 
        private static readonly DependencyPropertyKey TabHeaderLeftPropertyKey =
            DependencyProperty.RegisterReadOnly("TabHeaderLeft", typeof(double), typeof(RibbonTab), null);
 
        public static readonly DependencyProperty TabHeaderLeftProperty = TabHeaderLeftPropertyKey.DependencyProperty;
 
        /// <summary>
        ///     This is position of the left edge of the corresponding RibbonTabHeader in the coordinate space of this RibbonTab
        /// </summary>
        public double TabHeaderLeft
        {
            get { return (double)GetValue(TabHeaderLeftProperty); }
            internal set { SetValue(TabHeaderLeftPropertyKey, value); }
        }
 
        private static readonly DependencyPropertyKey TabHeaderRightPropertyKey =
            DependencyProperty.RegisterReadOnly("TabHeaderRight", typeof(double), typeof(RibbonTab), null);
 
        public static readonly DependencyProperty TabHeaderRightProperty = TabHeaderRightPropertyKey.DependencyProperty;
 
        /// <summary>
        ///     This is position of the right edge of the corresponding RibbonTabHeader in the coordinate space of this RibbonTab
        /// </summary>
        public double TabHeaderRight
        {
            get { return (double)GetValue(TabHeaderRightProperty); }
            internal set { SetValue(TabHeaderRightPropertyKey, value); }
        }
 
        #endregion
 
        #region Protected Methods
        
        /// <summary>
        ///     Returns a new RibbonGroup as the item container
        /// </summary>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new RibbonGroup();
        }
 
        /// <summary>
        ///     An item is its own container if it is RibbonGroup.
        /// </summary>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is RibbonGroup);
        }
 
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            ItemsControl childItemsControl = element as ItemsControl;
            if (childItemsControl != null)
            {
                // copy templates and styles from this ItemsControl
                var itemTemplate = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateProperty);
                var itemTemplateSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateSelectorProperty);
                var itemStringFormat = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemStringFormatProperty);
                var itemContainerStyle = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleProperty);
                var itemContainerStyleSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleSelectorProperty);
                var alternationCount = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.AlternationCountProperty);
                var itemBindingGroup = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemBindingGroupProperty);
 
                base.PrepareContainerForItemOverride(element, item);
 
                // Call this function to work around a restriction of supporting hetrogenous 
                // ItemsCotnrol hierarchy. The method takes care of both ItemsControl and
                // HeaderedItemsControl (in this case) and assign back the default properties
                // whereever appropriate.
                RibbonHelper.IgnoreDPInheritedFromParentItemsControl(
                    childItemsControl,
                    this,
                    itemTemplate,
                    itemTemplateSelector,
                    itemStringFormat,
                    itemContainerStyle,
                    itemContainerStyleSelector,
                    alternationCount,
                    itemBindingGroup,
                    null,
                    null,
                    null);
            }
            else
            {
                base.PrepareContainerForItemOverride(element, item);
            }
 
            RibbonGroup ribbonGroup = element as RibbonGroup;
            if (ribbonGroup != null)
            {
                ribbonGroup.PrepareRibbonGroup();
            }
        }
 
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            base.ClearContainerForItemOverride(element, item);
            RibbonGroup ribbonGroup = element as RibbonGroup;
            if (ribbonGroup != null)
            {
                ribbonGroup.ClearRibbonGroup();
            }
        }
 
        /// <summary>
        ///     Raises event indicating that the IsSelected property is now true.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected virtual void OnSelected(RoutedEventArgs e)
        {
            RaiseEvent(e);
        }
 
        /// <summary>
        ///     Raises event indicating that the IsSelected property is now false.
        /// </summary>
        /// <param name="e">Event arguments</param>
        protected virtual void OnUnselected(RoutedEventArgs e)
        {
            RaiseEvent(e);
        }
 
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
 
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Reset:
                    _groupAutoResizeIndex = null;
                    _groupReduceOrderLocation = -1;
                    _automaticResizeOrder.Clear();
                    _groupReductionResizeStatus.Clear();
                    break;
                case NotifyCollectionChangedAction.Remove:
                    if (GroupSizeReductionOrder != null)
                    {
                        int removedCount = e.OldItems.Count;
                        for (int i = 0; i < removedCount; i++)
                        {
                            int deletedItemIndex = i + e.OldStartingIndex;
                            for (int index = 0; index < _automaticResizeOrder.Count; index++)
                            {
                                if (deletedItemIndex == _automaticResizeOrder[index])
                                {
                                    _automaticResizeOrder.RemoveAt(index--);
                                }
                                else if (deletedItemIndex < _automaticResizeOrder[index])
                                {
                                    _automaticResizeOrder[index]--;
                                }
                            }
 
                            if (_groupAutoResizeIndex != null &&
                                _groupAutoResizeIndex.Value > deletedItemIndex)
                            {
                                _groupAutoResizeIndex--;
 
                                // If we have underflowed our Groups collection, start again at the end.  This
                                // is what makes our group reduction cyclical.
                                if (_groupAutoResizeIndex < 0)
                                {
                                    _groupAutoResizeIndex = Items.Count - 1;
                                }
                            }
                        }
                    }
                    break;
            }
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
 
            if (!e.Handled)
            {
                DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject;
                RibbonTabHeader tabHeader = RibbonTabHeader;
                if (e.Key == Key.Up && focusedElement != null && tabHeader != null)
                {
                    // On arrow up key press if the focus goes out of the tab,
                    // then force it to move to the corresponding TabHeader.
                    DependencyObject upObj = RibbonHelper.PredictFocus(focusedElement, FocusNavigationDirection.Up);
                    if (!RibbonHelper.IsAncestorOf(this, upObj))
                    {
                        if (tabHeader.Focus())
                        {
                            e.Handled = true;
                        }
                    }
                }
            }
        }
 
        #endregion
 
        #region Resizing Logic
 
        /// <summary>
        ///     Method which finds the next group to
        ///     increase size of and increases it.
        /// </summary>
        internal bool IncreaseNextGroupSize()
        {
            RibbonGroup nextGroup = null;
            return IncreaseNextGroupSize(true, out nextGroup);
        }
 
        /// <summary>
        ///     Method which finds the next group to 
        ///     increase the size of.
        /// </summary>
        internal RibbonGroup GetNextIncreaseSizeGroup()
        {
            RibbonGroup nextGroup = null;
            IncreaseNextGroupSize(false, out nextGroup);
            return nextGroup;
        }
 
        /// <summary>
        ///     If the application developer has specified a GroupSizeReductionOrder, this
        ///     takes the next group in that order and tells it to increase to its next size.
        ///     If no GroupSizeReductionOrder was specified, or if we collapsed RibbonGroups
        ///     beyond what was specified by the developer, we expand groups in reverse order
        ///     of their reduction.
        /// </summary>
        /// <returns>True if a group was able to be expanded in size, false otherwise.</returns>
        private bool IncreaseNextGroupSize(bool update, out RibbonGroup nextRibbonGroup)
        {
            nextRibbonGroup = null;
            bool resizeSuccessful = false;
            int automaticResizeOrderCount = _automaticResizeOrder.Count;
            while (automaticResizeOrderCount > 0 && !resizeSuccessful)
            {
                int nextGroupIndex = _automaticResizeOrder[automaticResizeOrderCount - 1];
                nextRibbonGroup = ItemContainerGenerator.ContainerFromIndex(nextGroupIndex) as RibbonGroup;
                if (nextRibbonGroup != null)
                {
                    resizeSuccessful = nextRibbonGroup.IncreaseGroupSize(update);
                }
                if (update)
                {
                    _automaticResizeOrder.RemoveAt(automaticResizeOrderCount - 1);
                    _groupAutoResizeIndex = nextGroupIndex;
                }
                automaticResizeOrderCount--;
            }
 
            if (!resizeSuccessful)
            {
                if (GroupSizeReductionOrder != null &&
                    _groupReduceOrderLocation >= 0)
                {
                    int groupReduceOrderLocation = _groupReduceOrderLocation;
                    int resizeStatusCount = _groupReductionResizeStatus.Count;
                    while (groupReduceOrderLocation >= 0 && !resizeSuccessful)
                    {
                        Debug.Assert(resizeStatusCount > 0);
                        bool wasResizeSuccessful = _groupReductionResizeStatus[resizeStatusCount - 1];
                        if (update)
                        {
                            _groupReductionResizeStatus.RemoveAt(resizeStatusCount - 1);
                        }
                        resizeStatusCount--;
                        if (!wasResizeSuccessful)
                        {
                            groupReduceOrderLocation--;
                            continue;
                        }
 
                        // Find the RibbonGroup whose name is specified next in the GroupSizeReductionOrder.
                        nextRibbonGroup = FindRibbonGroupWithName(GroupSizeReductionOrder[groupReduceOrderLocation--]);
 
                        if (nextRibbonGroup == null)
                        {
                            resizeSuccessful = false;
                        }
                        else
                        {
                            // A group was found, tell it to increase its size.
                            resizeSuccessful = nextRibbonGroup.IncreaseGroupSize(update);
                        }
                    }
                    if (update)
                    {
                        _groupReduceOrderLocation = groupReduceOrderLocation;
                    }
                }
            }
 
            if (!resizeSuccessful)
            {
                nextRibbonGroup = null;
            }
            return resizeSuccessful;
        }
 
        /// <summary>
        ///     If the application developer has specified a GroupSizeReductionOrder, this
        ///     takes the next group in that order and tells it to reduce to its next size.
        ///     If no GroupSizeReductionOrder was specified, or if we need to collapse RibbonGroups
        ///     beyond what was specified by the developer, we reduce groups from right-to-left,
        ///     step by step in cyclical order.
        /// </summary>
        /// <returns>
        ///     Returns true if a group was located and resized successfully, false otherwise.
        /// </returns>
        internal bool DecreaseNextGroupSize()
        {
            bool resizeSuccessful = false;
            if (GroupSizeReductionOrder != null)
            {
                while (_groupReduceOrderLocation < GroupSizeReductionOrder.Count - 1 && !resizeSuccessful)
                {
                    // Find the group who's next to be reduced.
                    RibbonGroup targetGroup = FindRibbonGroupWithName(GroupSizeReductionOrder[++_groupReduceOrderLocation]);
 
                    if (targetGroup == null)
                    {
                        resizeSuccessful = false;
                    }
                    else
                    {
                        resizeSuccessful = targetGroup.DecreaseGroupSize();
                    }
                    _groupReductionResizeStatus.Add(resizeSuccessful);
                }
            }
 
            if (!resizeSuccessful)
            {
                // Either no GroupSizeReductionOrder was specified, or we've run out of predefined orderings.
                // In this case we should begin reducing groups in size right-to-left, step by step, in cyclical
                // order.
                resizeSuccessful = DefaultCyclicalReduceGroup();
            }
 
            return resizeSuccessful;
        }
 
        /// <summary>
        ///     From right-to-left, finds the next group who can be reduced in size.  If the leftmost
        ///     group is encountered reduction will continue in a cyclical fashion back at the rightmost
        ///     RibbonGroup.
        /// </summary>
        /// <returns>True if a group was successfully located and reduced in size, false otherwise.</returns>
        private bool DefaultCyclicalReduceGroup()
        {
            bool resizeSuccessful = false;
 
            if (_groupAutoResizeIndex == null)
            {
                _groupAutoResizeIndex = Items.Count - 1;
            }
 
            bool resizesRemain = true;
 
            while (resizesRemain && !resizeSuccessful)
            {
                int numAttempts = 0;
                do
                {
                    numAttempts++;
                    RibbonGroup group = ItemContainerGenerator.ContainerFromIndex((_groupAutoResizeIndex--).Value) as RibbonGroup;
                    if (group != null)
                    {
                        resizeSuccessful = group.DecreaseGroupSize();
                    }
 
                    if (resizeSuccessful == true)
                    {
                        _automaticResizeOrder.Add(_groupAutoResizeIndex.Value + 1);
                    }
 
                    // If we have underflowed our Groups collection, start again at the end.  This
                    // is what makes our group reduction cyclical.
                    if (_groupAutoResizeIndex.Value < 0)
                    {
                        _groupAutoResizeIndex = Items.Count - 1;
                        break;
                    }
                } while (resizeSuccessful == false);
 
                // If we failed to resize during this pass, and we attempted to resize for every
                // group, then there are no reamining groups to resize.
                if (numAttempts == Items.Count)
                {
                    resizesRemain = false;
                }
            }
 
            return resizeSuccessful;
        }
        #endregion
 
        #region Automation
 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new RibbonTabAutomationPeer(this);
        }
 
        #endregion
 
        #region Internal Methods
 
        internal void PrepareRibbonTab()
        {
            if (ContextualTabGroupHeader != null && Ribbon.ContextualTabGroupItemsControl != null)
            {
                ContextualTabGroup = Ribbon.ContextualTabGroupItemsControl.FindHeader(ContextualTabGroupHeader);
            }
 
            CoerceValue(VisibilityProperty);
 
            RibbonTabHeader tabHeader = RibbonTabHeader;
            if (tabHeader != null)
            {
                tabHeader.InitializeTransferProperties();
            }
        }
 
        internal void NotifyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == HeaderStyleProperty || e.Property == Ribbon.TabHeaderStyleProperty)
            {
                PropertyHelper.TransferProperty(this, HeaderStyleProperty);
            }
            else if (e.Property == HeaderTemplateProperty || e.Property == Ribbon.TabHeaderTemplateProperty)
            {
                PropertyHelper.TransferProperty(this, HeaderTemplateProperty);
            }
        }
 
        #endregion
 
        #region Private Methods
 
        private static object CoerceVisibility(DependencyObject d, object value)
        {
            Visibility baseVisibility = (Visibility)value;
            Visibility contextualVisibility = Visibility.Visible;
            RibbonTab tab = (RibbonTab)d;
            bool contextualHeaderSet = tab.ContextualTabGroupHeader != null;
 
            if (tab.ContextualTabGroup == null && contextualHeaderSet)
            {
                if (tab.Ribbon != null && tab.Ribbon.ContextualTabGroupItemsControl != null)
                {
                    tab.ContextualTabGroup = tab.Ribbon.ContextualTabGroupItemsControl.FindHeader(tab.ContextualTabGroupHeader);
                }
            }
 
            if (tab.ContextualTabGroup != null)
            {
                contextualVisibility = tab.ContextualTabGroup.Visibility;
            }
            else if (contextualHeaderSet)
            {
                contextualVisibility = Visibility.Collapsed;
            }
 
            if (baseVisibility != Visibility.Visible ||
                contextualVisibility != Visibility.Visible)
            {
                return Visibility.Collapsed;
            }
            else
            {
                return Visibility.Visible;
            }
        }
 
        private static void OnVisibilityChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonTab tab = (RibbonTab)sender;
            if (tab.RibbonTabHeader != null)
            {
                tab.RibbonTabHeader.CoerceValue(VisibilityProperty);
            }
 
            // If the selected tab goes from visible to no longer visible, then reset the Ribbon's selected tab.
            Ribbon ribbon = tab.Ribbon;
            if (ribbon != null &&
                tab.IsSelected &&
                (Visibility)e.OldValue == Visibility.Visible &&
                (Visibility)e.NewValue != Visibility.Visible)
            {
                ribbon.ResetSelection();
            }
        }
 
        /// <summary>
        ///     Property changed callback for Header property
        /// </summary>
        private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonTab tab = (RibbonTab)d;
            Ribbon ribbon = tab.Ribbon;
            if (ribbon != null)
            {
                ribbon.NotifyTabHeaderChanged();
            }
            OnNotifyHeaderPropertyChanged(d, e);
        }
 
        /// <summary>
        ///     Property changed called back for IsSelected property
        /// </summary>
        private static void OnIsSelectedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            RibbonTab ribbonTab = (RibbonTab)sender;
            if (ribbonTab.IsSelected)
            {
                ribbonTab.OnSelected(new RoutedEventArgs(Selector.SelectedEvent, ribbonTab));
            }
            else
            {
                ribbonTab.OnUnselected(new RoutedEventArgs(Selector.UnselectedEvent, ribbonTab));
            }
            RibbonTabHeader header = ribbonTab.RibbonTabHeader;
            if (header != null)
            {
                header.CoerceValue(RibbonTabHeader.IsRibbonTabSelectedProperty);
            }
 
            // Raise UI automation events on this RibbonTab
            if ( AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementSelected)
                || AutomationPeer.ListenerExists(AutomationEvents.SelectionItemPatternOnElementRemovedFromSelection))
            {
                RibbonTabAutomationPeer peer = RibbonTabAutomationPeer.CreatePeerForElement(ribbonTab) as RibbonTabAutomationPeer;
                if (peer != null)
                {
                    peer.RaiseTabSelectionEvents();
                }
            }
        }
 
        /// <summary>
        ///     Property changed callback for IsEnabled property.
        /// <summary>
        private static void OnIsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            RibbonTab ribbonTab = (RibbonTab)sender;
            RibbonTabHeader header = ribbonTab.RibbonTabHeader;
            if (header != null)
            {
                header.CoerceValue(RibbonTabHeader.IsEnabledProperty);
            }
        }
 
        /// <summary>
        ///     Property changed callback for ContextualTabGroupHeader property
        /// </summary>
        private static void OnContextualTabGroupHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonTab ribbonTab = (RibbonTab)d;
            Ribbon ribbon = ribbonTab.Ribbon;
            if (ribbon != null)
            {
                ribbon.NotifyTabContextualTabGroupHeaderChanged();
                if (e.NewValue != null && ribbonTab.Ribbon.ContextualTabGroupItemsControl != null)
                {
                    ribbonTab.ContextualTabGroup = ribbonTab.Ribbon.ContextualTabGroupItemsControl.FindHeader(e.NewValue);
                }
                else
                {
                    ribbonTab.ContextualTabGroup = null;
                }
                OnNotifyHeaderPropertyChanged(d, e);
 
                ribbonTab.CoerceValue(VisibilityProperty);
            }
        }
 
        private RibbonGroup FindRibbonGroupWithName(string groupName)
        {
            if (groupName != null)
            {
                groupName = groupName.Trim();
            }
            if (string.IsNullOrEmpty(groupName))
            {
                return null;
            }
            int itemCount = Items.Count;
            for (int i = 0; i < itemCount; i++)
            {
                RibbonGroup group = ItemContainerGenerator.ContainerFromIndex(i) as RibbonGroup;
                if (group != null && group.Name == groupName)
                {
                    return group;
                }
            }
            return null;
        }
 
        private static void OnNotifyHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonTab tab = (RibbonTab)d;
            tab.NotifyPropertyChanged(e);
            RibbonTabHeader tabHeader = tab.RibbonTabHeader;
            if (tabHeader != null)
            {
                tabHeader.NotifyPropertyChanged(e);
            }
        }
 
        private static object CoerceHeaderStyle(DependencyObject d, object baseValue)
        {
            RibbonTab ribbonTab = (RibbonTab)d;
            return PropertyHelper.GetCoercedTransferPropertyValue(ribbonTab,
                baseValue,
                HeaderStyleProperty,
                ribbonTab.Ribbon,
                Ribbon.TabHeaderStyleProperty);
        }
 
        private static object CoerceHeaderTemplate(DependencyObject d, object baseValue)
        {
            RibbonTab ribbonTab = (RibbonTab)d;
            return PropertyHelper.GetCoercedTransferPropertyValue(ribbonTab,
                baseValue,
                HeaderTemplateProperty,
                ribbonTab.Ribbon,
                Ribbon.TabHeaderTemplateProperty);
        }
 
        #endregion
 
        #region Private Data
 
        private int _groupReduceOrderLocation = -1;
        private int? _groupAutoResizeIndex;
        private Collection<int> _automaticResizeOrder = new Collection<int>();
        private Collection<bool> _groupReductionResizeStatus = new Collection<bool>();
 
        #endregion
 
        #region KeyTips
 
        private static void OnKeyTipChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonTab tab = (RibbonTab)d;
            RibbonTabHeader tabHeader = tab.RibbonTabHeader;
            if (tabHeader != null)
            {
                tabHeader.CoerceValue(KeyTipService.KeyTipProperty);
            }
        }
 
        /// <summary>
        ///     DependencyProperty for KeyTip property.
        /// </summary>
        public static readonly DependencyProperty KeyTipProperty =
            KeyTipService.KeyTipProperty.AddOwner(typeof(RibbonTab));
 
        /// <summary>
        ///     KeyTip string for the control.
        /// </summary>
        public string KeyTip
        {
            get { return KeyTipService.GetKeyTip(this); }
            set { KeyTipService.SetKeyTip(this, value); }
        }
 
        private static void OnActivatingKeyTipThunk(object sender, ActivatingKeyTipEventArgs e)
        {
            ((RibbonTab)sender).OnActivatingKeyTip(e);
        }
 
        protected virtual void OnActivatingKeyTip(ActivatingKeyTipEventArgs e)
        {
            if (e.OriginalSource == this)
            {
                // Disable the keytip. The KeyTip is
                // actually used by RibbonTabHeader and hence
                // that will take care of this.
                e.KeyTipVisibility = Visibility.Collapsed;
            }
        }
 
        private static void OnKeyTipAccessedThunk(object sender, KeyTipAccessedEventArgs e)
        {
            ((RibbonTab)sender).OnKeyTipAccessed(e);
        }
 
        protected virtual void OnKeyTipAccessed(KeyTipAccessedEventArgs e)
        {
        }
 
        #endregion
    }
}