File: Microsoft\Windows\Controls\Ribbon\RibbonGroup.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.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
#if RIBBON_IN_FRAMEWORK
using System.Windows.Controls.Ribbon.Primitives;
using Microsoft.Windows.Controls;
#else
    using Microsoft.Windows.Automation.Peers;
    using Microsoft.Windows.Controls.Ribbon.Primitives;
#endif
using MS.Internal;
 
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon
#else
namespace Microsoft.Windows.Controls.Ribbon
#endif
{
    #endregion
 
    /// <summary>
    ///   A RibbonGroup represents a logical group of controls as they appear on
    ///   a RibbonTab.  These groups resize independently of one another to layout
    ///   their controls in the largest available space.
    /// </summary>
    [TemplatePart(Name = RibbonGroup.CollapsedDropDownButtonTemplatePartName, Type = typeof(RibbonToggleButton))]
    [TemplatePart(Name = RibbonGroup.HeaderContentPresenterTemplatePartName, Type = typeof(ContentPresenter))]
    [TemplatePart(Name = RibbonGroup.HotBackgroundBorderTemplatePartName, Type = typeof(Border))]
    [TemplatePart(Name = RibbonGroup.ItemsPresenterTemplatePartName, Type = typeof(ItemsPresenter))]
    [TemplatePart(Name = RibbonGroup.PopupGridTemplatePartName, Type = typeof(Grid))]
    [TemplatePart(Name = RibbonGroup.PopupTemplatePartName, Type = typeof(Popup))]
    [TemplatePart(Name = RibbonGroup.TemplateContentControlTemplatePartName, Type = typeof(ContentControl))]
    public class RibbonGroup : HeaderedItemsControl
    {
 
        #region Constructors
 
        /// <summary>
        ///   Initializes static members of the RibbonGroup class.
        ///   This overrides the default style.
        /// </summary>
        static RibbonGroup()
        {
            Type ownerType = typeof(RibbonGroup);
 
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(RibbonGroupItemsPanel)))));
            ToolTipService.ShowOnDisabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
            HeaderProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnHeaderChanged)));
            RibbonControlService.IsInQuickAccessToolBarPropertyKey.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnIsInQuickAccessToolBarChanged)));
            ToolTipProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTip)));
            ContextMenuProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(RibbonHelper.OnContextMenuChanged, RibbonHelper.OnCoerceContextMenu));
            ContextMenuService.ShowOnDisabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
            FocusableProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(false));
            ForegroundProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnForegroundChanged)));
#if RIBBON_IN_FRAMEWORK
            AutomationProperties.IsOffscreenBehaviorProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(IsOffscreenBehavior.FromClip));
#endif
 
            EventManager.RegisterClassHandler(ownerType, Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk));
            EventManager.RegisterClassHandler(ownerType, Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCaptureThunk), true /* handledEventsToo */);
            EventManager.RegisterClassHandler(ownerType, RibbonControlService.DismissPopupEvent, new RibbonDismissPopupEventHandler(OnDismissPopupThunk));
            EventManager.RegisterClassHandler(ownerType, Mouse.MouseDownEvent, new MouseButtonEventHandler(OnMouseDownThunk), true);
            EventManager.RegisterClassHandler(ownerType, FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpeningThunk), true);
            EventManager.RegisterClassHandler(ownerType, FrameworkElement.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClosingThunk), true);
            EventManager.RegisterClassHandler(ownerType, KeyTipService.ActivatingKeyTipEvent, new ActivatingKeyTipEventHandler(OnActivatingKeyTipThunk));
            EventManager.RegisterClassHandler(ownerType, KeyTipService.KeyTipAccessedEvent, new KeyTipAccessedEventHandler(OnKeyTipAccessedThunk));
        }
 
        /// <summary>
        ///   Creates an instance of the RibbonGroup class.
        /// </summary>
        public RibbonGroup()
        {
            RibbonGroupSizeDefinitionBaseCollection collection = new RibbonGroupSizeDefinitionBaseCollection();
            _defaultGroupSizeDefinitionsRef = new WeakReference(collection);
            GroupSizeDefinitions = collection;
 
            this.Loaded += delegate
            {
                // Because we queue up asynchronous group size definition updates on the dispatcher
                // with Loaded priority, we need to update group size definitions once here to prevent an
                // unnecessary 2nd render on startup. Before the GroupSizeDefinitions value has been
                // properly coerced, we fall back to _defaultGroupSizeDefinitions, and controls under 
                // RibbonContentPresenters will initially be laid out in their large variant, regardless
                // of the current width constraint. Note that there still exists a corner case that
                // is not handled: if the RibbonGroup starts out with a width that is too small to
                // accommodate the large variant, and an item is added to the items collection, that
                // new item will get rendered in the large variant once, and then will get re-rendered
                // at its correct size variant once the asynchronous callback at Loaded priority comes
                // through.
 
                UpdateGroupSizeDefinitionsCallback();
            };
 
            // Enables custom keytip siblings for the group
            KeyTipService.SetCustomSiblingKeyTipElements(this, new RibbonGroupCustomKeyTipSiblings(this));
        }
 
        #endregion
 
        #region Overrides
 
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
 
            _headerContentPresenter = this.GetTemplateChild(HeaderContentPresenterTemplatePartName) as ContentPresenter;
            _collapsedDropDownButton = this.GetTemplateChild(CollapsedDropDownButtonTemplatePartName) as FrameworkElement;
            _templateContentControl = GetTemplateChild(TemplateContentControlTemplatePartName) as ContentControl;
            _itemsPresenter = GetTemplateChild(ItemsPresenterTemplatePartName) as ItemsPresenter;
            _collapsedGroupPopup = GetTemplateChild(PopupTemplatePartName) as Popup;
 
            if (_hotBackgroundBorder != null)
            {
                _mouseEnterStoryboard = null;
                _mouseLeaveStoryboard = null;
            }
 
            _hotBackgroundBorder = GetTemplateChild(HotBackgroundBorderTemplatePartName) as Border;
 
            if (_hotBackgroundBorder != null)
            {
                _mouseEnterStoryboard = new Storyboard();
                _mouseEnterStoryboard.Children.Add(CreateOpacityAnimation(true, _hotBackgroundBorder));
                _mouseLeaveStoryboard = new Storyboard();
                _mouseLeaveStoryboard.Children.Add(CreateOpacityAnimation(false, _hotBackgroundBorder));
 
                Grid popupGrid = this.GetTemplateChild(PopupGridTemplatePartName) as Grid;
                if (popupGrid != null)
                {
                    popupGrid.MouseEnter += (s, e) =>
                        {
                            _mouseEnterStoryboard.Stop();
                            _mouseEnterStoryboard.Begin();
                        };
 
                    popupGrid.MouseLeave += (s, e) =>
                        {
                            _mouseLeaveStoryboard.Stop();
                            _mouseLeaveStoryboard.Begin();
                        };
                }
            }
 
            CoerceValue(ToolTipProperty);
            RibbonHelper.SetContentAsToolTip(this, this.VisualChild, this.Header, (IsCollapsed && !IsDropDownOpen));
 
            PropertyHelper.TransferProperty(this, ContextMenuProperty);   // Coerce to get a default ContextMenu if none has been specified.
            PropertyHelper.TransferProperty(this, RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty);
 
            RibbonGroupSizeDefinitionBaseCollection groupSizeDefinitions = GroupSizeDefinitions;
            if (groupSizeDefinitions != null &&
                _sizeDefinitionIndex >= 0 &&
                _sizeDefinitionIndex < groupSizeDefinitions.Count)
            {
                SetAppropriatePresenterVisibility(groupSizeDefinitions[_sizeDefinitionIndex] is RibbonGroupSizeDefinition ? Visibility.Visible : Visibility.Collapsed);
            }
        }
 
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);
 
            if (_mouseEnterStoryboard != null &&
                !IsCollapsed)
            {
                _mouseEnterStoryboard.Stop();
                _mouseEnterStoryboard.Begin();
            }
        }
 
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            base.OnMouseLeave(e);
 
            if (_mouseLeaveStoryboard != null &&
                !IsCollapsed)
            {
                _mouseLeaveStoryboard.Stop();
                _mouseLeaveStoryboard.Begin();
            }
        }
 
#if RIBBON_IN_FRAMEWORK
        protected internal override void OnRenderSizeChanged(SizeChangedInfo info)
#else
        protected override void OnRenderSizeChanged(SizeChangedInfo info)
#endif
        {
            base.OnRenderSizeChanged(info);
            if (info.WidthChanged)
            {
                RibbonGroupsPanel groupsPanel = VisualTreeHelper.GetParent(this) as RibbonGroupsPanel;
                if (groupsPanel != null)
                {
                    groupsPanel.OnChildGroupRenderSizeChanged(this, info.PreviousSize.Width);
                }
            }
        }
 
        #endregion
 
        #region Public Properties
 
        /// <summary>
        ///     Gets a value indicating whether or not the RibbonGroup is displayed in a "Collapsed"
        ///     state. In this state the RibbonGroup looks like a drop-down button.
        /// </summary>
        public bool IsCollapsed
        {
            get { return (bool)GetValue(IsCollapsedProperty); }
            internal set { SetValue(IsCollapsedPropertyKey, value); }
        }
 
        /// <summary>
        ///   Key for the IsCollapsed DependencyProperty.
        /// </summary>
        private static readonly DependencyPropertyKey IsCollapsedPropertyKey =
                    DependencyProperty.RegisterReadOnly(
                            "IsCollapsed",
                            typeof(bool),
                            typeof(RibbonGroup),
                            new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged), new CoerceValueCallback(CoerceIsCollapsed)));
 
        private static void OnIsCollapsedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)d;
            RibbonHelper.DelayCoerceProperty(ribbonGroup, IsDropDownOpenProperty);
            ribbonGroup.CoerceValue(ToolTipProperty);
            RibbonHelper.SetContentAsToolTip(ribbonGroup, ribbonGroup.VisualChild, ribbonGroup.Header, (ribbonGroup.IsCollapsed && !ribbonGroup.IsDropDownOpen));
        }
 
        private static object CoerceIsCollapsed(DependencyObject d, object baseValue)
        {
            RibbonGroup group = (RibbonGroup)d;
            if (group.IsInQuickAccessToolBar)
            {
                return true;
            }
            return baseValue;
        }
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for IsCollapsedProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty IsCollapsedProperty =
                IsCollapsedPropertyKey.DependencyProperty;
 
 
        /// <summary>
        ///     Gets or sets the GroupSizeDefinitions property.  This is a collection of RibbonGroupSizeDefinitions which describe
        ///     how the controls in the RibbonGroup should be sized for different size variations of the RibbonGroup itself.
        /// </summary>
        public RibbonGroupSizeDefinitionBaseCollection GroupSizeDefinitions
        {
            get { return (RibbonGroupSizeDefinitionBaseCollection)GetValue(GroupSizeDefinitionsProperty); }
            set { SetValue(GroupSizeDefinitionsProperty, value); }
        }
 
        // Using a DependencyProperty as the backing store for GroupSizeDefinitions.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GroupSizeDefinitionsProperty =
            DependencyProperty.Register(
                "GroupSizeDefinitions", 
                typeof(RibbonGroupSizeDefinitionBaseCollection), 
                typeof(RibbonGroup), 
                new FrameworkPropertyMetadata(new PropertyChangedCallback(OnGroupSizeDefinitionsChanged), new CoerceValueCallback(CoerceGroupSizeDefinitions)));
 
        #endregion
 
        #region RibbonControlService Properties
 
        /// <summary>
        ///     DependencyProperty for SmallImageSource property.
        /// </summary>
        public static readonly DependencyProperty SmallImageSourceProperty =
            RibbonControlService.SmallImageSourceProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     ImageSource property which is normally a 16X16 icon.
        /// </summary>
        public ImageSource SmallImageSource
        {
            get { return RibbonControlService.GetSmallImageSource(this); }
            set { RibbonControlService.SetSmallImageSource(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for LargeImageSource property.
        /// </summary>
        public static readonly DependencyProperty LargeImageSourceProperty =
            RibbonControlService.LargeImageSourceProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     ImageSource property which is normally a 32X32 icon.
        /// </summary>
        public ImageSource LargeImageSource
        {
            get { return RibbonControlService.GetLargeImageSource(this); }
            set { RibbonControlService.SetLargeImageSource(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipTitle property.
        /// </summary>
        public static readonly DependencyProperty ToolTipTitleProperty =
            RibbonControlService.ToolTipTitleProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Title text for the tooltip of the control.
        /// </summary>
        public string ToolTipTitle
        {
            get { return RibbonControlService.GetToolTipTitle(this); }
            set { RibbonControlService.SetToolTipTitle(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipDescription property.
        /// </summary>
        public static readonly DependencyProperty ToolTipDescriptionProperty =
            RibbonControlService.ToolTipDescriptionProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Description text for the tooltip of the control.
        /// </summary>
        public string ToolTipDescription
        {
            get { return RibbonControlService.GetToolTipDescription(this); }
            set { RibbonControlService.SetToolTipDescription(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipImageSource property.
        /// </summary>
        public static readonly DependencyProperty ToolTipImageSourceProperty =
            RibbonControlService.ToolTipImageSourceProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Image source for the tooltip of the control.
        /// </summary>
        public ImageSource ToolTipImageSource
        {
            get { return RibbonControlService.GetToolTipImageSource(this); }
            set { RibbonControlService.SetToolTipImageSource(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipFooterTitle property.
        /// </summary>
        public static readonly DependencyProperty ToolTipFooterTitleProperty =
            RibbonControlService.ToolTipFooterTitleProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Title text for the footer of tooltip of the control.
        /// </summary>
        public string ToolTipFooterTitle
        {
            get { return RibbonControlService.GetToolTipFooterTitle(this); }
            set { RibbonControlService.SetToolTipFooterTitle(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipFooterDescription property.
        /// </summary>
        public static readonly DependencyProperty ToolTipFooterDescriptionProperty =
            RibbonControlService.ToolTipFooterDescriptionProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Description text for the footer of the tooltip of the control.
        /// </summary>
        public string ToolTipFooterDescription
        {
            get { return RibbonControlService.GetToolTipFooterDescription(this); }
            set { RibbonControlService.SetToolTipFooterDescription(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for ToolTipFooterImageSource property.
        /// </summary>
        public static readonly DependencyProperty ToolTipFooterImageSourceProperty =
            RibbonControlService.ToolTipFooterImageSourceProperty.AddOwner(typeof(RibbonGroup), new FrameworkPropertyMetadata(new PropertyChangedCallback(RibbonHelper.OnRibbonToolTipPropertyChanged)));
 
        /// <summary>
        ///     Image source for the footer of the tooltip of the control.
        /// </summary>
        public ImageSource ToolTipFooterImageSource
        {
            get { return RibbonControlService.GetToolTipFooterImageSource(this); }
            set { RibbonControlService.SetToolTipFooterImageSource(this, value); }
        }
 
        #endregion
 
        #region Visual States
 
        /// <summary>
        ///     DependencyProperty for Ribbon property.
        /// </summary>
        public static readonly DependencyProperty RibbonProperty =
            RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     This property is used to access visual style brushes defined on the Ribbon class.
        /// </summary>
        public Ribbon Ribbon
        {
            get { return RibbonControlService.GetRibbon(this); }
        }
 
        /// <summary>
        ///     DependencyProperty for MouseOverBorderBrush property.
        /// </summary>
        public static readonly DependencyProperty MouseOverBorderBrushProperty =
            RibbonControlService.MouseOverBorderBrushProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     Outer border brush used in a "hover" state of the RibbonButton.
        /// </summary>
        public Brush MouseOverBorderBrush
        {
            get { return RibbonControlService.GetMouseOverBorderBrush(this); }
            set { RibbonControlService.SetMouseOverBorderBrush(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for MouseOverBackground property.
        /// </summary>
        public static readonly DependencyProperty MouseOverBackgroundProperty =
            RibbonControlService.MouseOverBackgroundProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     Control background brush used in a "hover" state of the RibbonButton.
        /// </summary>
        public Brush MouseOverBackground
        {
            get { return RibbonControlService.GetMouseOverBackground(this); }
            set { RibbonControlService.SetMouseOverBackground(this, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for IsInQuickAccessToolBar property.
        /// </summary>
        public static readonly DependencyProperty IsInQuickAccessToolBarProperty =
            RibbonControlService.IsInQuickAccessToolBarProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///     This property indicates whether the control is part of a QuickAccessToolBar.
        /// </summary>
        public bool IsInQuickAccessToolBar
        {
            get { return RibbonControlService.GetIsInQuickAccessToolBar(this); }
            internal set { RibbonControlService.SetIsInQuickAccessToolBar(this, value); }
        }
 
        #endregion
 
        #region Private Properties
 
        /// <summary>
        ///  Gets the first visual child
        /// </summary>
        private FrameworkElement VisualChild
        {
            get
            {
                return VisualChildrenCount == 0 ? null : (GetVisualChild(0) as FrameworkElement);
            }
        }
 
        /// <summary>
        ///   Gets or sets the GroupSizeDefinitionsInternal property.  This property supplies default
        ///   values for the GroupSizeDefinitions property if no other value has been assigned.
        /// </summary>
        private RibbonGroupSizeDefinitionBaseCollection GroupSizeDefinitionsInternal
        {
            get
            {
                RibbonGroupSizeDefinition large = GetLargeGroupSizeDefinition();
                if (large == null)
                {
                    return null;
                }
 
                RibbonGroupSizeDefinitionBaseCollection result = new RibbonGroupSizeDefinitionBaseCollection();
                result.Add(large);
 
                if (Items.Count > 3)
                {
                    // Create successively smaller group size definitions by reducing
                    // groups of 3 consecutive controls that have the same size, looping backwards
                    // cyclically, starting from the end.
 
                    // Examples:
                    //   L L L L -> L M M M -> L S S S -> Collapsed
                    //   L L L L L L -> L L L M M M -> M M M M M M -> M M M S S S -> S S S S S S -> Collapsed
 
                    RibbonGroupSizeDefinition last = large;
                    int lastRepeatStartIndex = last.ControlSizeDefinitions.Count - 1;
 
                    while (true)
                    {
                        RibbonGroupSizeDefinition reduced = ReduceGroupSizeDefinition(last, ref lastRepeatStartIndex);
                        if (reduced == null)
                        {
                            break;
                        }
 
                        result.Add(reduced);
                        last = reduced;
 
                        if (lastRepeatStartIndex < 3)
                        {
                            lastRepeatStartIndex = last.ControlSizeDefinitions.Count - 1;
                        }
                    }
                }
                else if (Items.Count == 3)
                {
                    // Special case: L L L -> M M M -> Collapsed (M M M doesn't reduce to S S S)
 
                    if (large.ControlSizeDefinitions[0].ImageSize == RibbonImageSize.Large &&
                        large.ControlSizeDefinitions[0].IsLabelVisible &&
                        large.ControlSizeDefinitions[1].ImageSize == RibbonImageSize.Large &&
                        large.ControlSizeDefinitions[1].IsLabelVisible &&
                        large.ControlSizeDefinitions[2].ImageSize == RibbonImageSize.Large &&
                        large.ControlSizeDefinitions[2].IsLabelVisible)
                    {
                        RibbonGroupSizeDefinition medium = new RibbonGroupSizeDefinition();
 
                        medium.ControlSizeDefinitions.Add(new RibbonControlSizeDefinition() { ImageSize = RibbonImageSize.Small, IsLabelVisible = true });
                        medium.ControlSizeDefinitions.Add(new RibbonControlSizeDefinition() { ImageSize = RibbonImageSize.Small, IsLabelVisible = true });
                        medium.ControlSizeDefinitions.Add(new RibbonControlSizeDefinition() { ImageSize = RibbonImageSize.Small, IsLabelVisible = true });
 
                        result.Add(medium);
                    }
                }
                else if (Items.Count == 2)
                {
                    // Special case: L L -> M M -> Collapsed
 
                    if (large.ControlSizeDefinitions[0].ImageSize == RibbonImageSize.Large &&
                        large.ControlSizeDefinitions[0].IsLabelVisible &&
                        large.ControlSizeDefinitions[1].ImageSize == RibbonImageSize.Large &&
                        large.ControlSizeDefinitions[1].IsLabelVisible)
                    {
                        RibbonGroupSizeDefinition medium = new RibbonGroupSizeDefinition();
 
                        medium.ControlSizeDefinitions.Add(new RibbonControlSizeDefinition() { ImageSize = RibbonImageSize.Small, IsLabelVisible = true });
                        medium.ControlSizeDefinitions.Add(new RibbonControlSizeDefinition() { ImageSize = RibbonImageSize.Small, IsLabelVisible = true });
 
                        result.Add(medium);
                    }
                }
 
                RibbonGroupSizeDefinition collapsed = new RibbonGroupSizeDefinition() { IsCollapsed = true };
                result.Add(collapsed);
 
                result.Freeze();
                return result;
            }
        }
 
        private static void OnForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)d;
            if (ribbonGroup._headerContentPresenter != null)
            {
                BaseValueSource newValueSource = DependencyPropertyHelper.GetValueSource(ribbonGroup, ForegroundProperty).BaseValueSource;
                if (newValueSource > BaseValueSource.Inherited && !SystemParameters.HighContrast)
                {
                    ribbonGroup._headerContentPresenter.SetValue(TextElement.ForegroundProperty, e.NewValue);
                }
                else
                {
                    ribbonGroup._headerContentPresenter.ClearValue(TextElement.ForegroundProperty);
                }
            }
        }
 
        public bool IsDropDownOpen
        {
            get { return (bool)GetValue(IsDropDownOpenProperty); }
            set { SetValue(IsDropDownOpenProperty, value); }
        }
 
        public static readonly DependencyProperty IsDropDownOpenProperty = 
                                                        DependencyProperty.Register("IsDropDownOpen",
                                                        typeof(bool), 
                                                        typeof(RibbonGroup), 
                                                        new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsDropDownOpenChanged), new CoerceValueCallback(CoerceIsDropDownOpen)));
                    
        private static void OnIsDropDownOpenChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup group = (RibbonGroup)sender;
 
            // If the drop down is closed due to
            // an action of context menu or if the 
            // ContextMenu for a parent  
            // was opened by right clicking this 
            // instance then ContextMenuClosed 
            // event is never raised. 
            // Hence reset the flag.
            group.InContextMenu = false;
 
            if (group._collapsedGroupPopup != null)
            {
                UIElement popupChild = group._collapsedGroupPopup.TryGetChild();
                RibbonHelper.HandleIsDropDownChanged(group,
                        delegate() { return group.IsDropDownOpen; },
                        popupChild,
                        popupChild);
            }
 
            if ((bool)(e.NewValue))
            {
                group.RetainFocusOnEscape = RibbonHelper.IsKeyboardMostRecentInputDevice();
            }
 
            group.CoerceValue(ToolTipProperty);
            RibbonHelper.SetContentAsToolTip(group, group.VisualChild, group.Header, (group.IsCollapsed && !group.IsDropDownOpen));
 
            RibbonGroupAutomationPeer peer = UIElementAutomationPeer.FromElement(group) as RibbonGroupAutomationPeer;
            if (peer != null)
            {
                peer.RaiseExpandCollapseAutomationEvent((bool)e.OldValue, (bool)e.NewValue);
            }
        }
 
        private static object CoerceIsDropDownOpen(DependencyObject d, object baseValue)
        {
            RibbonGroup group = (RibbonGroup)d;
            if ((bool)baseValue)
            {
                if (!group.IsLoaded)
                {
                    group.RegisterToOpenOnLoad();
                    return false;
                }
 
                if (!group.IsVisible)
                {
                    group.RegisterOpenOnVisible();
                    return false;
                }
            }
 
            if (!group.IsCollapsed)
            {
                return false;
            }
            return baseValue;
        }
 
        private void RegisterToOpenOnLoad()
        {
            Loaded += new RoutedEventHandler(OpenOnLoad);
        }
 
        private void OpenOnLoad(object sender, RoutedEventArgs e)
        {
            RibbonHelper.DelayCoerceProperty(this, IsDropDownOpenProperty);
            Loaded -= new RoutedEventHandler(OpenOnLoad);
        }
 
        private void RegisterOpenOnVisible()
        {
            IsVisibleChanged += new DependencyPropertyChangedEventHandler(HandleIsVisibleChanged);
        }
 
        void HandleIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            RibbonHelper.DelayCoerceProperty(this, IsDropDownOpenProperty);
            IsVisibleChanged -= new DependencyPropertyChangedEventHandler(HandleIsVisibleChanged);
        }
 
        internal FrameworkElement CollapsedDropDownButton
        {
            get { return _collapsedDropDownButton; }
        }
 
        internal ContentPresenter HeaderContentPresenter
        {
            get { return _headerContentPresenter; }
        }
 
        internal ItemsPresenter ItemsPresenter
        {
            get
            {
                return _itemsPresenter;
            }
        }
        
        #endregion
 
        #region Protected Methods
 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new RibbonGroupAutomationPeer(this);
        }
 
        /// <summary>
        ///     Generates RibbonControl as container
        /// </summary>
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new RibbonControl();
        }
 
        /// <summary>
        ///     An item is its own container if it is RibbonControl
        /// </summary>
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return (item is RibbonControl);
        }
 
        /// <summary>
        ///     Prepare container after generation
        /// </summary>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            base.PrepareContainerForItemOverride(element, item);
            PrepareContainerSize(element);
        }
 
        /// <summary>
        ///     Clear container before detach
        /// </summary>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            // We need to make sure we clear ControlSizeDefinition here. This is especially 
            // important when IsCollapsed changes to true to make sure measure is correctly 
            // invalidated for this element's subtree before the descendants get re-inserted into 
            // the new ItemsPresenter in the collapsed template. See Dev10 bug 899738 for more 
            // information.
 
            element.ClearValue(RibbonControlService.ControlSizeDefinitionProperty);
            base.ClearContainerForItemOverride(element, item);
        }
 
        /// <summary>
        ///     Gets called when items collection changes.
        /// </summary>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);
            UpdateGroupSizeDefinitionsAsync();
        }
 
        protected override Size MeasureOverride(Size availableSize)
        {
            if (IsInQuickAccessToolBar && _sizeDefinitionIndex < 0 && GroupSizeDefinitions.Count > 0)
            {
                _sizeDefinitionIndex = 0;
                ApplyGroupSizeDefinitionBase(GroupSizeDefinitions[0]);
            }
            return base.MeasureOverride(availableSize);
        }
        
        #endregion
 
        #region Internal Methods
 
        /// <summary>
        ///     Applies appropriate GroupSizeDefinitionBase whenever
        ///     the container for group gets prepared.
        /// </summary>
        internal void PrepareRibbonGroup()
        {
            UpdateGroupSizeDefinitionsAsync();
            GroupPrepared = true;          
        }
 
        internal void ClearRibbonGroup()
        {
            GroupPrepared = false;
        }
 
        /// <summary>
        ///     Called whenever the RibbonGroup should try to increase its size.  Moves the size
        ///     definition index counter to the next largest group size definition and applies it.
        /// </summary>
        /// <returns>Returns true if the resize was successful, false otherwise.</returns>
        internal bool IncreaseGroupSize(bool update)
        {
            if (_sizeDefinitionIndex > 0 && this.GroupSizeDefinitions.Count > 0)
            {
                if (update)
                {
                    ApplyGroupSizeDefinitionBase(this.GroupSizeDefinitions[--_sizeDefinitionIndex]);
                }
                return true;
            }
 
            return false;
        }
 
        /// <summary>
        ///     Called whenever the RibbonGroup should try to decrease its size.  Moves the size
        ///     definition index counter to the next smallest group size definition and applies it.
        /// </summary>
        /// <returns>Returns true if the resize was successful, false otherwise.</returns>
        internal bool DecreaseGroupSize()
        {
            if (_sizeDefinitionIndex >= 0 && _sizeDefinitionIndex < this.GroupSizeDefinitions.Count - 1)
            {
                ApplyGroupSizeDefinitionBase(this.GroupSizeDefinitions[++_sizeDefinitionIndex]);
                return true;
            }
 
            return false;
        }
 
        #endregion
 
        #region Private Methods
 
        /// <summary>
        /// Constructs the largest group size definition intelligently based on the image sizes
        /// that have been set on the underlying controls.
        /// </summary>
        private RibbonGroupSizeDefinition GetLargeGroupSizeDefinition()
        {
            if (Items.Count == 0)
            {
                return null;
            }
 
            RibbonGroupSizeDefinition largeGroupSizeDefinition = new RibbonGroupSizeDefinition();
 
            if (IsCollapsed && _itemsPresenter != null)
            {
                if (_itemsPresenter.ApplyTemplate())
                {
                    // If the group is collapsed then force the container generation if not already
                    // done since the containers are needed for determining default GDSs collection.
                    _itemsPresenter.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                }
            }
 
            for (int i = 0; i < Items.Count; i++)
            {
                RibbonControl ribbonControl = ItemContainerGenerator.ContainerFromIndex(i) as RibbonControl;
                RibbonControlSizeDefinition controlSizeDefinition = null;
                if (ribbonControl != null)
                {
                    UIElement contentChild = ribbonControl.ContentChild;
                    if (contentChild != null)
                    {
                        controlSizeDefinition = RibbonControlService.GetDefaultControlSizeDefinition(contentChild);
                        if (controlSizeDefinition == null)
                        {
                            contentChild.CoerceValue(RibbonControlService.DefaultControlSizeDefinitionProperty);
                            controlSizeDefinition = RibbonControlService.GetDefaultControlSizeDefinition(contentChild);
                        }
                    }
                }
                if (controlSizeDefinition == null)
                {
                    // While there is no container available assume 
                    // the control is in its largest variant.
                    controlSizeDefinition = new RibbonControlSizeDefinition();
                }
 
                largeGroupSizeDefinition.ControlSizeDefinitions.Add(controlSizeDefinition);
            }
 
            return largeGroupSizeDefinition;
        }
 
        /// <summary>
        /// Constructs a smaller group size definition by decreasing the size of 3 consecutive
        /// controls with the same size, starting the search backwards from repeatStartIndex.
        /// </summary>
        private static RibbonGroupSizeDefinition ReduceGroupSizeDefinition(RibbonGroupSizeDefinition groupSizeDefinition, ref int repeatStartIndex)
        {
            RibbonControlSizeDefinition lastControlSize = groupSizeDefinition.ControlSizeDefinitions[repeatStartIndex];
            int sameSizeCount = 1;
 
            // Look for 3 consecutive controls with the same size that can be made smaller.
 
            for (int i = repeatStartIndex - 1; i >= 0; i--)
            {
                RibbonControlSizeDefinition controlSize = groupSizeDefinition.ControlSizeDefinitions[i];
 
                if (controlSize.ImageSize != RibbonImageSize.Collapsed &&
                    (controlSize.IsLabelVisible || controlSize.ImageSize == RibbonImageSize.Large) &&
                    controlSize.ImageSize == lastControlSize.ImageSize &&
                    controlSize.IsLabelVisible == lastControlSize.IsLabelVisible)
                {
                    if (++sameSizeCount == 3)
                    {
                        repeatStartIndex = i;
                        break;
                    }
                }
                else
                {
                    sameSizeCount = 1;
                }
 
                lastControlSize = controlSize;
            }
 
            if (sameSizeCount != 3)
            {
                // We didn't find 3 consecutive controls with the same size, so we're done.
                return null;
            }
 
            RibbonGroupSizeDefinition reduced = new RibbonGroupSizeDefinition();
 
            // Add everything before the consecutive 3 controls unchanged.
 
            for (int i = 0; i < repeatStartIndex; i++)
            {
                RibbonControlSizeDefinition controlSize = (RibbonControlSizeDefinition)groupSizeDefinition.ControlSizeDefinitions[i].Clone();
                reduced.ControlSizeDefinitions.Add(controlSize);
            }
 
            // Decrease the size of the 3 consecutive controls.
 
            RibbonControlSizeDefinition repeatedControlSizeDefinition = groupSizeDefinition.ControlSizeDefinitions[repeatStartIndex];
            bool isNewLabelVisible = (repeatedControlSizeDefinition.ImageSize == RibbonImageSize.Large && repeatedControlSizeDefinition.IsLabelVisible);
 
            for (int i = 0; i < 3; i++)
            {
                RibbonControlSizeDefinition sameSize = new RibbonControlSizeDefinition()
                {
                    ImageSize = RibbonImageSize.Small,
                    IsLabelVisible = isNewLabelVisible,
                };
 
                reduced.ControlSizeDefinitions.Add(sameSize);
            }
 
            // Add everything after the consecutive 3 unchanged.
 
            for (int i = repeatStartIndex + 3; i < groupSizeDefinition.ControlSizeDefinitions.Count; i++)
            {
                RibbonControlSizeDefinition controlSize = (RibbonControlSizeDefinition)groupSizeDefinition.ControlSizeDefinitions[i].Clone();
                reduced.ControlSizeDefinitions.Add(controlSize);
            }
 
            return reduced;
        }
 
        /// <summary>
        /// Updates group size definitions asynchronously to give our child controls time to get
        /// inserted into the visual tree. We do this because our group size definition generation
        /// depends on querying properties on these controls.
        /// </summary>
        internal void UpdateGroupSizeDefinitionsAsync()
        {
            if (!GroupSizeUpdatePending)
            {
                GroupSizeUpdatePending = true;
                Dispatcher.BeginInvoke(new Action(UpdateGroupSizeDefinitionsCallback), DispatcherPriority.Loaded);
            }
        }
 
        private void UpdateGroupSizeDefinitionsCallback()
        {
            CoerceValue(GroupSizeDefinitionsProperty);
 
            RibbonGroupSizeDefinitionBaseCollection collection = GroupSizeDefinitions;
            if (collection != null && collection.Count > 0 && GroupPrepared)
            {
                if (_sizeDefinitionIndex < 0)
                {
                    _sizeDefinitionIndex = 0;
                }
                if (_sizeDefinitionIndex >= collection.Count)
                {
                    _sizeDefinitionIndex = collection.Count - 1;
                }
 
                ApplyGroupSizeDefinitionBase(collection[_sizeDefinitionIndex]);
                SetAppropriatePresenterVisibility(GroupSizeDefinitions[_sizeDefinitionIndex] is RibbonGroupSizeDefinition ? Visibility.Visible : Visibility.Collapsed);
 
                RibbonGroupsPanel panel = TreeHelper.FindVisualAncestor<RibbonGroupsPanel>(this);
                if (panel != null)
                {
                    panel.InvalidateCachedMeasure();
                }
            }
 
            GroupSizeUpdatePending = false;
        }
 
        private static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)d;
            RibbonHelper.SetContentAsToolTip(ribbonGroup, ribbonGroup.VisualChild, ribbonGroup.Header, (ribbonGroup.IsCollapsed && !ribbonGroup.IsDropDownOpen));
        }
 
        /// <summary>
        ///     Coerces the GroupSizeDefinitions DependencyProperty to either use its non-null developer assigned value,
        ///     or the default internal GroupSizeDefinitions property value.
        /// </summary>
        /// <param name="d">The RibbonGroup whose GroupSizeDefinitions property changed.</param>
        /// <param name="baseValue">The new value of the GroupSizeDefinitions property, prior to any coercion attempt.</param>
        /// <returns>The coerced value of the GroupSizeDefinitions property.</returns>
        private static object CoerceGroupSizeDefinitions(DependencyObject d, object baseValue)
        {
            RibbonGroup group = (RibbonGroup)d;
            RibbonGroupSizeDefinitionBaseCollection defaultCollection = 
                group._defaultGroupSizeDefinitionsRef.Target as RibbonGroupSizeDefinitionBaseCollection;
            RibbonGroupSizeDefinitionBaseCollection returnValue = baseValue as RibbonGroupSizeDefinitionBaseCollection;
            if (baseValue == null ||
                ((baseValue == defaultCollection) &&
                 (defaultCollection == null || defaultCollection.Count == 0)))
            {
                if (group.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                {
                    // When the containers are not generated yet,
                    // return the existing value as is (which might
                    // be old coerced value too), assuming that
                    // the coercion will happen again after
                    // container generation.
                    returnValue = group.GroupSizeDefinitions;
                }
                else
                {
                    returnValue = group.GroupSizeDefinitionsInternal;
                }
            }
 
            if (returnValue == null)
            {
                if (defaultCollection == null)
                {
                    defaultCollection = new RibbonGroupSizeDefinitionBaseCollection();
                    group._defaultGroupSizeDefinitionsRef = new WeakReference(defaultCollection);
                }
                returnValue = defaultCollection;
            }
            return returnValue;
        }
 
        /// <summary>
        ///     Prepares a child control by applying appropriate size definition
        /// </summary>
        private void PrepareContainerSize(DependencyObject element)
        {
            int index = ItemContainerGenerator.IndexFromContainer(element);
            RibbonGroupSizeDefinitionBaseCollection groupSizeDefinitions = GroupSizeDefinitions;
            int groupSizeDefinitionsCount = groupSizeDefinitions.Count;
            if (_sizeDefinitionIndex >= 0 && _sizeDefinitionIndex < groupSizeDefinitionsCount)
            {
                RibbonGroupSizeDefinition groupDefinition = groupSizeDefinitions[_sizeDefinitionIndex] as RibbonGroupSizeDefinition;
                if (groupDefinition != null)
                {
                    RibbonControlSizeDefinition controlSizeDefinition = null;
                    RibbonControlSizeDefinitionCollection controlSizeDefinitions = groupDefinition.ControlSizeDefinitions;
                    if (controlSizeDefinitions != null &&
                        index < controlSizeDefinitions.Count)
                    {
                        controlSizeDefinition = controlSizeDefinitions[index];
                    }
 
                    // If the group is collapsed, then search for an
                    // appropriate control size definition
                    if (IsCollapsed &&
                        (controlSizeDefinitions == null || controlSizeDefinitions.Count == 0))
                    {
                        RibbonControlSizeDefinitionCollection targetControlSizeDefinitions = GetControlDefinitionsForCollapsedGroup(groupDefinition);
                        if (targetControlSizeDefinitions != null && index < targetControlSizeDefinitions.Count)
                        {
                            controlSizeDefinition = targetControlSizeDefinitions[index];
                        }
                    }
                    if (controlSizeDefinition != null)
                    {
                        RibbonControlService.SetControlSizeDefinition(element, controlSizeDefinition);
                    }
                    else
                    {
                        element.ClearValue(RibbonControlService.ControlSizeDefinitionProperty);
                    }
                }
            }
        }
 
        /// <summary>
        ///     Applies the RibbonGroupSizeDefinitionBase.
        /// </summary>
        private void ApplyGroupSizeDefinitionBase(RibbonGroupSizeDefinitionBase definition)
        {
            if (definition == null)
            {
                definition = _defaultGroupSizeDefinition;
            }
 
            bool remeasure = false;
            if (definition.IsCollapsed != IsCollapsed)
            {
                IsCollapsed = definition.IsCollapsed;
                remeasure = true;
            }
 
            RibbonGroupSizeDefinition groupSizeDefinition = definition as RibbonGroupSizeDefinition;
            if (groupSizeDefinition != null)
            {
                // Apply RibbonGroupSizeDefinition
                if (SetAppropriatePresenterVisibility(Visibility.Visible))
                {
                    TreeHelper.InvalidateMeasureForVisualAncestorPath<RibbonGroup>(_itemsPresenter);
                    remeasure = true;
                }
                if (remeasure)
                {
                    Measure(DesiredSize);
                }
                ApplyGroupSizeDefinition(groupSizeDefinition);
            }
            else
            {
                RibbonGroupTemplateSizeDefinition groupTemplateSizeDefinition = definition as RibbonGroupTemplateSizeDefinition;
                if (groupTemplateSizeDefinition != null)
                {
                    // Apply RibbonGroupTemplateSizeDefinition
                    SetAppropriatePresenterVisibility(Visibility.Collapsed);
                    if (remeasure)
                    {
                        Measure(DesiredSize);
                    }
                    ApplyGroupTemplateSizeDefinition(groupTemplateSizeDefinition);
                }
            }
        }
 
        private bool SetAppropriatePresenterVisibility(Visibility itemsPresenterVisibility)
        {
            bool remeasure = false;
            if (_itemsPresenter != null && _itemsPresenter.Visibility != itemsPresenterVisibility)
            {
                _itemsPresenter.Visibility = itemsPresenterVisibility;
                if (itemsPresenterVisibility == Visibility.Visible)
                {
                    remeasure = true;
                }
            }
 
            if (_templateContentControl != null)
            {
                _templateContentControl.Visibility = (itemsPresenterVisibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible);
            }
            return remeasure;
        }
 
        /// <summary>
        ///     Helper method which searches for
        ///     appropriate control size definition collection
        ///     in case of collapsed group.
        /// </summary>
        private RibbonControlSizeDefinitionCollection GetControlDefinitionsForCollapsedGroup(RibbonGroupSizeDefinition groupSizeDefinition)
        {
            Debug.Assert(groupSizeDefinition != null && groupSizeDefinition.IsCollapsed);
            RibbonGroupSizeDefinitionBaseCollection groupSizeDefinitions = GroupSizeDefinitions;
            int groupSizeDefCount = groupSizeDefinitions.Count;
            for (int i = 0; i < groupSizeDefCount; i++)
            {
                RibbonGroupSizeDefinitionBase currentGroupSizeDefinitionBase = groupSizeDefinitions[i];
                if (currentGroupSizeDefinitionBase == groupSizeDefinition)
                {
                    return null;
                }
                RibbonGroupSizeDefinition currentGroupSizeDefinition = currentGroupSizeDefinitionBase as RibbonGroupSizeDefinition;
                if (currentGroupSizeDefinition != null)
                {
                    RibbonControlSizeDefinitionCollection currentControlSizeDefinitions = currentGroupSizeDefinition.ControlSizeDefinitions;
                    if (currentControlSizeDefinitions != null &&
                        currentControlSizeDefinitions.Count > 0)
                    {
                        return currentGroupSizeDefinition.ControlSizeDefinitions;
                    }
                }
            }
            return null;
        }
 
        /// <summary>
        ///     Logic to apply ControlSizeDefinitions from RibbonGroupSizeDefinition
        /// </summary>
        private void ApplyGroupSizeDefinition(RibbonGroupSizeDefinition groupSizeDefinition)
        {
            RibbonControlSizeDefinitionCollection controlSizeDefinitions = groupSizeDefinition.ControlSizeDefinitions;
            if (IsCollapsed && (controlSizeDefinitions == null || controlSizeDefinitions.Count == 0))
            {
                controlSizeDefinitions = GetControlDefinitionsForCollapsedGroup(groupSizeDefinition);
            }
            int numDefinedSizes = 0;
            if (controlSizeDefinitions != null)
            {
                numDefinedSizes = controlSizeDefinitions.Count;
            }
 
            int itemCount = Items.Count;
            for (int i = 0; i < itemCount; i++)
            {
                DependencyObject d = ItemContainerGenerator.ContainerFromIndex(i);
                if (d != null)
                {
                    if (i < numDefinedSizes)
                    {
                        RibbonControlSizeDefinition def = controlSizeDefinitions[i];
                        RibbonControlService.SetControlSizeDefinition(d, def);
                    }
                    else
                    {
                        d.ClearValue(RibbonControlService.ControlSizeDefinitionProperty);
                    }
                    TreeHelper.InvalidateMeasureForVisualAncestorPath<RibbonGroup>(d);
                }
            }
        }
 
        /// <summary>
        ///     Logic to apply RibbonGroupTemplateSizeDefinition.
        /// </summary>
        /// <param name="grouptemplateSizeDefinition"></param>
        private void ApplyGroupTemplateSizeDefinition(RibbonGroupTemplateSizeDefinition grouptemplateSizeDefinition)
        {
            DataTemplate contentTemplate = grouptemplateSizeDefinition.ContentTemplate;
            if (IsCollapsed && contentTemplate == null)
            {
                RibbonGroupSizeDefinitionBaseCollection groupSizeDefinitions = GroupSizeDefinitions;
                int groupSizeDefCount = groupSizeDefinitions.Count;
                for (int i = 0; i < groupSizeDefCount; i++)
                {
                    RibbonGroupSizeDefinitionBase currentGroupSizeDefinitionBase = groupSizeDefinitions[i];
                    if (currentGroupSizeDefinitionBase == grouptemplateSizeDefinition)
                    {
                        contentTemplate = null;
                        break;
                    }
                    RibbonGroupTemplateSizeDefinition currentGroupSizeDefinition = currentGroupSizeDefinitionBase as RibbonGroupTemplateSizeDefinition;
                    if (currentGroupSizeDefinition != null && currentGroupSizeDefinition.ContentTemplate != null)
                    {
                        contentTemplate = currentGroupSizeDefinition.ContentTemplate;
                        break;
                    }
                }
            }
 
            if (contentTemplate != null && _templateContentControl != null)
            {
                _templateContentControl.ContentTemplate = contentTemplate;
                if (!IsCollapsed)
                {
                    _templateContentControl.Measure(_templateContentControl.DesiredSize);
                    RibbonHelper.FixMeasureInvalidationPaths(_templateContentControl);
                    TreeHelper.InvalidateMeasureForVisualAncestorPath<RibbonGroup>(_templateContentControl);
                }
            }
        }
 
        /// <summary>
        ///     property changed callback to update ownership and event handlers
        ///     when GroupSizeDefinitions change.
        /// </summary>
        private static void OnGroupSizeDefinitionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)d;
            RibbonGroupSizeDefinitionBaseCollection collection = ribbonGroup.GroupSizeDefinitions;
            if (collection != null)
            {
                if (ribbonGroup._sizeDefinitionIndex >= collection.Count)
                {
                    ribbonGroup._sizeDefinitionIndex = collection.Count - 1;
                }
                if (ribbonGroup.GroupPrepared && ribbonGroup._sizeDefinitionIndex >= 0)
                {
                    ribbonGroup.ApplyGroupSizeDefinitionBase(collection[ribbonGroup._sizeDefinitionIndex]);
                }
            }
        }
 
        private static void OnIsInQuickAccessToolBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(IsCollapsedProperty);
        }
 
        private static object CoerceToolTip(DependencyObject d, object value)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)d;
            if (value == null && ribbonGroup.IsCollapsed && !ribbonGroup.IsDropDownOpen)
            {
                return RibbonHelper.CoerceRibbonToolTip(d, value);
            }
            return value;
        }
 
        // Instantiates a DoubleAnimation for showing/hiding the glow effect.
        // This template part toggles a glow effect on the Ribbon group when mouse enters/leaves.
        private static DoubleAnimation CreateOpacityAnimation(bool shouldTurnOn, DependencyObject target)
        {
            DoubleAnimation opacityAnimation;
            if (shouldTurnOn)
            {
                TimeSpan twoTenthsOfASeconds = new TimeSpan(0, 0, 0, 0, 200);
                opacityAnimation = new DoubleAnimation(1, new Duration(twoTenthsOfASeconds));
            }
            else
            {
                TimeSpan fourTenthsOfASeconds = new TimeSpan(0, 0, 0, 0, 400);
                opacityAnimation = new DoubleAnimation(0, new Duration(fourTenthsOfASeconds));
            }
 
            opacityAnimation.SetValue(Storyboard.TargetProperty, target);
            opacityAnimation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Opacity"));
 
            return opacityAnimation;
        }
 
        #endregion
 
        #region Private Data
 
        private enum Bits
        {
            GroupPrepared = 0x01,
            RetainFocusOnEscape = 0x02,
            GroupSizeUpdatePending = 0x04,
            InContextMenu = 0x08
        }
 
        private bool GroupPrepared
        {
            get { return _bits[(int)Bits.GroupPrepared]; }
            set { _bits[(int)Bits.GroupPrepared] = value; }
        }
 
        private bool RetainFocusOnEscape
        {
            get { return _bits[(int)Bits.RetainFocusOnEscape]; }
            set { _bits[(int)Bits.RetainFocusOnEscape] = value; }
        }
 
        private bool GroupSizeUpdatePending
        {
            get { return _bits[(int)Bits.GroupSizeUpdatePending]; }
            set { _bits[(int)Bits.GroupSizeUpdatePending] = value; }
        }
 
        private bool InContextMenu
        {
            get { return _bits[(int)Bits.InContextMenu]; }
            set { _bits[(int)Bits.InContextMenu] = value; }
        }
 
        private int _sizeDefinitionIndex = -1; // The current position of the RibbonGroup ControlSizeDefinition index.
        private FrameworkElement _collapsedDropDownButton; // The SplitButton that replaces RibbonGroup's normal appearance when Collapsed
        private Popup _collapsedGroupPopup;
        private Border _hotBackgroundBorder;  // Used in an animation to give a highlighting effect when mouse-entering a RibbonGroup.
        private ContentPresenter _headerContentPresenter;   // Header
        private ContentControl _templateContentControl;
        private ItemsPresenter _itemsPresenter;
        private WeakReference _defaultGroupSizeDefinitionsRef;
        private static RibbonGroupSizeDefinition _defaultGroupSizeDefinition = new RibbonGroupSizeDefinition();
        private BitVector32 _bits = new BitVector32(0);
        private Storyboard _mouseEnterStoryboard;
        private Storyboard _mouseLeaveStoryboard;
 
        private const double KeyTipVerticalOffsetDelta = 2;
        private const string CollapsedDropDownButtonTemplatePartName = "PART_ToggleButton";
        private const string HeaderContentPresenterTemplatePartName = "PART_Header";
        private const string HotBackgroundBorderTemplatePartName = "PART_HotBackground";
        private const string ItemsPresenterTemplatePartName = "ItemsPresenter";
        private const string PopupGridTemplatePartName = "PART_PopupGrid";
        private const string PopupTemplatePartName = "PART_Popup";
        private const string TemplateContentControlTemplatePartName = "PART_TemplateContentControl";
 
        #endregion
 
        #region DismissPopup
 
        private static void OnLostMouseCaptureThunk(object sender, MouseEventArgs e)
        {
            RibbonGroup group = (RibbonGroup)sender;
            group.OnLostMouseCaptureThunk(e);
        }
 
        private void OnLostMouseCaptureThunk(MouseEventArgs e)
        {
            UIElement popupChild = _collapsedGroupPopup.TryGetChild();
            RibbonHelper.HandleLostMouseCapture(this,
                    e,
                    delegate() { return (IsDropDownOpen && !InContextMenu); },
                    delegate(bool value) { IsDropDownOpen = value; },
                    popupChild,
                    popupChild);
        }
 
        private static void OnClickThroughThunk(object sender, MouseButtonEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)sender;
            ribbonGroup.OnClickThrough(e);
        }
 
        private void OnClickThrough(MouseButtonEventArgs e)
        {
            RibbonHelper.HandleClickThrough(this, e, _collapsedGroupPopup.TryGetChild());
        }
 
        protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseDown(e);
            if (IsDropDownOpen)
            {
                // Close the drop down if the click happened on the toggle button.
                if (RibbonHelper.IsMousePhysicallyOver(_collapsedDropDownButton))
                {
                    IsDropDownOpen = false;
                    e.Handled = true;
                }
            }
        }
 
        private static void OnDismissPopupThunk(object sender, RibbonDismissPopupEventArgs e)
        {
            RibbonGroup ribbonGroup = (RibbonGroup)sender;
            ribbonGroup.OnDismissPopup(e);
        }
 
        private void OnDismissPopup(RibbonDismissPopupEventArgs e)
        {
            UIElement popupChild = _collapsedGroupPopup.TryGetChild();
            RibbonHelper.HandleDismissPopup(e, 
                delegate(bool value) { IsDropDownOpen = value; }, 
                delegate(DependencyObject d) { return d  == _collapsedDropDownButton; },
                popupChild,
                this);
        }
 
        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);
            if (!e.Handled && IsCollapsed)
            {
                RibbonHelper.HandleDropDownKeyDown(this, e,
                    delegate { return IsDropDownOpen; },
                    delegate(bool value) { IsDropDownOpen = value; },
                    RetainFocusOnEscape ? _collapsedDropDownButton : null,
                    _itemsPresenter);
            }
        }
 
        private static void OnMouseDownThunk(object sender, MouseButtonEventArgs e)
        {
            ((RibbonGroup)(sender)).OnAnyMouseDown();
        }
 
        private void OnAnyMouseDown()
        {
            RetainFocusOnEscape = false;
        }
 
        #endregion DismissPopup
 
        #region Context Menu
 
        private static void OnContextMenuOpeningThunk(object sender, ContextMenuEventArgs e)
        {
            ((RibbonGroup)sender).OnContextMenuOpeningInternal();
        }
 
        private void OnContextMenuOpeningInternal()
        {
            InContextMenu = true;
        }
 
        private static void OnContextMenuClosingThunk(object sender, ContextMenuEventArgs e)
        {
            ((RibbonGroup)sender).OnContextMenuClosingInternal();
        }
 
        private void OnContextMenuClosingInternal()
        {
            InContextMenu = false;
            if (IsDropDownOpen)
            {
                UIElement popupChild = _collapsedGroupPopup.TryGetChild();
                RibbonHelper.AsyncSetFocusAndCapture(this,
                    delegate() { return IsDropDownOpen; },
                    popupChild,
                    popupChild);
            }
        }
 
        #endregion
 
        #region QAT
 
        /// <summary>
        ///   DependencyProperty for QuickAccessToolBarId property.
        /// </summary>
        public static readonly DependencyProperty QuickAccessToolBarIdProperty =
            RibbonControlService.QuickAccessToolBarIdProperty.AddOwner(typeof(RibbonGroup));
 
        /// <summary>
        ///   This property is used as a unique identifier to link a control in the Ribbon with its counterpart in the QAT.
        /// </summary>
        public object QuickAccessToolBarId
        {
            get { return RibbonControlService.GetQuickAccessToolBarId(this); }
            set { RibbonControlService.SetQuickAccessToolBarId(this, value); }
        }
 
        /// <summary>
        ///   DependencyProperty for CanAddToQuickAccessToolBarDirectly property.
        /// </summary>
        public static readonly DependencyProperty CanAddToQuickAccessToolBarDirectlyProperty =
            RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty.AddOwner(typeof(RibbonGroup),
            new FrameworkPropertyMetadata(true));
 
 
        /// <summary>
        ///   Property determining whether a control can be added to the RibbonQuickAccessToolBar directly.
        /// </summary>
        public bool CanAddToQuickAccessToolBarDirectly
        {
            get { return RibbonControlService.GetCanAddToQuickAccessToolBarDirectly(this); }
            set { RibbonControlService.SetCanAddToQuickAccessToolBarDirectly(this, value); }
        }
 
        #endregion QAT
 
        #region Custom KeyTip Siblings
 
        private class RibbonGroupCustomKeyTipSiblings : IEnumerable<DependencyObject>
        {
            #region Constructor and Properties
 
            public RibbonGroupCustomKeyTipSiblings(RibbonGroup group)
            {
                RibbonGroup = group;
            }
 
            RibbonGroup RibbonGroup
            {
                get;
                set;
            }
 
            #endregion
 
            #region IEnumerable<DependencyObject> Members
 
            public IEnumerator<DependencyObject> GetEnumerator()
            {
                if (!RibbonGroup.IsInQuickAccessToolBar)
                {
                    // Return all the items in group which have Keytip
                    // including those which are inside a ControlGroup.
                    // This should not be done when in QAT.
                    foreach (object item in RibbonGroup.Items)
                    {
                        DependencyObject element = item as DependencyObject;
                        if (element != null)
                        {
                            if (!string.IsNullOrEmpty(KeyTipService.GetKeyTip(element)))
                            {
                                yield return element;
                            }
 
                            RibbonControlGroup controlGroup = element as RibbonControlGroup;
                            if (controlGroup != null &&
                                !KeyTipService.GetIsKeyTipScope(controlGroup))
                            {
                                foreach (object controlGroupItem in controlGroup.Items)
                                {
                                    DependencyObject controlGroupElement = controlGroupItem as DependencyObject;
                                    if (controlGroupItem != null &&
                                        !string.IsNullOrEmpty(KeyTipService.GetKeyTip(controlGroupElement)))
                                    {
                                        yield return controlGroupElement;
                                    }
                                }
                            }
                        }
                    }
                }
            }
 
            #endregion
 
            #region IEnumerable Members
 
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
 
            #endregion
        }
 
        #endregion
 
        #region KeyTips
 
        /// <summary>
        ///     DependencyProperty for KeyTip property.
        /// </summary>
        public static readonly DependencyProperty KeyTipProperty =
            KeyTipService.KeyTipProperty.AddOwner(typeof(RibbonGroup));
 
        /// <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)
        {
            ((RibbonGroup)sender).OnActivatingKeyTip(e);
        }
 
        protected virtual void OnActivatingKeyTip(ActivatingKeyTipEventArgs e)
        {
            if (e.OriginalSource == this)
            {
                if (!IsCollapsed)
                {
                    // KeyTip should be hidden when not collapsed
                    e.KeyTipVisibility = Visibility.Hidden;
                }
                else
                {
                    if (IsInQuickAccessToolBar)
                    {
                        e.KeyTipHorizontalPlacement = KeyTipHorizontalPlacement.KeyTipCenterAtTargetCenter;
                        e.KeyTipVerticalPlacement = KeyTipVerticalPlacement.KeyTipTopAtTargetCenter;
                        e.KeyTipHorizontalOffset = e.KeyTipVerticalOffset = 0;
                    }
                    else
                    {
                        Ribbon ribbon = Ribbon;
                        if (ribbon != null)
                        {
                            if (ribbon.IsMinimized)
                            {
                                SetMinimizedRibbonKeyTipPlacement(ribbon, e);
                            }
                            else
                            {
                                SetUnminimizedRibbonKeyTipPlacement(ribbon, e);
                            }
                        }
                    }
                }
            }
            else if (_itemsPresenter != null)
            {
                UIElement placementTarget = e.PlacementTarget;
                if (placementTarget == null)
                {
                    placementTarget = RibbonHelper.GetContainingUIElement(e.OriginalSource as DependencyObject);
                }
                if (placementTarget != null &&
                    TreeHelper.IsVisualAncestorOf(_itemsPresenter, placementTarget))
                {
                    // For all the visual descendant set this property,
                    // so that they can be nudged to top/bottom axis if
                    // needed.
                    e.OwnerRibbonGroup = this;
                }
            }
        }
 
        private void SetUnminimizedRibbonKeyTipPlacement(Ribbon ribbon, ActivatingKeyTipEventArgs e)
        {
            GeneralTransform groupToRibbon = TransformToAncestor(ribbon);
            if (groupToRibbon != null)
            {
                Point groupOrigin = groupToRibbon.Transform(new Point());
                double horizontalOffset = groupOrigin.X + (ActualWidth / 2);
                if (DoubleUtil.GreaterThanOrClose(horizontalOffset, 0) &&
                    DoubleUtil.LessThanOrClose(horizontalOffset, ribbon.ActualWidth))
                {
                    e.KeyTipHorizontalPlacement = KeyTipHorizontalPlacement.KeyTipCenterAtTargetLeft;
                    e.KeyTipVerticalPlacement = KeyTipVerticalPlacement.KeyTipTopAtTargetTop;
                    e.KeyTipHorizontalOffset = horizontalOffset;
                    e.KeyTipVerticalOffset = groupOrigin.Y + ActualHeight + KeyTipVerticalOffsetDelta;
                    e.PlacementTarget = ribbon;
                }
                else
                {
                    e.KeyTipVisibility = Visibility.Hidden;
                }
            }
            else
            {
                e.KeyTipVisibility = Visibility.Hidden;
            }
        }
 
        private void SetMinimizedRibbonKeyTipPlacement(Ribbon ribbon, ActivatingKeyTipEventArgs e)
        {
            UIElement ribbonPopupChild = ribbon.ItemsPresenterPopup.TryGetChild();
            if (ribbonPopupChild != null)
            {
                Point popupChildOrigin = ribbon.PointFromScreen(ribbonPopupChild.PointToScreen(new Point()));
                GeneralTransform groupToPopup = TransformToAncestor(ribbonPopupChild);
                if (groupToPopup != null)
                {
                    double horizontalOffset = groupToPopup.Transform(new Point()).X + (ActualWidth / 2);
                    if (DoubleUtil.GreaterThanOrClose(horizontalOffset, 0) &&
                        DoubleUtil.LessThanOrClose(horizontalOffset, ribbonPopupChild.RenderSize.Width))
                    {
                        e.KeyTipHorizontalPlacement = KeyTipHorizontalPlacement.KeyTipCenterAtTargetLeft;
                        e.KeyTipVerticalPlacement = KeyTipVerticalPlacement.KeyTipTopAtTargetTop;
                        e.KeyTipHorizontalOffset = horizontalOffset + popupChildOrigin.X;
                        e.KeyTipVerticalOffset = ribbonPopupChild.RenderSize.Height + popupChildOrigin.Y + KeyTipVerticalOffsetDelta;
                        e.PlacementTarget = ribbon;
                    }
                    else
                    {
                        e.KeyTipVisibility = Visibility.Hidden;
                    }
                }
                else
                {
                    e.KeyTipVisibility = Visibility.Hidden;
                }
            }
        }
 
        private static void OnKeyTipAccessedThunk(object sender, KeyTipAccessedEventArgs e)
        {
            ((RibbonGroup)sender).OnKeyTipAccessed(e);
        }
 
        protected virtual void OnKeyTipAccessed(KeyTipAccessedEventArgs e)
        {
            if (e.OriginalSource == this)
            {
                if (IsCollapsed)
                {
                    // Open the dropdown.
                    IsDropDownOpen = true;
                    UIElement popupChild = _collapsedGroupPopup.TryGetChild();
                    if (popupChild != null)
                    {
                        KeyTipService.SetIsKeyTipScope(popupChild, true);
                        e.TargetKeyTipScope = popupChild;
                    }
                }
                else
                {
                    RibbonTab tab = ItemsControl.ItemsControlFromItemContainer(this) as RibbonTab;
                    if (tab != null &&
                        KeyTipService.GetIsKeyTipScope(tab))
                    {
                        e.TargetKeyTipScope = tab;
                    }
                }
                e.Handled = true;
            }
        }
 
        #endregion
    }
}