File: Microsoft\Windows\Controls\Ribbon\RibbonGallery.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;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml;
using Microsoft.Windows.Input;
using MS.Internal;
#if RIBBON_IN_FRAMEWORK
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;
#endif
 
    #endregion
 
    /// <summary>
    ///   RibbonGallery inherits from ItemsControl. It contains RibbonGalleryCategory instances which in turn contain
    ///   RibbonGalleryItem instances.
    /// </summary>
    [StyleTypedProperty(Property = "AllFilterItemContainerStyle", StyleTargetType = typeof(RibbonMenuItem))]
    [StyleTypedProperty(Property = "FilterItemContainerStyle", StyleTargetType = typeof(RibbonMenuItem))]
    [StyleTypedProperty(Property = "GalleryItemStyle", StyleTargetType = typeof(RibbonGalleryItem))]
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(RibbonGalleryCategory))]
    [TemplatePart(Name = ItemsHostName, Type = typeof(ItemsPresenter))]
    [TemplatePart(Name = _filterMenuButtonTemplatePartName, Type = typeof(RibbonMenuButton))]
    [TemplatePart(Name = ScrollViewerTemplatePartName, Type=typeof(ScrollViewer))]
    [TemplatePart(Name = FilterContentPaneTemplatePartName, Type = typeof(ContentPresenter))]
    public class RibbonGallery : ItemsControl, IWeakEventListener, IPreviewCommandSource
    {
        #region Constructors
 
        /// <summary>
        ///   Initializes static members of the RibbonGallery class.
        /// </summary>
        static RibbonGallery()
        {
            Type ownerType = typeof(RibbonGallery);
            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            ItemContainerStyleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemContainerStyle)));
            ItemTemplateProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(OnCoerceItemTemplate)));
            EventManager.RegisterClassHandler(ownerType, MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
            ToolTipProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(RibbonHelper.CoerceRibbonToolTip)));
            ToolTipService.ShowOnDisabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
            ContextMenuProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(RibbonHelper.OnContextMenuChanged, RibbonHelper.OnCoerceContextMenu));
            ContextMenuService.ShowOnDisabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
#if IN_RIBBON_GALLERY
            ScrollViewer.VerticalScrollBarVisibilityProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceVerticalScrollBarVisibility)));
#else
            ScrollViewer.VerticalScrollBarVisibilityProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null));
#endif
            EventManager.RegisterClassHandler(ownerType, RibbonControlService.DismissPopupEvent, new RibbonDismissPopupEventHandler(OnDismissPopupThunk));
 
            FilterCommand = new RoutedCommand("Filter", ownerType);
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(FilterCommand, FilterExecuted, FilterCanExecute));
            EventManager.RegisterClassHandler(ownerType, LoadedEvent, new RoutedEventHandler(OnLoaded));
            EventManager.RegisterClassHandler(ownerType, UnloadedEvent, new RoutedEventHandler(OnUnloaded));
        }
 
        /// <summary>
        ///   Initializes an instance of the RibbonGallery class.
        /// </summary>
        public RibbonGallery()
        {
            this.ItemContainerGenerator.StatusChanged += new EventHandler(OnItemContainerGeneratorStatusChanged);
 
            // Ensure coercion happens for these APIs.
            this.CoerceValue(FilterItemTemplateSelectorProperty);
            this.CoerceValue(FilterItemContainerStyleSelectorProperty);
        }
 
        #endregion
 
        #region Template
 
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
 
            // remove any old handlers
            if (_filterMenuButton != null)
            {
                Debug.Assert(_filterMenuButton.ItemContainerGenerator != null);
                _filterMenuButton.ItemContainerGenerator.StatusChanged -= OnFilterButtonItemContainerGeneratorStatusChanged;
            }
 
            _filterMenuButton = this.Template.FindName(_filterMenuButtonTemplatePartName, this) as RibbonFilterMenuButton;
 
            if (_filterMenuButton != null)
            {
                Debug.Assert(_filterMenuButton.ItemContainerGenerator != null);
                _filterMenuButton.ItemContainerGenerator.StatusChanged += new EventHandler(OnFilterButtonItemContainerGeneratorStatusChanged);
                Binding itemsSourceBinding = new Binding() { Source = this._categoryFilters };
                _filterMenuButton.SetBinding(RibbonMenuButton.ItemsSourceProperty, itemsSourceBinding);
            }
 
            _itemsPresenter = (ItemsPresenter)GetTemplateChild(ItemsHostName);
            _filterContentPane = GetTemplateChild(FilterContentPaneTemplatePartName) as ContentPresenter;
            _scrollViewer = GetTemplateChild(RibbonGallery.ScrollViewerTemplatePartName) as ScrollViewer;
 
            PropertyHelper.TransferProperty(this, ContextMenuProperty);   // Coerce to get a default ContextMenu if none has been specified.
            PropertyHelper.TransferProperty(this, RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty);
 
#if IN_RIBBON_GALLERY
            if (_scrollViewer != null)
            {
                InRibbonGallery parentInRibbonGallery = ParentInRibbonGallery;
                if (parentInRibbonGallery != null)
                {
                    parentInRibbonGallery.CommandTarget = _scrollViewer;
                }
            }
#endif
        }
 
#if IN_RIBBON_GALLERY
        private static object CoerceVerticalScrollBarVisibility(DependencyObject d, object baseValue)
        {
            RibbonGallery me = (RibbonGallery)d;
 
            // Don't show ScrollBar Template when in InRibbonGalleryMode as it has it's own scroll buttons.
            if (me.IsInInRibbonGalleryMode())
            {
                return ScrollBarVisibility.Hidden;
            }
            return baseValue;
        }
#endif
 
        #region CurrentFilter
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for CurrentFilter.  This enables animation, styling, binding, etc...
        /// </summary>
        private static readonly DependencyProperty CurrentFilterProperty =
            DependencyProperty.Register("CurrentFilter", typeof(object), typeof(RibbonGallery), new FrameworkPropertyMetadata(_allFilter, OnCurrentFilterChanged));
 
        /// <summary>
        ///   Specifies the current filter.
        /// </summary>
        private object CurrentFilter
        {
            get { return GetValue(CurrentFilterProperty); }
            set { SetValue(CurrentFilterProperty, value); }
        }
 
        private static readonly DependencyProperty CurrentFilterStyleProperty =
            DependencyProperty.Register("CurrentFilterStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(null, null, OnCoerceCurrentFilterStyle));
 
        private Style CurrentFilterStyle
        {
            get { return (Style)GetValue(CurrentFilterStyleProperty); }
        }
 
        // coercion precedence:
        //  1) FilterItemContainerStyleSelector, if one is specified.
        //  2) FilterItemContainerStyle/AllFilterItemContainerStyle, if specified.
        //  3) null (the base value)
        // We get this all for free thanks to the coercion on FilterItemContainerStyleSelector and the logic in the default StyleSelector.
        // We just need to tell the following properties to call this coercion when they change:
        //  1) FilterItemContainerStyleSelector
        //  2) FilterItemContainerStyle
        //  3) AllFilterItemContainerStyle
        //  4) CurrentFilter
        private static object OnCoerceCurrentFilterStyle(DependencyObject d, object baseValue)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            object currentFilter = gallery.CurrentFilter;
            StyleSelector filterItemContainerStyleSelector = gallery.FilterItemContainerStyleSelector;
            if (currentFilter != null &&
                filterItemContainerStyleSelector != null &&
                gallery._filterMenuButton != null)
            {
                return filterItemContainerStyleSelector.SelectStyle(currentFilter, gallery._filterMenuButton.CurrentFilterItem);
            }
            return null;
        }
 
        // Header is tricky.  For the filter items, Header defaults to the DataContext.
        // However, Header can be overridden by a Setter at a the ItemContainerStyle level.
        // Therefore, we should only bind Header to CurrentFilter when no Style is setting Header.
        // We need to re-run this logic whenever any of the following change:
        //  1) FilterItemContainerStyleSelector
        //  2) FilterItemContainerStyle
        //  3) AllFilterItemContainerStyle
        //  4) CurrentFilter
        // We also need to run this logic in OnApplyTemplate AFTER Style bindings have been set up.
        internal void SetHeaderBindingForCurrentFilterItem()
        {
            if (_filterMenuButton != null)
            {
                RibbonMenuItem currentFilterItem = _filterMenuButton.CurrentFilterItem;
                if (currentFilterItem != null)
                {
                    currentFilterItem.ClearValue(RibbonMenuItem.HeaderProperty);
 
                    if (PropertyHelper.IsDefaultValue(currentFilterItem, RibbonMenuItem.HeaderProperty))
                    {
                        // In the default case (where no Style is setting Header), we fall back
                        // to setting currentFilterItem.Header to CurrentFilter.
                        currentFilterItem.Header = this.CurrentFilter;
                    }
                }
            }
        }
 
        private static readonly DependencyProperty CurrentFilterTemplateProperty =
            DependencyProperty.Register("CurrentFilterTemplate", typeof(DataTemplate), typeof(RibbonGallery),
                new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCurrentFilterTemplateChanged), new CoerceValueCallback(OnCoerceCurrentFilterTemplate)));
 
        private DataTemplate CurrentFilterTemplate
        {
            get { return (DataTemplate)GetValue(CurrentFilterTemplateProperty); }
        }
 
        private static void OnCurrentFilterTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.SetTemplateBindingForCurrentFilterItem();
        }
 
        private bool FilterMenuButtonTemplateIsBound
        {
            get { return _bits[(int)Bits.FilterMenuButtonTemplateIsBound]; }
            set { _bits[(int)Bits.FilterMenuButtonTemplateIsBound] = value; }
        }
 
        // This method sets up a binding between _filterMenuButton's hosted RibbonMenuItem.HeaderTemplate and CurrentFilterTemplate.
        // We need to call it whenever RibbonGallery is retemplated or CurrentFilterTemplate changes.
        // If CurrentFilterTemplate is not being acquired through a user-set value, we do not want to have this binding.  That way,
        // a Setter for HeaderTemplate in _filterMenuButton's Style, if present, will be honored.
        internal void SetTemplateBindingForCurrentFilterItem()
        {
            if (_filterMenuButton != null)
            {
                RibbonMenuItem currentFilterItem = _filterMenuButton.CurrentFilterItem;
                if (currentFilterItem != null)
                {
                    // Assume we are not going to set up a binding for template.  The user may using FilterItemContainerStyle with a Setter
                    // for HeaderTemplate.  In this case, we need to honor that setter so long as an item template for the filter has not
                    // been set through one of the other APIs: FilterItemTemplate, AllFilterItemTemplate, FilterItemTemplateSelector.
                    // Thus, only set this binding on HeaderTemplate when one of those template APIs is specified.  When a template for the filter item
                    // is not specified, we unset this binding so that a Setter for HeaderTemplate in the Style is able to bleed through.
                    bool templateShouldBeBound = false;
 
                    if (FilterItemTemplateSelector is RibbonGalleryDefaultFilterItemTemplateSelector)
                    {
                        if (object.ReferenceEquals(CurrentFilter, AllFilterItem))
                        {
                            if (this.AllFilterItemTemplate != null)
                            {
                                templateShouldBeBound = true;
                            }
                        }
                        else
                        {
                            if (this.FilterItemTemplate != null)
                            {
                                templateShouldBeBound = true;
                            }
                        }
                    }
                    else
                    {
                        // Someone is setting FilterItemTemplateSelector.  We should bind to the template that gets selected.
                        templateShouldBeBound = true;
                    }
 
                    if (templateShouldBeBound && !FilterMenuButtonTemplateIsBound)
                    {
                        Binding currentFilterTemplateBinding = new Binding("CurrentFilterTemplate") { Source = this };
                        currentFilterItem.SetBinding(RibbonMenuItem.HeaderTemplateProperty, currentFilterTemplateBinding);
                        FilterMenuButtonTemplateIsBound = true;
                    }
                    else if (!templateShouldBeBound && FilterMenuButtonTemplateIsBound)
                    {
                        BindingOperations.ClearBinding(currentFilterItem, RibbonMenuItem.HeaderTemplateProperty);
                        FilterMenuButtonTemplateIsBound = false;
                    }
                }
            }
        }
 
        // coercion precedence:
        //  1) FilterItemTemplateSelector, if one is specified.
        //  2) FilterItemTemplate/AllFilterItemTemplate, if specified.
        //  3) null (the base value)
        // We get this all for free thanks to the coercion on FilterItemTemplateSelector and the logic in the default DataTemplateSelector.
        // We just need to tell the following properties to call this coercion when they change:
        //  1) FilterItemTemplateSelector
        //  2) FilterItemTemplate
        //  3) AllFilterItemTemplate
        //  4) CurrentFilter
        private static object OnCoerceCurrentFilterTemplate(DependencyObject d, object baseValue)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            object currentFilter = gallery.CurrentFilter;
            DataTemplateSelector filterItemTemplateSelector = gallery.FilterItemTemplateSelector;
            if (currentFilter != null &&
                filterItemTemplateSelector != null &&
                gallery._filterMenuButton != null)
            {
                return filterItemTemplateSelector.SelectTemplate(currentFilter, gallery._filterMenuButton.CurrentFilterItem);
            }
            return null;
        }
 
        private void OnItemContainerGeneratorStatusChanged(object sender, EventArgs e)
        {
            if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                RepopulateCategoryFilters();
                SynchronizeWithCurrentItem();
 
                if (_itemsPresenter != null)
                {
                    ItemsHostSite = (Panel)(ItemsPanel.FindName(RibbonGallery.ItemsHostPanelName, _itemsPresenter));
                }
            }
        }
 
        #endregion CurrentFilterItem
 
        private void OnFilterButtonItemContainerGeneratorStatusChanged(object sender, EventArgs e)
        {
            if (_filterMenuButton.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                foreach (object filter in this._categoryFilters)
                {
                    RibbonMenuItem filterItem = _filterMenuButton.ItemContainerGenerator.ContainerFromItem(filter) as RibbonMenuItem;
 
                    // Bind filterItem.IsChecked to true when Object.ReferenceEquals(this.CurrentFilter, filterItem.DataContext).
                    MultiBinding isCheckedBinding = new MultiBinding();
                    isCheckedBinding.Converter = new ReferentialEqualityConverter();
                    Binding currentFilterBinding = new Binding("CurrentFilter") { Source = this };
                    Binding myHeaderBinding = new Binding("DataContext") { Source = filterItem };
                    isCheckedBinding.Bindings.Add(currentFilterBinding);
                    isCheckedBinding.Bindings.Add(myHeaderBinding);
                    filterItem.SetBinding(RibbonMenuItem.IsCheckedProperty, isCheckedBinding);
 
                    // Set up FilterCommand properties.
                    filterItem.Command = RibbonGallery.FilterCommand;
 
                    Binding commandParameterBinding = new Binding("DataContext") { RelativeSource = new RelativeSource(RelativeSourceMode.Self) };
                    filterItem.SetBinding(RibbonMenuItem.CommandParameterProperty, commandParameterBinding);
                }
            }
        }
 
        #endregion Template
 
        #region Tree
 
        // To fetch defined ItemsPanel in RibbonGalleryCategory's Template. It is set during ApplyTemplate.
        internal Panel ItemsHostSite
        {
            get;
            private set;
        }
 
        internal ScrollViewer ScrollViewer
        {
            get
            {
                return _scrollViewer;
            }
        }
 
        internal ItemsPresenter ItemsPresenter
        {
            get
            {
                return _itemsPresenter;
            }
        }
 
        #endregion
 
        #region Layout
 
        /// <summary>
        /// MinColumnCount is the property defined on RibbonGallery. RibbonGalleryCategory also Adds
        /// itself Owner to this property.
        /// It's used by RibbonGalleryItemsPanel during Measure/Arrange which is default panel for RibbonGalleryCategory
        /// Default is 0
        /// </summary>
        public int MinColumnCount
        {
            get { return (int)GetValue(MinColumnCountProperty); }
            set { SetValue(MinColumnCountProperty, value); }
        }
 
        /// <summary>
        /// MinColumnCount is the property defined on RibbonGallery. RibbonGalleryCategory also Adds
        /// itself Owner to this property.
        /// It's used by RibbonGalleryItemsPanel during Measure/Arrange which is default panel for RibbonGalleryCategory
        /// Default is 0
        /// </summary>
        public static readonly DependencyProperty MinColumnCountProperty =
            DependencyProperty.Register(
                            "MinColumnCount",
                            typeof(int),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(OnLayoutPropertyChange)), new ValidateValueCallback(IsMinMaxColumnCountValid));
 
        /// <summary>
        /// MaxColumnCount is the property defined on RibbonGallery. RibbonGalleryCategory also Adds
        /// itself Owner to this property.
        /// It's used by RibbonGalleryItemsPanel during Measure/Arrange which is default panel for RibbonGalleryCategory
        /// Default is int.MaxValue
        /// </summary>
        public int MaxColumnCount
        {
            get { return (int)GetValue(MaxColumnCountProperty); }
            set { SetValue(MaxColumnCountProperty, value); }
        }
 
        /// <summary>
        /// MaxColumnCount is the property defined on RibbonGallery. RibbonGalleryCategory also Adds
        /// itself Owner to this property.
        /// It's used by RibbonGalleryItemsPanel during Measure/Arrange which is default panel for RibbonGalleryCategory
        /// Default is int.MaxValue
        /// </summary>
        public static readonly DependencyProperty MaxColumnCountProperty =
            DependencyProperty.Register(
                            "MaxColumnCount",
                            typeof(int),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(
                                int.MaxValue,
                                FrameworkPropertyMetadataOptions.AffectsMeasure,
                                new PropertyChangedCallback(OnLayoutPropertyChange),
                                new CoerceValueCallback(CoerceMaxColumnCount)),
                            new ValidateValueCallback(IsMinMaxColumnCountValid));
 
        // coerce MaxColumnCount so as it's never lesser than MinColumnCount
        private static object CoerceMaxColumnCount(DependencyObject d, object baseValue)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            int minColCount = gallery.MinColumnCount;
            if (minColCount > (int)baseValue)
                return minColCount;
            return baseValue;
        }
 
        private static bool IsMinMaxColumnCountValid(object value)
        {
            int v = (int)value;
            return (v > 0);
        }
 
        /// <summary>
        /// When ColumnsStretchToFill is true, RibbonGalleryItems are stretched during layout to occupy all the width available.
        /// ColumnsStretchToFill is honored only when IsSharedColumnSizeScope is true.
        /// </summary>
        public bool ColumnsStretchToFill
        {
            get { return (bool)GetValue(ColumnsStretchToFillProperty); }
            set { SetValue(ColumnsStretchToFillProperty, value); }
        }
 
        public static readonly DependencyProperty ColumnsStretchToFillProperty = DependencyProperty.Register("ColumnsStretchToFill",
                                                                                                        typeof(bool),
                                                                                                        typeof(RibbonGallery),
                                                                                                        new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnLayoutPropertyChange)));
 
        /// <summary>
        ///   IsSharedColumnSizeScope: defined on RibbonGallery. RibbonGalleryCategory also adds itself owner to it.
        ///   It's a boolean property where True means that I (the control on which the property is set) am the Scope for
        ///   uniform layout of items. The truth table for this could be defined by:
        ///     gallery     category    Scope
        ///     -------     --------    --------
        ///     T           T           category
        ///     T           F           gallery
        ///     F           T           category
        ///     F           F           gallery*
        ///         * The most correct thing for the F - F combination would be to have no shared size scope at all.  This would
        ///           require us to make non-trivial changes to our measure and arrange algorithms for our panels, and since we
        ///           don't envision a desired user scenario for this we simplify things and treat the F - F combo as gallery scope.
        ///
        ///   We want the default scope to be Gallery scope; hence the default value on Gallery is True and on Category it's false.
        /// </summary>
        public bool IsSharedColumnSizeScope
        {
            get { return (bool)GetValue(IsSharedColumnSizeScopeProperty); }
            set { SetValue(IsSharedColumnSizeScopeProperty, value); }
        }
 
        public static readonly DependencyProperty IsSharedColumnSizeScopeProperty =
            DependencyProperty.Register(
                            "IsSharedColumnSizeScope",
                            typeof(bool),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure,new PropertyChangedCallback(OnLayoutPropertyChange)));
 
        // MaxColumnWidth is the desired maximum width of any RibbonGalleryItem in this RibbonGallery for all the Categories
        // whose Panel's uniform layout scope results in scope of gallery.
        internal double MaxColumnWidth
        {
            get { return (double)GetValue(MaxColumnWidthProperty); }
            set { SetValue(MaxColumnWidthProperty, value); }
        }
 
        // MaxColumnWidth is the desired maximum width of any RibbonGalleryItem in this RibbonGallery for all the Categories
        // whose Panel's uniform layout scope results in scope of gallery.
        internal static readonly DependencyProperty MaxColumnWidthProperty =
            DependencyProperty.Register(
                            "MaxColumnWidth",
                            typeof(double),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnLayoutPropertyChange)));
 
        // MaxItemHeight is the desired maximum height of any RibbonGalleryItem in this RibbonGallery for all the Categories
        internal double MaxItemHeight
        {
            get { return (double)GetValue(MaxItemHeightProperty); }
            set { SetValue(MaxItemHeightProperty, value); }
        }
 
        // MaxItemHeight is the desired maximum height of any RibbonGalleryItem in this RibbonGallery for all the Categories
        internal static readonly DependencyProperty MaxItemHeightProperty =
            DependencyProperty.Register(
                            "MaxItemHeight",
                            typeof(double),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnLayoutPropertyChange)));
 
        /// <summary>
        /// The actual width at which a GalleryItem in a SharedScope is arranged.
        /// Its calculated such that the remaining horizontal space in the parent panel is filled up.
        /// ArrangeWidth is recalculated whenever MaxColumnWidth changes.
        /// </summary>
        internal double ArrangeWidth
        {
            get;
            set;
        }
 
        /// <summary>
        /// Flag to indicate that ArrangeWidth should be recalculated because MaxColumnWidth changed.
        /// </summary>
        internal bool IsArrangeWidthValid
        {
            get;
            set;
        }
 
        /// <summary>
        /// Flag to indicate that MaxColumnWidth should be recalculated. for e.g. when Items collection changes.
        /// </summary>
        internal bool IsMaxColumnWidthValid
        {
            get;
            set;
        }
 
        // This is a PropertyChangedCallBack for the layout related properties MinColumnCount/MaxColumnCount
        // IsSharedColumnScope and MaxColumnWidth on RibbonGallery. This calls InvalidateMeasure for all the
        // categories' ItemsPanel and they must use changed values.
        private static void OnLayoutPropertyChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery me = (RibbonGallery)d;
            me.IsArrangeWidthValid = false;
            me.InvalidateMeasureOnAllCategoriesPanel();
        }
 
        // Invalidate Measure on all the Categories' panel.
        internal void InvalidateMeasureOnAllCategoriesPanel()
        {
            if (Items != null)
            {
                for (int i = 0; i < Items.Count; i++)
                {
                    RibbonGalleryCategory category = (RibbonGalleryCategory)ItemContainerGenerator.ContainerFromIndex(i);
                    if (category != null)
                    {
                        if (category.ItemsHostSite != null)
                        {
                            TreeHelper.InvalidateMeasureForVisualAncestorPath<RibbonGallery>(category.ItemsHostSite, false);
                        }
                    }
                }
            }
        }
 
        #endregion Layout
 
        #region Selection
 
        /// <summary>
        ///     Event fired when <see cref="SelectedItem"/> changes.
        /// </summary>
        public static readonly RoutedEvent SelectionChangedEvent =
            EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble, typeof(RoutedPropertyChangedEventHandler<object>), typeof(RibbonGallery));
 
        /// <summary>
        ///     Event fired when <see cref="SelectedItem"/> changes.
        /// </summary>
        public event RoutedPropertyChangedEventHandler<object> SelectionChanged
        {
            add
            {
                AddHandler(SelectionChangedEvent, value);
            }
 
            remove
            {
                RemoveHandler(SelectionChangedEvent, value);
            }
        }
 
        /// <summary>
        ///     Called when <see cref="SelectedItem"/> changes.
        ///     Default implementation fires the <see cref="SelectionChanged"/> event.
        /// </summary>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnSelectionChanged(RoutedPropertyChangedEventArgs<object> e)
        {
            RaiseEvent(e);
 
            if (ShouldExecuteCommand && !e.Handled && e.NewValue != null)
            {
                CommandHelpers.InvokeCommandSource(CommandParameter, PreviewCommandParameter, this, CommandOperation.Execute);
            }
        }
 
        // There are times when the SelectedItem and the HighlightedItem
        // are being mutated temporarily. This is specifically the case
        // with RibbonComboBox's use of the RibbonGallery. At this time
        // we do not want to fire the Commands.
 
        internal bool ShouldExecuteCommand
        {
            get { return _bits[(int)Bits.ShouldExecuteCommand]; }
            set { _bits[(int)Bits.ShouldExecuteCommand] = value; }
        }
 
        /// <summary>
        ///     The DependencyProperty for the <see cref="SelectedItem"/> property.
        ///     Default Value: null
        /// </summary>
        public static readonly DependencyProperty SelectedItemProperty =
                    DependencyProperty.Register(
                                        "SelectedItem",
                                        typeof(object),
                                        typeof(RibbonGallery),
                                        new FrameworkPropertyMetadata(
                                                null,
                                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                null,
                                                new CoerceValueCallback(CoerceSelectedItem)));
 
 
        /// <summary>
        ///     Specifies the selected item.
        /// </summary>
        public object SelectedItem
        {
            get
            {
                return GetValue(SelectedItemProperty);
            }
            set
            {
                SetValue(SelectedItemProperty, value);
            }
        }
 
        private bool ShouldForceCoerceSelectedItem
        {
            get { return _bits[(int)Bits.ShouldForceCoerceSelectedItem]; }
            set { _bits[(int)Bits.ShouldForceCoerceSelectedItem] = value; }
        }
 
        // To force Coercion even if the SelectionItem didn't change, useful in case of when ItemsCollection changes.
        internal void ForceCoerceSelectedItem()
        {
            try
            {
                ShouldForceCoerceSelectedItem = true;
                CoerceValue(SelectedItemProperty);
            }
            finally
            {
                ShouldForceCoerceSelectedItem = false;
            }
        }
 
        private static object CoerceSelectedItem(DependencyObject d, object value)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            if (!gallery.IsSelectionChangeActive)
            {
                object oldItem = gallery.SelectedItem;
                object newItem = value;
 
                if (!VerifyEqual(oldItem, newItem))
                {
                    if (newItem != null)
                    {
                        RibbonGalleryCategory category = null;
                        RibbonGalleryItem galleryItem = null;
 
                        // If the newItem doesn't exist in RibbonGallery under any category then return UnsetValue
                        // so as not to change existing value
                        bool ignoreItemContainerGeneratorStatus = false;
                        if (!gallery.ContainsItem(newItem, ignoreItemContainerGeneratorStatus, out category, out galleryItem))
                        {
                            return DependencyProperty.UnsetValue;
                        }
 
                        // Changes the selection to newItem
                        gallery.ChangeSelection(newItem, galleryItem, true);
                    }
                    else
                    {
                        // Deselect oldItem
                        gallery.ChangeSelection(oldItem, null, false);
                    }
                }
                else if (gallery.ShouldForceCoerceSelectedItem)
                {
                    RibbonGalleryCategory category = null;
                    RibbonGalleryItem galleryItem = null;
 
                    // This block is called when ItemCollection changes either at the Gallery or the Category levels
                    // to handle the case that a previously SelectedItem is removed or replaced.
                    bool ignoreItemContainerGeneratorStatus = true;
                    if (!gallery.ContainsItem(newItem, ignoreItemContainerGeneratorStatus, out category, out galleryItem))
                    {
                        // Deselect oldItem
                        value = null;
                        gallery.ChangeSelection(oldItem, null, false);
                    }
                }
            }
 
            return value;
        }
 
        /// <summary>
        ///     The DependencyProperty for the <see cref="SelectedValue"/> property.
        ///     Default Value: null
        /// </summary>
        public static readonly DependencyProperty SelectedValueProperty =
                    DependencyProperty.Register(
                                        "SelectedValue",
                                        typeof(object),
                                        typeof(RibbonGallery),
                                        new FrameworkPropertyMetadata(
                                                null,
                                                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                null,
                                                new CoerceValueCallback(CoerceSelectedValue)));
 
        /// <summary>
        ///     Specifies the value on the selected item as defined by <see cref="SelectedValuePath" />.
        /// </summary>
        public object SelectedValue
        {
            get
            {
                return GetValue(SelectedValueProperty);
            }
            set
            {
                SetValue(SelectedValueProperty, value);
            }
        }
 
        private bool ShouldForceCoerceSelectedValue
        {
            get { return _bits[(int)Bits.ShouldForceCoerceSelectedValue]; }
            set { _bits[(int)Bits.ShouldForceCoerceSelectedValue] = value; }
        }
 
        // To force Coercion even if the SelectionValue didn't change, useful when synchronizing
        // SelectedItem after container generation is complete.
        internal void ForceCoerceSelectedValue()
        {
            try
            {
                ShouldForceCoerceSelectedValue = true;
                CoerceValue(SelectedValueProperty);
            }
            finally
            {
                ShouldForceCoerceSelectedValue = false;
            }
        }
 
        private static object CoerceSelectedValue(DependencyObject d, object value)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            if (!gallery.IsSelectionChangeActive)
            {
                object oldValue = gallery.SelectedValue;
                object newValue = value;
 
                if (!VerifyEqual(oldValue, newValue))
                {
                    if (newValue != null)
                    {
                        object newItem;
                        RibbonGalleryCategory category = null;
                        RibbonGalleryItem galleryItem = null;
 
                        // If the newValue doesn't exist in RibbonGallery under any category then return UnsetValue
                        // so as not to change existing value
                        bool ignoreItemContainerGeneratorStatus = false;
                        if (!gallery.ContainsValue(value, ignoreItemContainerGeneratorStatus, out newItem, out category, out galleryItem))
                        {
                            return DependencyProperty.UnsetValue;
                        }
 
                        // Changes the selection to newItem
#if RIBBON_IN_FRAMEWORK
                        gallery.SetCurrentValue(SelectedItemProperty, newItem);
#else
                        gallery.SelectedItem = newItem;
#endif
                    }
                    else
                    {
                        // Deselect
#if RIBBON_IN_FRAMEWORK
                        gallery.InvalidateProperty(SelectedItemProperty);
#else
                        gallery.SelectedItem = null;
#endif
                    }
                }
                else if (gallery.ShouldForceCoerceSelectedValue)
                {
                    object newItem;
                    RibbonGalleryCategory category = null;
                    RibbonGalleryItem galleryItem = null;
 
                    // This block is called when generating RibbonGalleryCategory containers
                    // to synchronize the SelectedItem with the previously specified SelectedValue.
                    // If a match isn't found yet we keep the oldValue as is by returning UnsetValue.
                    bool ignoreItemContainerGeneratorStatus = true;
                    if (!gallery.ContainsValue(value, ignoreItemContainerGeneratorStatus, out newItem, out category, out galleryItem))
                    {
                        return DependencyProperty.UnsetValue;
                    }
#if RIBBON_IN_FRAMEWORK
                        gallery.SetCurrentValue(SelectedItemProperty, newItem);
#else
                        gallery.SelectedItem = newItem;
#endif
                }
            }
 
            return value;
        }
 
        /// <summary>
        ///     SelectedValuePath DependencyProperty
        /// </summary>
        public static readonly DependencyProperty SelectedValuePathProperty =
                DependencyProperty.Register(
                        "SelectedValuePath",
                        typeof(string),
                        typeof(RibbonGallery),
                        new FrameworkPropertyMetadata(
                                String.Empty,
                                new PropertyChangedCallback(OnSelectedValuePathChanged)));
 
        /// <summary>
        ///  The path used to retrieve the SelectedValue from the SelectedItem
        /// </summary>
        [Localizability(LocalizationCategory.NeverLocalize)] // not localizable
        public string SelectedValuePath
        {
            get { return (string) GetValue(SelectedValuePathProperty); }
            set { SetValue(SelectedValuePathProperty, value); }
        }
 
        private static void OnSelectedValuePathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            ValueSource valueSource = DependencyPropertyHelper.GetValueSource(gallery, RibbonGallery.SelectedValueProperty);
 
            if (valueSource.IsCoerced || gallery.SelectedValue != null)
            {
                gallery.CoerceValue(SelectedValueProperty);
            }
        }
 
        public static readonly DependencyProperty IsSynchronizedWithCurrentItemProperty =
            DependencyProperty.Register("IsSynchronizedWithCurrentItem", typeof(bool?), typeof(RibbonGallery),
            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnIsSynchronizedWithCurrentItemChanged)));
 
        /// <summary>
        ///   This flag chooses the synchronization behavior between the
        ///   SelectedItem and the CurrentItem for the associated CollectionView.
        ///   When null we choose the automatic behavior of synchronizing only
        ///   when bound to an explicit CollectionViewSource.
        /// </summary>
        public bool? IsSynchronizedWithCurrentItem
        {
            get { return (bool?)GetValue(IsSynchronizedWithCurrentItemProperty); }
            set { SetValue(IsSynchronizedWithCurrentItemProperty, value); }
        }
 
        private static void OnIsSynchronizedWithCurrentItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.UpdateIsSynchronizedWithCurrentItemInternal();
        }
 
        /// <summary>
        ///   Update the private flag IsSynchronizedWithCurrentItemInternal so that it
        ///   honor the public property when set and chooses the automatic behavior
        ///   which is synchornize currency only when bound to an explicit CollectionViewSource.
        /// </summary>
        private void UpdateIsSynchronizedWithCurrentItemInternal()
        {
            bool oldValue = IsSynchronizedWithCurrentItemInternal;
            if (oldValue)
            {
                // Stop listening for currency changes
                RemoveCurrentItemChangedListener();
            }
 
            bool? isSynchronizedWithCurrentItem = IsSynchronizedWithCurrentItem;
            if (isSynchronizedWithCurrentItem.HasValue)
            {
                IsSynchronizedWithCurrentItemInternal = isSynchronizedWithCurrentItem.Value;
            }
            else
            {
                IsSynchronizedWithCurrentItemInternal = IsInitialized && (RibbonGallery.GetSourceCollectionView(this) != null);
            }
 
 
            bool newValue = IsSynchronizedWithCurrentItemInternal;
            if (newValue)
            {
                // Listen for currency changes
                AddCurrentItemChangedListener();
 
                // Synchronize
                SynchronizeWithCurrentItem();
            }
 
            if (oldValue != newValue)
            {
                // Notify categories
                for (int i = 0; i < Items.Count; i++ )
                {
                    RibbonGalleryCategory category = ItemContainerGenerator.ContainerFromIndex(i) as RibbonGalleryCategory;
                    if (category != null)
                    {
                        if (newValue)
                        {
                            category.AddCurrentItemChangedListener();
                        }
                        else
                        {
                            category.RemoveCurrentItemChangedListener();
                        }
                    }
                }
            }
        }
 
        internal bool IsSynchronizedWithCurrentItemInternal
        {
            get { return _bits[(int)Bits.IsSynchronizedWithCurrentItemInternal]; }
            set { _bits[(int)Bits.IsSynchronizedWithCurrentItemInternal] = value; }
        }
 
        private void SynchronizeWithCurrentItem()
        {
            if (IsSynchronizedWithCurrentItemInternal)
            {
                object selectedItem = SelectedItem;
                if (selectedItem != null)
                {
                    // If there is a SelectedItem synchronize
                    // CurrentItem to match it
                    RibbonGalleryCategory category;
                    RibbonGalleryItem galleryItem;
                    bool ignoreItemContainerGeneratorStatus = false;
                    if (ContainsItem(selectedItem, ignoreItemContainerGeneratorStatus, out category, out galleryItem))
                    {
                        SynchronizeWithCurrentItem(category, selectedItem);
                    }
                }
                else
                {
                    // Since there isn't already a SelectedItem
                    // synchronize it to match CurrentItem
                    //
                    // It is possible that the RibbonGalleryCategory containers
                    // haven't been generated at the time that we attempt this
                    // synchronization. In such a case we retry when the
                    // containers have been generated.
                    if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                    {
                        OnCurrentItemChanged();
                    }
 
                    OnSourceCollectionViewCurrentItemChanged();
                }
            }
        }
 
        private void SynchronizeWithCurrentItem(
            RibbonGalleryCategory category,
            object selectedItem)
        {
            Debug.Assert(selectedItem != null, "Must have a selectedItem to synchronize with.");
 
            if (category != null)
            {
                // Synchronize currency on the category containing the SelectedItem
                MoveCurrentTo(category.CollectionView, selectedItem);
 
                // Synchronize currency on the gallery to be the category containing the SelectedItem
                MoveCurrentTo(CollectionView, ItemContainerGenerator.ItemFromContainer(category));
            }
 
            // Synchronize currency on the source CollectionView to be the SelectedItem
            int index = SourceCollectionView != null ? SourceCollectionView.IndexOf(selectedItem) : -1;
            if (index > -1)
            {
                MoveCurrentToPosition(SourceCollectionView, index);
            }
        }
 
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            UpdateIsSynchronizedWithCurrentItemInternal();
        }
 
        protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
        {
            base.OnItemsSourceChanged(oldValue, newValue);
            UpdateIsSynchronizedWithCurrentItemInternal();
 
            // Note that it is possible that we haven't had a chance to sync
            // to the CurrentItem on the CollectionView yet. This could happen,
            // if the ItemContainers were generated synchronously and the base
            // implementation fired ItemContainerGeneratorStatusChanged before
            // we got here. So this is one more attempt to keep things in sync.
 
            SynchronizeWithCurrentItem();
        }
 
        private void AddCurrentItemChangedListener()
        {
            Debug.Assert(IsSynchronizedWithCurrentItemInternal, "We should add currency change listeners only when IsSynchronizedWithCurrentItemInternal is true");
 
            CollectionView = Items;
            SourceCollectionView = RibbonGallery.GetSourceCollectionView(this);
 
            if (SourceCollectionView == CollectionView.SourceCollection)
            {
                // We need to track the SourceCollectionView only if it
                // is distinct from the immediate CollectionView
                SourceCollectionView = null;
            }
 
            // Listen for currency changes on the immediate CollectionView for the Gallery
            CurrentChangedEventManager.AddListener(CollectionView, this);
 
            // Listen for currency changes on the Source CollectionView of the Gallery
            if (SourceCollectionView != null)
            {
                CurrentChangedEventManager.AddListener(SourceCollectionView, this);
            }
        }
 
        private void RemoveCurrentItemChangedListener()
        {
            // Stop listening for currency changes on the immediate CollectionView for the Gallery
            if (CollectionView != null)
            {
                CurrentChangedEventManager.RemoveListener(CollectionView, this);
                CollectionView = null;
            }
 
            // Stop listening for currency changes on the Source CollectionView of the Gallery
            if (SourceCollectionView != null)
            {
                CurrentChangedEventManager.RemoveListener(SourceCollectionView, this);
                SourceCollectionView = null;
            }
        }
 
        internal CollectionView CollectionView
        {
            get;
            set;
        }
 
        internal CollectionView SourceCollectionView
        {
            get;
            set;
        }
 
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            if (managerType == typeof(CurrentChangedEventManager))
            {
                if (sender == CollectionView)
                {
                    // Update currency on the immediate CollectionView for the Gallery
                    OnCurrentItemChanged();
                }
                else
                {
                    // Update currency on the Source CollectionView of the Gallery
                    OnSourceCollectionViewCurrentItemChanged();
                }
            }
            else
            {
                // Unrecognized event
                return false;
            }
 
            return true;
        }
 
        // Update currency on the immediate CollectionView for the Gallery
        private void OnCurrentItemChanged()
        {
            Debug.Assert(IsSynchronizedWithCurrentItemInternal, "We shouldn't be listening for currency changes if IsSynchronizedWithCurrentItemInternal is false");
 
            if (CollectionView == null || IsSelectionChangeActive)
            {
                return;
            }
 
            // Synchronize the SelectedItem to be the first Item
            // within the current Category.
            RibbonGalleryCategory category = this.ItemContainerGenerator.ContainerFromItem(CollectionView.CurrentItem) as RibbonGalleryCategory;
            if (category != null && category.Items.Count > 0)
            {
#if RIBBON_IN_FRAMEWORK
                SetCurrentValue(SelectedItemProperty, category.Items[0]);
#else
                SelectedItem = category.Items[0];
#endif
            }
        }
 
        // Update currency on the Source CollectionView of the Gallery
        private void OnSourceCollectionViewCurrentItemChanged()
        {
            Debug.Assert(IsSynchronizedWithCurrentItemInternal, "We shouldn't be listening for currency changes if IsSynchronizedWithCurrentItemInternal is false");
 
            if (IsSelectionChangeActive)
            {
                return;
            }
 
            // Synchronize SelectedItem with the CurrentItem
            // of the Source CollectionView
            if (SourceCollectionView != null)
            {
#if RIBBON_IN_FRAMEWORK
                SetCurrentValue(SelectedItemProperty, SourceCollectionView.CurrentItem);
#else
                SelectedItem = SourceCollectionView.CurrentItem;
#endif
            }
        }
 
        // This is to handle the case where the Gallery is bound to the Groups
        // property of a CollectionViewSource with GroupDescriptions. Consider
        // this example.
        //
        // <RibbonGallery
        //  Name="rg"
        //  IsSynchronizedWithCurrentItem="True"
        //  Grid.Row="1"
        //  Grid.Column="0"
        //  ItemsSource="{Binding Source={StaticResource CitiesCVS},Path=Groups}"
        //  ItemTemplate="{StaticResource HDT2}" />
        // <ListBox
        //  Name="lb"
        //  SelectionMode="Single"
        //  IsSynchronizedWithCurrentItem="True"
        //  Grid.Row="1"
        //  Grid.Column="1"
        //  ItemsSource="{Binding Source={StaticResource CitiesCVS}}"
        //  ItemTemplate="{StaticResource CT2}"/>
        //
        // To keep the ListBox synchronized with the Gallery we will need to update currency on
        // the CollectionView that the Listox is bound which in this case is the Source CollectionView
        // for Groups collection that the Gallery is bound to.
        internal static CollectionView GetSourceCollectionView(ItemsControl itemsControl)
        {
            if (itemsControl == null)
                return null;
 
            CollectionViewSource cvs = null;
            CollectionView cv = null;
            Binding binding = BindingOperations.GetBinding(itemsControl, ItemsSourceProperty);
            if (binding != null)
            {
                cvs = binding.Source as CollectionViewSource;
                if (cvs != null)
                {
                    cv = cvs.View as CollectionView;
                }
            }
 
            return cv;
        }
 
        // Update all selection properties viz.
        // - SelectedItem
        // - SelectedValue
        // - CurrentItem
        // - IsSelected
        // - SelectedContainers
        internal void ChangeSelection(object item, RibbonGalleryItem container, bool isSelected)
        {
            if (IsSelectionChangeActive)
            {
                return;
            }
 
            object oldItem = SelectedItem;
            object newItem = item;
            bool selectedItemChanged = !VerifyEqual(oldItem, newItem);
 
            try
            {
                IsSelectionChangeActive = true;
 
                if (isSelected == selectedItemChanged)
                {
                    // Deselecting a single container. This can only happen
                    // when setting IsSelected to false on a specific container.
                    // Note that neither SelectedItem nor CurrentItem are updated
                    // in this case. We only updated the _selectedContainers and
                    // ContainsSelection properties both of which are specific to
                    // the containers in view.
                    if (!isSelected && container != null)
                    {
                        container.IsSelected = false;
                        int index = _selectedContainers.IndexOf(container);
                        if (index > -1)
                        {
                            _selectedContainers.RemoveAt(index);
                            container.OnUnselected(new RoutedEventArgs(RibbonGalleryItem.UnselectedEvent, container));
                        }
                    }
                    else
                    {
                        // This is the case where SelectedItem is changing.
                        // We start the processing by deselecting all existing
                        // containers.
                        for (int i = 0; i < _selectedContainers.Count; i++)
                        {
                            RibbonGalleryItem galleryItem = _selectedContainers[i];
                            galleryItem.IsSelected = false;
                            galleryItem.OnUnselected(new RoutedEventArgs(RibbonGalleryItem.UnselectedEvent, galleryItem));
 
                            if (!isSelected)
                            {
                                MoveCurrentToPosition(galleryItem.RibbonGalleryCategory.CollectionView, -1);
                            }
                        }
                        _selectedContainers.Clear();
 
                        if (!isSelected)
                        {
#if RIBBON_IN_FRAMEWORK
                            InvalidateProperty(SelectedItemProperty);
                            InvalidateProperty(SelectedValueProperty);
#else
                            SelectedItem = null;
                            SelectedValue = null;
#endif
 
                            MoveCurrentToPosition(CollectionView, -1);
                            MoveCurrentToPosition(SourceCollectionView, -1);
 
                            // When changing RibbonGallery.SelectedItem to null, we need to push RibbonComboBox to update its
                            // selection properties.  Even though RibbonComboBox handles RibbonGalleryItem.UnselectedEvent,
                            // at the time of its handling RibbonGallery.SelectedItem is still non-null.  We need to refresh
                            // RibbonComboBox's selection properties once RibbonGallery.SelectedItem is actually null.
                            RibbonComboBox comboBoxParent = LogicalTreeHelper.GetParent(this) as RibbonComboBox;
                            if (comboBoxParent != null &&
                                this == comboBoxParent.FirstGallery &&
                                comboBoxParent.IsSelectedItemCached == false)
                            {
                                comboBoxParent.UpdateSelectionProperties();
                            }
                        }
                    }
 
                    // Select the item
                    if (isSelected)
                    {
#if RIBBON_IN_FRAMEWORK
                        SetCurrentValue(SelectedItemProperty, item);
                        SetCurrentValue(SelectedValueProperty, GetSelectableValueFromItem(item));
#else
                        SelectedItem = item;
                        SelectedValue = GetSelectableValueFromItem(item);
#endif
 
                        // Synchronize currency with the specified SelectedItem.
                        if (container != null)
                        {
                            // This is the case where a single container is selected
                            SynchronizeWithCurrentItem(container.RibbonGalleryCategory, item);
                        }
                        else
                        {
                            // This is the case where the selected item is directly
                            // set in which case we need to additionally find the category
                            // that contains it to perform the currency synchronization
                            SynchronizeWithCurrentItem();
                        }
                    }
                }
 
                // Select the container and synchronize currency
                if (isSelected && container != null && !_selectedContainers.Contains(container))
                {
                    _selectedContainers.Add(container);
                    container.IsSelected = true;
                    container.OnSelected(new RoutedEventArgs(RibbonGalleryItem.SelectedEvent, container));
                }
            }
            finally
            {
                IsSelectionChangeActive = false;
            }
 
            if (selectedItemChanged)
            {
                RoutedPropertyChangedEventArgs<object> args = new RoutedPropertyChangedEventArgs<object>(oldItem, isSelected ? newItem : null, SelectionChangedEvent);
                this.OnSelectionChanged(args);
            }
        }
 
        // To find out if the RibbonGallery contains the specified item.
        // If ignoreItemContainerGeneratorStatus is true then skip the
        // container generation check.
        private bool ContainsItem(
            object item,
            bool ignoreItemContainerGeneratorStatus,
            out RibbonGalleryCategory category,
            out RibbonGalleryItem galleryItem)
        {
            category = null;
            galleryItem = null;
            int index = -1;
 
            if (!ignoreItemContainerGeneratorStatus &&
                ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
            {
                return true;
            }
 
            foreach (object current in Items)
            {
                category = ItemContainerGenerator.ContainerFromItem(current) as RibbonGalleryCategory;
                if (category != null)
                {
                    index = category.Items.IndexOf(item);
                    if (index > -1)
                    {
                        galleryItem = category.ItemContainerGenerator.ContainerFromIndex(index) as RibbonGalleryItem;
                        break;
                    }
                    category = null;
                }
            }
 
            return index > -1;
        }
 
        // To find out if the RibbonGallery contains the specified value
        // in any of the available items. If ignoreItemContainerGeneratorStatus
        // is true then skip the container generation check.
        private bool ContainsValue(
            object value,
            bool ignoreItemContainerGeneratorStatus,
            out object item,
            out RibbonGalleryCategory category,
            out RibbonGalleryItem galleryItem)
        {
            item = null;
            category = null;
            galleryItem = null;
 
            if (!ignoreItemContainerGeneratorStatus &&
                ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
            {
                return true;
            }
 
            ContentControl dummyElement = new ContentControl();
 
            foreach (object current in Items)
            {
                category = ItemContainerGenerator.ContainerFromItem(current) as RibbonGalleryCategory;
                if (category != null)
                {
                    for (int index=0; index<category.Items.Count; index++)
                    {
                        item = category.Items[index];
                        object itemValue = GetSelectableValueFromItem(item, dummyElement);
                        if (VerifyEqual(value, itemValue))
                        {
                            galleryItem = category.ItemContainerGenerator.ContainerFromIndex(index) as RibbonGalleryItem;
                            return true;
                        }
                        item = null;
                    }
                    category = null;
                }
            }
 
            return false;
        }
 
        internal object GetSelectableValueFromItem(object item)
        {
            return GetSelectableValueFromItem(item, new ContentControl());
        }
 
        // Find out the value of the item using SelectedValuePath.
        // If there is no SelectedValuePath then item itself is
        // it's value or the innerText in case of XML node.
        private object GetSelectableValueFromItem(object item, ContentControl dummyElement)
        {
            bool useXml = item is XmlNode;
            Binding itemBinding = new Binding();
            itemBinding.Source = item;
            if (useXml)
            {
                itemBinding.XPath = SelectedValuePath;
                itemBinding.Path = new PropertyPath("/InnerText");
            }
            else
            {
                itemBinding.Path = new PropertyPath(SelectedValuePath);
            }
 
            // optimize for case where there is no SelectedValuePath (meaning
            // that the value of the item is the item itself, or the InnerText
            // of the item)
            if (string.IsNullOrEmpty(SelectedValuePath))
            {
                // when there's no SelectedValuePath, the binding's Path
                // is either empty (CLR) or "/InnerText" (XML)
                string path = itemBinding.Path.Path;
                Debug.Assert(String.IsNullOrEmpty(path) || path == "/InnerText");
                if (string.IsNullOrEmpty(path))
                {
                    // CLR - item is its own selected value
                    return item;
                }
                else
                {
                    return GetInnerText(item);
                }
            }
 
            dummyElement.SetBinding(ContentControl.ContentProperty, itemBinding);
            return dummyElement.Content;
        }
 
        private static object GetInnerText(object item)
        {
            XmlNode node = item as XmlNode;
 
            if (node != null)
            {
                return node.InnerText;
            }
            else
            {
                return null;
            }
        }
 
        internal static bool VerifyEqual(object knownValue, object itemValue)
        {
            return Object.Equals(knownValue, itemValue);
        }
 
        private void MoveCurrentTo(CollectionView cv, object item)
        {
            if (cv != null && IsSynchronizedWithCurrentItemInternal)
            {
                cv.MoveCurrentTo(item);
            }
        }
 
        private void MoveCurrentToPosition(CollectionView cv, int position)
        {
            if (cv != null && IsSynchronizedWithCurrentItemInternal)
            {
                cv.MoveCurrentToPosition(position);
            }
        }
 
        internal Collection<RibbonGalleryItem> SelectedContainers
        {
            get { return _selectedContainers; }
        }
 
        internal RibbonGalleryCategory SelectedCategory
        {
            get
            {
                if (_selectedContainers.Count > 0)
                {
                    return _selectedContainers[0].RibbonGalleryCategory;
                }
 
                return null;
            }
 
        }
 
        internal bool IsSelectionChangeActive
        {
            get { return _bits[(int)Bits.IsSelectionChangeActive]; }
            set { _bits[(int)Bits.IsSelectionChangeActive] = value; }
        }
 
        #endregion Selection
 
        #region Highlight
 
        /// <summary>
        ///     The DependencyProperty for the <see cref="HighlightedItem"/> property.
        ///     Default Value: null
        /// </summary>
        private static readonly DependencyPropertyKey HighlightedItemPropertyKey =
                    DependencyProperty.RegisterReadOnly(
                                        "HighlightedItem",
                                        typeof(object),
                                        typeof(RibbonGallery),
                                        new FrameworkPropertyMetadata(
                                                new PropertyChangedCallback(OnHighlightedItemChangedPrivate),
                                                new CoerceValueCallback(CoerceHighlightedItem)));
 
 
        /// <summary>
        ///     The DependencyProperty for the HighlightedItem property.
        ///     Flags:              None
        ///     Default Value:      null
        /// </summary>
        public static readonly DependencyProperty HighlightedItemProperty =
                HighlightedItemPropertyKey.DependencyProperty;
 
 
        /// <summary>
        ///     Specifies the highlighted item.
        /// </summary>
        public object HighlightedItem
        {
            get { return GetValue(HighlightedItemProperty); }
            internal set { SetValue(HighlightedItemPropertyKey, value); }
        }
 
        // To force Coercion even if the HighlightedItem didn't change, useful in case of when ItemsCollection changes.
        internal void ForceCoerceHighlightedItem()
        {
            try
            {
                ShouldForceCoerceHighlightedItem = true;
                CoerceValue(HighlightedItemProperty);
            }
            finally
            {
                ShouldForceCoerceHighlightedItem = false;
            }
        }
 
        private static object CoerceHighlightedItem(DependencyObject d, object value)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            if (!gallery.IsHighlightChangeActive)
            {
                object oldItem = gallery.HighlightedItem;
                object newItem = value;
 
                if (!VerifyEqual(oldItem, newItem))
                {
                    if (newItem != null)
                    {
                        RibbonGalleryCategory category = null;
                        RibbonGalleryItem galleryItem = null;
 
                        // If the newItem doesn't exist in RibbonGallery under any category then return UnsetValue
                        // so as not to change existing value
                        bool ignoreItemContainerGeneratorStatus = false;
                        if (!gallery.ContainsItem(newItem, ignoreItemContainerGeneratorStatus, out category, out galleryItem))
                        {
                            return DependencyProperty.UnsetValue;
                        }
 
                        // Changes the highlight to newItem
                        gallery.ChangeHighlight(newItem, galleryItem, true);
                    }
                    else
                    {
                        // Dehighlight oldItem
                        gallery.ChangeHighlight(oldItem, null, false);
                    }
                }
                else if (gallery.ShouldForceCoerceHighlightedItem)
                {
                    RibbonGalleryCategory category = null;
                    RibbonGalleryItem galleryItem = null;
 
                    // This block is called when ItemCollection changes either at the Gallery or the Category levels
                    // to handle the case that a previously HighlightedItem is removed or replaced.
                    bool ignoreItemContainerGeneratorStatus = true;
                    if (!gallery.ContainsItem(newItem, ignoreItemContainerGeneratorStatus, out category, out galleryItem))
                    {
                        // Dehighlight oldItem
                        value = null;
                        gallery.ChangeHighlight(oldItem, null, false);
                    }
                }
            }
 
            return value;
        }
 
        private static void OnHighlightedItemChangedPrivate(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.OnHighlightedItemChanged(e);
        }
 
        protected virtual void OnHighlightedItemChanged(DependencyPropertyChangedEventArgs e)
        {
            if (HighlightChanged != null)
            {
                HighlightChanged(this, EventArgs.Empty);
            }
 
            if (ShouldExecuteCommand && e.OldValue != null)
            {
                CommandHelpers.InvokeCommandSource(CommandParameter, PreviewCommandParameter, this, CommandOperation.CancelPreview);
            }
 
            if (ShouldExecuteCommand && e.NewValue != null)
            {
                // Fire the Preview operation on a Dispatcher callback to allow the
                // PreviewCommandParameter's Binding to be updated to match the HighlightedItem
                Dispatcher.BeginInvoke(DispatcherPriority.Send, (DispatcherOperationCallback)delegate(object unused)
                {
                    CommandHelpers.InvokeCommandSource(CommandParameter, PreviewCommandParameter, this, CommandOperation.Preview);
                    return null;
                }, null);
            }
        }
 
        // Update all highlighting properties viz.
        // - HighlightedItem
        // - IsHighlighted
        // - HighlightedContainer
        internal void ChangeHighlight(object item, RibbonGalleryItem container, bool isHighlighted)
        {
            if (IsHighlightChangeActive)
            {
                return;
            }
 
            try
            {
                IsHighlightChangeActive = true;
 
                if (_highlightedContainer != null)
                {
                    _highlightedContainer.IsHighlighted = false;
                }
 
                if (!isHighlighted)
                {
                    _highlightedContainer = null;
                    HighlightedItem = null;
                }
                else
                {
                    _highlightedContainer = container;
                    HighlightedItem = item;
 
                    if (container != null)
                    {
                        container.IsHighlighted = true;
                    }
                }
            }
            finally
            {
                IsHighlightChangeActive = false;
            }
        }
 
        internal event EventHandler HighlightChanged;
 
        internal RibbonGalleryCategory HighlightedCategory
        {
            get
            {
                if (_highlightedContainer != null)
                {
                    return _highlightedContainer.RibbonGalleryCategory;
                }
 
                return null;
            }
 
        }
 
        internal RibbonGalleryItem HighlightedContainer
        {
            get { return _highlightedContainer; }
        }
 
        private bool IsHighlightChangeActive
        {
            get { return _bits[(int)Bits.IsHighlightChangeActive]; }
            set { _bits[(int)Bits.IsHighlightChangeActive] = value; }
        }
 
        private bool ShouldForceCoerceHighlightedItem
        {
            get { return _bits[(int)Bits.ShouldForceCoerceHighlightedItem]; }
            set { _bits[(int)Bits.ShouldForceCoerceHighlightedItem] = value; }
        }
 
        #endregion Highlight
 
        #region Filtering
 
        /// <summary>
        ///   Command fired when the user changes the current Gallery filter.
        /// </summary>
        public static RoutedCommand FilterCommand { get; private set; }
 
        // We only want to execute the FilterCommand if we are in the auto-filtering case and we have _filterMenuButton available.
        private static void FilterCanExecute(object sender, CanExecuteRoutedEventArgs args)
        {
            RibbonGallery rg = sender as RibbonGallery;
            if (rg != null &&
                rg.CanUserFilter &&
                rg._filterMenuButton != null &&
                rg.FilterPaneContent == null &&
                rg.FilterPaneContentTemplate == null)
            {
                args.CanExecute = true;
            }
        }
 
        private static void FilterExecuted(object sender, ExecutedRoutedEventArgs args)
        {
            RibbonGallery rg = (RibbonGallery)sender;
            rg.CurrentFilter = args.Parameter;
            args.Handled = true;
        }
 
        /// <summary>
        ///   Gets/Sets a value indicating whether the Gallery is filterable.
        /// </summary>
        public bool CanUserFilter
        {
            get { return (bool)GetValue(CanUserFilterProperty); }
            set { SetValue(CanUserFilterProperty, value); }
        }
 
#if IN_RIBBON_GALLERY
        private static object CoerceCanUserFilter(DependencyObject d, object baseValue)
        {
            RibbonGallery me = (RibbonGallery)d;
            // Don't allow Filter when in InRibbonGalleryMode
            if (me.IsInInRibbonGalleryMode())
            {
                return false;
            }
            return baseValue;
        }
#endif
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for CanUserFilter.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CanUserFilterProperty =
#if IN_RIBBON_GALLERY
            DependencyProperty.Register("CanUserFilter", typeof(bool), typeof(RibbonGallery), new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceCanUserFilter)));
#else
            DependencyProperty.Register("CanUserFilter", typeof(bool), typeof(RibbonGallery), new FrameworkPropertyMetadata(false));
#endif
 
        /// <summary>
        ///   Gets/Sets the FilterItemContainerStyle.  This is the container style for the filter items generated from RibbonGalleryCategory Headers.
        /// </summary>
        public Style FilterItemContainerStyle
        {
            get { return (Style)GetValue(FilterItemContainerStyleProperty); }
            set { SetValue(FilterItemContainerStyleProperty, value); }
        }
 
        /// <summary>
        ///   Style to allow customization of the filter items containers.
        /// </summary>
        public static readonly DependencyProperty FilterItemContainerStyleProperty =
            DependencyProperty.Register("FilterItemContainerStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(OnFilterItemContainerStyleChanged));
 
        /// <summary>
        ///   Gets/Sets the AllFilterItemContainerStyle.  This is the container style for the "All" filter.
        /// </summary>
        public Style AllFilterItemContainerStyle
        {
            get { return (Style)GetValue(AllFilterItemContainerStyleProperty); }
            set { SetValue(AllFilterItemContainerStyleProperty, value); }
        }
 
        /// <summary>
        ///   Style to allow customization of the "All" filter item's container.
        /// </summary>
        public static readonly DependencyProperty AllFilterItemContainerStyleProperty =
            DependencyProperty.Register("AllFilterItemContainerStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(OnFilterItemContainerStyleChanged));
 
        private static void OnFilterItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            // Reapply the FilterItemContainerStyleSelector coercion.
            gallery.CoerceValue(RibbonGallery.FilterItemContainerStyleSelectorProperty);
            gallery.CoerceValue(RibbonGallery.CurrentFilterStyleProperty);
            gallery.SetHeaderBindingForCurrentFilterItem();
        }
 
        /// <summary>
        ///   Gets/Sets the FilterItemContainerStyleSelector.
        /// </summary>
        public StyleSelector FilterItemContainerStyleSelector
        {
            get { return (StyleSelector)GetValue(FilterItemContainerStyleSelectorProperty); }
            set { SetValue(FilterItemContainerStyleSelectorProperty, value); }
        }
 
        /// <summary>
        ///   StyleSelector to allow customization of the filter item containers.
        /// </summary>
        public static readonly DependencyProperty FilterItemContainerStyleSelectorProperty =
            DependencyProperty.Register("FilterItemContainerStyleSelector",
                                        typeof(StyleSelector),
                                        typeof(RibbonGallery),
                                        new FrameworkPropertyMetadata(OnFilterItemContainerStyleSelectorChanged, OnCoerceFilterItemContainerStyleSelector));
 
        private static void OnFilterItemContainerStyleSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.CoerceValue(RibbonGallery.CurrentFilterStyleProperty);
            gallery.SetHeaderBindingForCurrentFilterItem();
        }
 
        // If FilterItemContainerStyle is specified but FilterItemContainerStyleSelector is unspecified, then we supply our own StyleSelector for FilterItemContainerStyleSelector.
        // This allows a user-supplied FilterItemContainerStyle to reskin the filter items without affecting the "All" item.
        //
        // We also coerce when both FilterItemContainerStyle & FilterItemContainerStyleSelector are set so that FilterItemContainerStyle dominates.  Thus, we need to
        // call this coercion whenever the FilterItemContainerStyle property (or AllFilterItemContainerStyle) changes.
        //
        // Similarly, if AllFilterItemContainerStyle is set, the default StyleSelector dominates FilterItemContainerStyleSelector.
        private static object OnCoerceFilterItemContainerStyleSelector(DependencyObject d, object baseValue)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            if (baseValue == null ||
                gallery.FilterItemContainerStyle != null ||
                gallery.AllFilterItemContainerStyle != null)
            {
                return new RibbonGalleryDefaultFilterItemContainerStyleSelector(gallery);
            }
 
            return baseValue;
        }
 
        private class RibbonGalleryDefaultFilterItemContainerStyleSelector : StyleSelector
        {
            private RibbonGallery _gallery;
 
            internal RibbonGalleryDefaultFilterItemContainerStyleSelector(RibbonGallery inputGallery) : base()
            {
                _gallery = inputGallery;
            }
 
            public override Style SelectStyle(object item, DependencyObject container)
            {
                if (Object.ReferenceEquals(item, _allFilter))
                {
                    if (_gallery.AllFilterItemContainerStyle != null)
                    {
                        return _gallery.AllFilterItemContainerStyle;
                    }
                }
                else
                {
                    if (_gallery.FilterItemContainerStyle != null)
                    {
                        return _gallery.FilterItemContainerStyle;
                    }
                }
 
                return base.SelectStyle(item, container);
            }
        }
 
        /// <summary>
        ///   Gets/Sets the FilterMenuButtonStyle.
        /// </summary>
        public Style FilterMenuButtonStyle
        {
            get { return (Style)GetValue(FilterMenuButtonStyleProperty); }
            set { SetValue(FilterMenuButtonStyleProperty, value); }
        }
 
        /// <summary>
        ///   Style to allow customization of the Filter menu button.
        /// </summary>
        public static readonly DependencyProperty FilterMenuButtonStyleProperty =
            DependencyProperty.Register("FilterMenuButtonStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(null));
 
        /// <summary>
        ///   Gets/Sets the FilterPaneContent.
        /// </summary>
        public object FilterPaneContent
        {
            get { return (object)GetValue(FilterPaneContentProperty); }
            set { SetValue(FilterPaneContentProperty, value); }
        }
 
        /// <summary>
        ///   Object to allow customization of the Filter pane.
        /// </summary>
        public static readonly DependencyProperty FilterPaneContentProperty =
            DependencyProperty.Register("FilterPaneContent", typeof(object), typeof(RibbonGallery), new FrameworkPropertyMetadata(null));
 
        /// <summary>
        ///   Gets/Sets the FilterPaneContentTemplate.
        /// </summary>
        public DataTemplate FilterPaneContentTemplate
        {
            get { return (DataTemplate)GetValue(FilterPaneContentTemplateProperty); }
            set { SetValue(FilterPaneContentTemplateProperty, value); }
        }
 
        /// <summary>
        ///   DataTemplate to allow customization of the Filter pane.
        /// </summary>
        public static readonly DependencyProperty FilterPaneContentTemplateProperty =
            DependencyProperty.Register("FilterPaneContentTemplate", typeof(DataTemplate), typeof(RibbonGallery), new FrameworkPropertyMetadata(null));
 
        /// <summary>
        ///   Gets/Sets the FilterItemTemplate.  This specifies the template for filter items generated from RibbonGalleryCategory Headers.
        /// </summary>
        public DataTemplate FilterItemTemplate
        {
            get { return (DataTemplate)GetValue(FilterItemTemplateProperty); }
            set { SetValue(FilterItemTemplateProperty, value); }
        }
 
        /// <summary>
        ///   DataTemplate to allow customization of the filter items.
        /// </summary>
        public static readonly DependencyProperty FilterItemTemplateProperty =
            DependencyProperty.Register("FilterItemTemplate", typeof(DataTemplate), typeof(RibbonGallery), new FrameworkPropertyMetadata(OnFilterItemTemplateChanged));
 
        /// <summary>
        ///   Gets/Sets the AllFilterItemTemplate.  This specifies the template for the "All" filter item.
        /// </summary>
        public DataTemplate AllFilterItemTemplate
        {
            get { return (DataTemplate)GetValue(AllFilterItemTemplateProperty); }
            set { SetValue(AllFilterItemTemplateProperty, value); }
        }
 
        /// <summary>
        ///   DataTemplate to allow customization of the "All" filter item.
        /// </summary>
        public static readonly DependencyProperty AllFilterItemTemplateProperty =
            DependencyProperty.Register("AllFilterItemTemplate", typeof(DataTemplate), typeof(RibbonGallery), new FrameworkPropertyMetadata(OnFilterItemTemplateChanged));
 
        private static void OnFilterItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            // Reapply the FilterItemTemplateSelector coercion.
            gallery.CoerceValue(RibbonGallery.FilterItemTemplateSelectorProperty);
            gallery.CoerceValue(RibbonGallery.CurrentFilterTemplateProperty);
        }
 
        /// <summary>
        ///   Gets/Sets the FilterItemTemplateSelector.
        /// </summary>
        public DataTemplateSelector FilterItemTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(FilterItemTemplateSelectorProperty); }
            set { SetValue(FilterItemTemplateSelectorProperty, value); }
        }
 
        /// <summary>
        ///   DataTemplateSelector to allow customization of the filter items.
        /// </summary>
        public static readonly DependencyProperty FilterItemTemplateSelectorProperty =
            DependencyProperty.Register("FilterItemTemplateSelector",
                                        typeof(DataTemplateSelector),
                                        typeof(RibbonGallery),
                                        new FrameworkPropertyMetadata(OnFilterItemTemplateSelectorChanged, OnCoerceFilterItemTemplateSelector));
 
        private static void OnFilterItemTemplateSelectorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.CoerceValue(RibbonGallery.CurrentFilterTemplateProperty);
        }
 
        // If FilterItemTemplate is specified but FilterItemTemplateSelector is unspecified, then we supply our own DataTemplateSelector for FilterItemTemplateSelector.
        // This allows a user-supplied FilterItemTemplate to reskin the filter items without affecting the "All" item.
        //
        // We also coerce when both FilterItemTemplate & FilterItemTemplateSelector are set so that FilterItemTemplate dominates.  Thus, we need to
        // call this coercion whenever the FilterItemTemplate (or AllFilterItemTemplate) property changes.
        //
        // Similarly, if AllFilterItemTemplate is set, then the default DataTemplateSelector dominates FilterItemTemplateSelector.
        private static object OnCoerceFilterItemTemplateSelector(DependencyObject d, object baseValue)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            if (baseValue == null ||
                gallery.FilterItemTemplate != null ||
                gallery.AllFilterItemTemplate != null)
            {
                return new RibbonGalleryDefaultFilterItemTemplateSelector(gallery);
            }
 
            return baseValue;
        }
 
        private class RibbonGalleryDefaultFilterItemTemplateSelector : DataTemplateSelector
        {
            private RibbonGallery _gallery;
 
            internal RibbonGalleryDefaultFilterItemTemplateSelector(RibbonGallery inputGallery) : base()
            {
                _gallery = inputGallery;
            }
 
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
                if (Object.ReferenceEquals(item, _allFilter))
                {
                    if (_gallery.AllFilterItemTemplate != null)
                    {
                        return _gallery.AllFilterItemTemplate;
                    }
                }
                else
                {
                    if (_gallery.FilterItemTemplate != null)
                    {
                        return _gallery.FilterItemTemplate;
                    }
                }
 
                return base.SelectTemplate(item, container);
            }
        }
 
        internal ContentPresenter FilterContentPane
        {
            get
            {
                return _filterContentPane;
            }
        }
 
        internal RibbonFilterMenuButton FilterMenuButton
        {
            get
            {
                return _filterMenuButton;
            }
        }
 
        #endregion Filtering
 
        #region ContainerGeneration
 
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is RibbonGalleryCategory;
        }
 
        protected override DependencyObject GetContainerForItemOverride()
        {
            return new RibbonGalleryCategory();
        }
 
        /// <summary>
        ///   Called when the container is being attached to the parent ItemsControl
        /// </summary>
        /// <param name="element"></param>
        /// <param name="item"></param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            RibbonGalleryCategory category = (RibbonGalleryCategory)element;
            category.RibbonGallery = this;
 
            if (SelectedValue != null && SelectedItem == null)
            {
                // Synchronize SelectedItem with SelectedValue
                ForceCoerceSelectedValue();
            }
 
            if (HasItems)
            {
                object selectedItem = SelectedItem;
                for (int index = 0; index < category.Items.Count; index++)
                {
                    RibbonGalleryItem galleryItem = category.ItemContainerGenerator.ContainerFromIndex(index) as RibbonGalleryItem;
                    if (galleryItem != null)
                    {
                        // Set IsSelected to true on GalleryItems that match the SelectedItem
                        if (selectedItem != null)
                        {
                            if (VerifyEqual(selectedItem, category.Items[index]))
                            {
                                galleryItem.IsSelected = true;
                            }
                        }
                        else if (galleryItem.IsSelected)
                        {
                            // If a GalleryItem is marked IsSelected true then synchronize SelectedItem with it
#if RIBBON_IN_FRAMEWORK
                            SetCurrentValue(SelectedItemProperty, category.Items[index]);
#else
                            SelectedItem = category.Items[index];
#endif
                        }
                    }
                }
            }
 
            // copy templates and styles from this ItemsControl
            var itemTemplate = RibbonHelper.GetValueAndValueSource(category, ItemsControl.ItemTemplateProperty);
            var itemTemplateSelector = RibbonHelper.GetValueAndValueSource(category, ItemsControl.ItemTemplateSelectorProperty);
            var itemStringFormat = RibbonHelper.GetValueAndValueSource(category, ItemsControl.ItemStringFormatProperty);
            var itemContainerStyle = RibbonHelper.GetValueAndValueSource(category, ItemsControl.ItemContainerStyleProperty);
            var itemContainerStyleSelector = RibbonHelper.GetValueAndValueSource(category, ItemsControl.ItemContainerStyleSelectorProperty);
            var alternationCount = RibbonHelper.GetValueAndValueSource(category, ItemsControl.AlternationCountProperty);
            var itemBindingGroup = RibbonHelper.GetValueAndValueSource(category, 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(
                category,
                this,
                itemTemplate,
                itemTemplateSelector,
                itemStringFormat,
                itemContainerStyle,
                itemContainerStyleSelector,
                alternationCount,
                itemBindingGroup,
                null,
                null,
                null);
        }
 
        /// <summary>
        ///   Called when the container is being detached from the parent ItemsControl
        /// </summary>
        /// <param name="element"></param>
        /// <param name="item"></param>
        protected override void ClearContainerForItemOverride(DependencyObject element, object item)
        {
            RibbonGalleryCategory category = (RibbonGalleryCategory)element;
 
            for (int index = 0; index < category.Items.Count; index++)
            {
                RibbonGalleryItem galleryItem = category.ItemContainerGenerator.ContainerFromIndex(index) as RibbonGalleryItem;
                if (galleryItem != null)
                {
                    object dataItem = category.Items[index];
 
                    // Turn off selection and highlight on GalleryItems that are being cleared.
                    // Note that we directly call Change[Selection/Highlight] instead of setting
                    // Is[Selected/Highlighted] because we aren't able to get ItemFromContainer
                    // in OnIs[Selected/Highlighted]Changed because the ItemContainerGenerator
                    // has already detached this container.
                    if (galleryItem.IsHighlighted)
                    {
                        galleryItem.RibbonGallery.ChangeHighlight(dataItem, galleryItem, false);
                    }
                    if (galleryItem.IsSelected)
                    {
                        galleryItem.RibbonGallery.ChangeSelection(dataItem, galleryItem, false);
                    }
                }
            }
 
            category.RibbonGallery = null;
            base.ClearContainerForItemOverride(element, item);
        }
 
        #endregion ContainerGeneration
 
        #region DropDownContainer
 
        /// <summary>
        ///     True if this is the current "Selection" of its parent.
        ///     A gallery is selected when Keyboard focus is within or mouse enters.
        ///
        ///     We use the Ribbon prefix to disambiguate this property from MenuItem.IsSelected.
        /// </summary>
        internal bool RibbonIsSelected
        {
            get { return (bool)GetValue(RibbonIsSelectedProperty); }
            set { SetValue(RibbonIsSelectedProperty, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for RibbonIsSelected property.
        /// </summary>
        internal static readonly DependencyProperty RibbonIsSelectedProperty = RibbonMenuItem.RibbonIsSelectedProperty.AddOwner(
                typeof(RibbonGallery),
                new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnRibbonIsSelectedChanged)));
 
        private static void OnRibbonIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RibbonGallery)d).RaiseEvent(new RoutedPropertyChangedEventArgs<bool>((bool)e.OldValue, (bool)e.NewValue, RibbonMenuButton.RibbonIsSelectedChangedEvent));
        }
 
        /// <summary>
        /// Called when the focus changes within the subtree
        /// </summary>
        protected override void OnIsKeyboardFocusWithinChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnIsKeyboardFocusWithinChanged(e);
 
            if (IsKeyboardFocusWithin)
            {
                if (!RibbonIsSelected)
                {
                    // If an item within us got focus (probably programatically), we need to become selected
                    RibbonIsSelected = true;
                }
 
#if IN_RIBBON_GALLERY
                // When keyboard-navigating into an InRibbonGallery, transfer focus to PartToggleButton.  When
                // IsInInRibbonMode is true, only PartToggleButton gets focus and RibbonGalleryItems are skipped.
                InRibbonGallery parentInRibbonGallery = ParentInRibbonGallery;
                if (parentInRibbonGallery != null &&
                    parentInRibbonGallery.IsInInRibbonMode)
                {
                    RibbonToggleButton partToggleButton = parentInRibbonGallery.PartToggleButton;
                    if (partToggleButton != null)
                    {
                        Keyboard.Focus(partToggleButton);
                    }
                }
#endif
            }
        }
 
        #endregion
 
        #region CollectionChange
 
        private void RepopulateCategoryFilters()
        {
            _categoryFilters.Clear();
 
            // Add the "All" filter and make this the current filter.
            _categoryFilters.Add(_allFilter);
 
            // Search Items collection for categories.
            for (int i = 0; i < this.Items.Count; i++)
            {
                RibbonGalleryCategory category;
                object filterToAdd;
 
                // If this item is a RibbonGalleryCategory, then add its Header to the filters collection.
                // Otherwise add the item itself.
                if (this.Items[i] is RibbonGalleryCategory)
                {
                    category = (RibbonGalleryCategory)this.Items[i];
                    filterToAdd = category.Header;
                }
                else
                {
                    category = (RibbonGalleryCategory)this.ItemContainerGenerator.ContainerFromIndex(i);
                    filterToAdd = this.Items[i];
                }
 
                // RibbonGalleryCategories that omit Header are omitted from the filters collection.
                // If category.Header is a string, make sure it is non-empty as well.
                if ((category.Header is string && !String.IsNullOrEmpty((string)category.Header)) ||
                     category.Header != null)
                {
                    _categoryFilters.Add(filterToAdd);
                }
            }
 
            CurrentFilter = _allFilter;
        }
 
        private static void OnCurrentFilterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            object newFilter = e.NewValue;
 
            Debug.Assert(gallery.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated);
 
            for (int i = 0; i < gallery.Items.Count; i++)
            {
                RibbonGalleryCategory category;
                object dataToCompareAgainst;
 
                // If this item is a RibbonGalleryCategory, then we want to compare against its Header.
                // Otherwise compare against the item itself.
                if (gallery.Items[i] is RibbonGalleryCategory)
                {
                    category = (RibbonGalleryCategory)gallery.Items[i];
                    dataToCompareAgainst = category.Header;
                }
                else
                {
                    category = (RibbonGalleryCategory)gallery.ItemContainerGenerator.ContainerFromIndex(i);
                    dataToCompareAgainst = gallery.Items[i];
                }
 
                // Show the category if the current filter is the "All" filter or dataToCompareAgainst.
                if (Object.ReferenceEquals(newFilter, _allFilter) ||
                    Object.ReferenceEquals(newFilter, dataToCompareAgainst))
                {
                    category.Visibility = Visibility.Visible;
                }
                else
                {
                    category.Visibility = Visibility.Collapsed;
                }
            }
 
            gallery.CoerceValue(RibbonGallery.CurrentFilterStyleProperty);
            gallery.SetHeaderBindingForCurrentFilterItem();
            gallery.CoerceValue(RibbonGallery.CurrentFilterTemplateProperty);
        }
 
        /// <summary>
        ///   The "All" item in a RibbonGallery filter.  This is a localized string.
        ///
        ///   Custom FilterItemTemplateSelector or FilterItemContainerStyleSelector implementations can use this to
        ///   distinguish between the "All" filter item and normal filter items.
        /// </summary>
        public static object AllFilterItem
        {
            get { return _allFilter; }
        }
 
        /// <summary>
        ///   This method is invoked when the Items property changes.
        /// </summary>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Remove:
                case NotifyCollectionChangedAction.Reset:
                case NotifyCollectionChangedAction.Replace:
                    if (SelectedItem != null)
                    {
                        // Synchronize SelectedItem after Remove, Replace or Reset operations.
                        ForceCoerceSelectedItem();
                    }
                    if (HighlightedItem != null)
                    {
                        // Synchronize HighlightedItem after Remove, Replace or Reset operations.
                        ForceCoerceHighlightedItem();
                    }
                    break;
 
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Move:
                    break;
            }
        }
 
        #endregion CollectionChange
 
        #region CategoryTemplate
 
        /// <summary>
        ///     Equivalent of ItemTemplate.
        /// </summary>
        /// <remarks>
        ///     If this property has a non-null value, it will override the value
        ///     of ItemTemplate.
        /// </remarks>
        [BindableAttribute(true)]
        public DataTemplate CategoryTemplate
        {
            get { return (DataTemplate)GetValue(CategoryTemplateProperty); }
            set { SetValue(CategoryTemplateProperty, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for the CategoryStyle property.
        /// </summary>
        public static readonly DependencyProperty CategoryTemplateProperty =
            DependencyProperty.Register("CategoryTemplate", typeof(DataTemplate), typeof(RibbonGallery), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCategoryTemplateChanged)));
 
        private static void OnCategoryTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(ItemTemplateProperty);
        }
 
        // Always respect CategoryTemplate property over ItemTemplateProperty
        private static object OnCoerceItemTemplate(DependencyObject d, object baseValue)
        {
            if (!PropertyHelper.IsDefaultValue(d, RibbonGallery.CategoryTemplateProperty))
            {
                return d.GetValue(RibbonGallery.CategoryTemplateProperty);
            }
 
            return baseValue;
        }
 
        /// <summary>
        ///     Equivalent of ItemContainerStyle.
        /// </summary>
        /// <remarks>
        ///     If this property has a non-null value, it will override the value
        ///     of ItemContainerStyle.
        /// </remarks>
        public Style CategoryStyle
        {
            get { return (Style)GetValue(CategoryStyleProperty); }
            set { SetValue(CategoryStyleProperty, value); }
        }
 
        /// <summary>
        ///     DependencyProperty for the CategoryStyle property.
        /// </summary>
        public static readonly DependencyProperty CategoryStyleProperty =
            DependencyProperty.Register("CategoryStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnCategoryStyleChanged)));
 
        private static void OnCategoryStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            d.CoerceValue(ItemContainerStyleProperty);
        }
 
        // // Always respect CategoryStyleProperty property over ItemConatinerStyleProperty
        private static object OnCoerceItemContainerStyle(DependencyObject d, object baseValue)
        {
            if (!PropertyHelper.IsDefaultValue(d, RibbonGallery.CategoryStyleProperty))
            {
                return d.GetValue(RibbonGallery.CategoryStyleProperty);
            }
 
            return baseValue;
        }
 
        #endregion CategoryTemplate
 
        #region GalleryItemTemplate
 
        public DataTemplate GalleryItemTemplate
        {
            get { return (DataTemplate)GetValue(GalleryItemTemplateProperty); }
            set { SetValue(GalleryItemTemplateProperty, value); }
        }
 
        public static readonly DependencyProperty GalleryItemTemplateProperty =
            DependencyProperty.Register("GalleryItemTemplate", typeof(DataTemplate), typeof(RibbonGallery), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyGalleryItemTemplateOrStylePropertyChanged)));
 
        public Style GalleryItemStyle
        {
            get { return (Style)GetValue(GalleryItemStyleProperty); }
            set { SetValue(GalleryItemStyleProperty, value); }
        }
 
        public static readonly DependencyProperty GalleryItemStyleProperty =
                        DependencyProperty.Register("GalleryItemStyle", typeof(Style), typeof(RibbonGallery), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyGalleryItemTemplateOrStylePropertyChanged)));
 
        private static void OnNotifyGalleryItemTemplateOrStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
 
            for (int index = 0; index < gallery.Items.Count; index++)
            {
                RibbonGalleryCategory category = (RibbonGalleryCategory)gallery.ItemContainerGenerator.ContainerFromIndex(index);
 
                if (category != null)
                {
                    category.NotifyPropertyChanged(e);
                }
            }
        }
 
        #endregion GalleryItemTemplate
 
#if IN_RIBBON_GALLERY
        #region InRibbonGallery

        // Note this sometimes returns null even if the RibbonGallery is indeed the first gallery of an InRibbonGallery.
        // This returns null until the InRibbonGallery's template has been applied (including its ContentPresenter and ItemsPresenter)
        // and the InRibbonGallery.FirstGallery link has been established.
        internal InRibbonGallery ParentInRibbonGallery
        {
            get
            {
                ItemsControl parentItemsControl = ItemsControl.ItemsControlFromItemContainer(this);
                InRibbonGallery parentInRibbonGallery = null;
 
                if (parentItemsControl != null)
                {
                    parentInRibbonGallery = parentItemsControl as InRibbonGallery;
                }
                else
                {
                    // When an InRibbonGallery's children are specified in XAML, they are part of the logical tree
                    // and it is sufficient to call ItemsControl.ItemsControlFromItemContainer(this) to find the
                    // InRibbonGallery that is the logical parent.
                    // However, in the MVVM scenario we are not part of the logical tree.  When IsDropDownOpen == false,
                    // FirstGallery is hosted in a ContentPresenter and must walk the visual tree to determine the
                    // parent InRibbonGallery.  This will not happen when IsDropDownOpen == true and we are hosted
                    // in a RibbonMenuItemsPanel, because ItemsControl.ItemsControlFromItemContainer(this) also covers
                    // this scenario.
                    ContentPresenter parentContentPresenter = VisualTreeHelper.GetParent(this) as ContentPresenter;
                    if (parentContentPresenter != null)
                    {
                        parentInRibbonGallery = parentContentPresenter.TemplatedParent as InRibbonGallery;
                    }
                }
 
                if (parentInRibbonGallery != null &&
                    parentInRibbonGallery.FirstGallery == this)
                {
                    return parentInRibbonGallery;
                }
 
                return null;
            }
        }
 
        // Check if the Gallery is hosted in and InRibbonGallery and is in InRibbon mode.
        internal bool IsInInRibbonGalleryMode()
        {
            InRibbonGallery parentInRibbonGallery = ParentInRibbonGallery;
 
            return parentInRibbonGallery != null &&
                   !parentInRibbonGallery.IsCollapsed &&
                   !parentInRibbonGallery.IsDropDownOpen;
        }
 
        #endregion
#endif
 
        #region Automation
 
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new RibbonGalleryAutomationPeer(this);
        }
 
        #endregion Automation
 
        #region Input Handling
 
        // The RibbonGallery tracks the position of the mouse relative to
        // itself in order to differentiate cases where the mouse move event
        // is generated as an artifact of elements moving (eg. scrolling) vs
        // the mouse actually moving.  We track this point by listening to the
        // MouseEnter/MouseMove/MouseLeave events.  The pattern is for
        // RibbonGalleryItems to call DidMouseMove in response to the bubbling
        // MouseMove event, which should be routed through the
        // RibbonGalleryItems before it is routed through the RibbonGallery.
 
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            _localMousePosition = e.GetPosition(this);
            if (!RibbonIsSelected)
            {
                // If it's already focused, make sure it's also selected.
                RibbonIsSelected = true;
            }
 
            base.OnMouseEnter(e);
        }
 
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            _localMousePosition = null;
            if (RibbonIsSelected)
            {
                RibbonIsSelected = false;
            }
            base.OnMouseLeave(e);
        }
 
        private static void OnMouseMove(object sender, MouseEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)sender;
            gallery._localMousePosition = e.GetPosition(gallery);
        }
 
        internal bool DidMouseMove(MouseEventArgs e)
        {
            Debug.Assert(e.RoutedEvent == Mouse.MouseMoveEvent || e.RoutedEvent == Mouse.MouseLeaveEvent);
            Debug.Assert(_localMousePosition.HasValue);
 
            Point currentMousePosition = e.GetPosition(this);
            return currentMousePosition != _localMousePosition;
        }
 
        private Point? _localMousePosition;
 
        /// <summary>
        /// Gallery's ScrollViewer handles up/down arrow keys and marks the KeyDown event handled
        /// even if the next focusable element is not its descendant.
        /// The logic here enables navigation to outside the gallery
        /// (for e.g. from the last/first galleryItem to gallery's sibling MenuItem).
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            base.OnPreviewKeyDown(e);
 
            if (!e.Handled)
            {
                DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject;
                OnNavigationKeyDown(e, focusedElement);
            }
        }
 
        internal void OnNavigationKeyDown(KeyEventArgs e, DependencyObject focusedElement)
        {
            if (e.Key == Key.Down || e.Key == Key.Up)
            {
                FocusNavigationDirection direction = (e.Key == Key.Down) ? FocusNavigationDirection.Down : FocusNavigationDirection.Up;
                DependencyObject predictedFocus = null;
 
                if (focusedElement != null)
                {
                    // if predictedFocus is not a descendant of the Gallery._scrollViewer
                    // then handle the navigation
                    predictedFocus = RibbonHelper.PredictFocus(focusedElement, direction) as UIElement;
                    if (_scrollViewer != null && predictedFocus != null)
                    {
                        ScrollViewer predictedFocusAncestor = TreeHelper.FindVisualAncestor<ScrollViewer>(predictedFocus);
                        if (predictedFocusAncestor != _scrollViewer)
                        {
                            RibbonHelper.Focus(predictedFocus);
                            e.Handled = true;
                        }
                    }
                }
            }
            else if ((e.Key == Key.Left) == (FlowDirection == FlowDirection.LeftToRight))
            {
                UIElement predictedFocus = null;
                FocusNavigationDirection direction = (FlowDirection == FlowDirection.LeftToRight ? FocusNavigationDirection.Left : FocusNavigationDirection.Right);
                if (focusedElement != null)
                {
                    RibbonMenuItem menuItem = TreeHelper.FindAncestor(this, delegate(DependencyObject d) { return (d is RibbonMenuItem); }) as RibbonMenuItem;
                    if (menuItem != null)
                    {
                        predictedFocus = RibbonHelper.PredictFocus(focusedElement, direction) as UIElement;
                        bool callRibbonMenuItemHandler = false;
 
                        // If predicted focus of logical left is null or the focused element
                        // itself or if its origin with respect to screen is logically to the
                        // right of focused element, then assume that we are at the left boundary
                        // (any maybe trying to cycle) and hence make the
                        // ancestor RibbonMenuItem handle the event.
                        if (predictedFocus == null ||
                            predictedFocus == focusedElement)
                        {
                            callRibbonMenuItemHandler = true;
                        }
                        else
                        {
                            Point focusedOrigin = new Point();
                            UIElement focusContainer = RibbonHelper.GetContainingUIElement(focusedElement);
                            if (focusContainer != null)
                            {
                                focusedOrigin = focusContainer.PointToScreen(new Point());
                            }
                            Point predictedFocusedOrigin = predictedFocus.PointToScreen(new Point());
                            if ((FlowDirection == FlowDirection.LeftToRight && DoubleUtil.GreaterThan(predictedFocusedOrigin.X, focusedOrigin.X)) ||
                                (FlowDirection == FlowDirection.RightToLeft && DoubleUtil.LessThan(predictedFocusedOrigin.X, focusedOrigin.X)))
                            {
                                callRibbonMenuItemHandler = true;
                            }
                        }
 
                        if (callRibbonMenuItemHandler)
                        {
                            e.Handled = menuItem.HandleLeftKeyDown(e.OriginalSource as DependencyObject);
                        }
                    }
                }
            }
            else if (e.Key == Key.PageDown || e.Key == Key.PageUp)
            {
                FocusNavigationDirection direction = e.Key == Key.PageDown ? FocusNavigationDirection.Down : FocusNavigationDirection.Up;
 
                RibbonGalleryItem focusedGalleryItem = focusedElement as RibbonGalleryItem;
                if (focusedGalleryItem != null)
                {
                    RibbonGalleryItem highlightedGalleryItem;
                    e.Handled = RibbonHelper.NavigatePageAndHighlightRibbonGalleryItem(
                        this,
                        focusedGalleryItem,
                        direction,
                        out highlightedGalleryItem);
 
                    if (highlightedGalleryItem != null)
                    {
                        highlightedGalleryItem.Focus();
                        highlightedGalleryItem.BringIntoView();
                    }
                }
            }
        }
 
        private static void OnDismissPopupThunk(object sender, RibbonDismissPopupEventArgs e)
        {
            RibbonGallery ribbonGallery = (RibbonGallery)sender;
            ribbonGallery.OnDismissPopup(e);
        }
 
        private void OnDismissPopup(RibbonDismissPopupEventArgs e)
        {
            if (e.DismissMode == RibbonDismissPopupMode.Always)
            {
                // Stop popup dismissal if the original source
                // is from FilterPane.
                ContentPresenter filterPane = FilterContentPane;
                if (filterPane != null &&
                    RibbonHelper.IsAncestorOf(filterPane, e.OriginalSource as DependencyObject))
                {
                    e.Handled = true;
                }
            }
        }
 
        #endregion Input Handling
 
        #region Commanding
 
        /// <summary>
        ///   Gets or sets the Command that will be executed when the command source is invoked.
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
                    DependencyProperty.Register(
                            "Command",
                            typeof(ICommand),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
 
        /// <summary>
        ///   Gets or sets a user defined data value that can be passed to the command when it is executed.
        /// </summary>
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandParameterProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
                    DependencyProperty.Register(
                            "CommandParameter",
                            typeof(object),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCommandParameterChanged)));
 
        /// <summary>
        ///   Gets or sets a user defined data value that can be passed to the command when it is previewed.
        /// </summary>
        public object PreviewCommandParameter
        {
            get { return (object)GetValue(PreviewCommandParameterProperty); }
            set { SetValue(PreviewCommandParameterProperty, value); }
        }
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for PreviewCommandParameterProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty PreviewCommandParameterProperty =
                    DependencyProperty.Register(
                            "PreviewCommandParameter",
                            typeof(object),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(null));
 
        /// <summary>
        ///   Gets or sets the object that the command is being executed on.
        /// </summary>
        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }
 
        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandTargetProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandTargetProperty =
                    DependencyProperty.Register(
                            "CommandTarget",
                            typeof(IInputElement),
                            typeof(RibbonGallery),
                            new FrameworkPropertyMetadata(null));
 
        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            ICommand oldCommand = (ICommand)e.OldValue;
            ICommand newCommand = (ICommand)e.NewValue;
 
            if (oldCommand != null)
            {
                gallery.UnhookCommand(oldCommand);
            }
            if (newCommand != null)
            {
                gallery.HookCommand(newCommand);
            }
 
            RibbonHelper.OnCommandChanged(d, e);
        }
 
        private void HookCommand(ICommand command)
        {
#if RIBBON_IN_FRAMEWORK
            CanExecuteChangedEventManager.AddHandler(command, OnCanExecuteChanged);
#else
            _canExecuteChangedHandler = new EventHandler(OnCanExecuteChanged);
            command.CanExecuteChanged += _canExecuteChangedHandler;
#endif
            UpdateCanExecute();
        }
 
        private void UnhookCommand(ICommand command)
        {
#if RIBBON_IN_FRAMEWORK
            CanExecuteChangedEventManager.RemoveHandler(command, OnCanExecuteChanged);
#else
            if (_canExecuteChangedHandler != null)
            {
                command.CanExecuteChanged -= _canExecuteChangedHandler;
                _canExecuteChangedHandler = null;
            }
#endif
            UpdateCanExecute();
        }
 
        private void OnCanExecuteChanged(object sender, EventArgs e)
        {
            UpdateCanExecute();
        }
 
        private void UpdateCanExecute()
        {
            if (Command != null)
            {
                CanExecute = CommandHelpers.CanExecuteCommandSource(CommandParameter, this);
            }
            else
            {
                CanExecute = true;
            }
        }
 
        private static void OnCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)d;
            gallery.UpdateCanExecute();
        }
 
        /// <summary>
        ///     Fetches the value of the IsEnabled property
        /// </summary>
        /// <remarks>
        ///     The reason this property is overridden is so that RibbonGallery
        ///     can infuse the value for CanExecute into it.
        /// </remarks>
        protected override bool IsEnabledCore
        {
            get
            {
                return base.IsEnabledCore && CanExecute;
            }
        }
 
        private bool CanExecute
        {
            get { return _bits[(int)Bits.CanExecute]; ; }
            set
            {
                _bits[(int)Bits.CanExecute] = value;
                CoerceValue(IsEnabledProperty);
            }
        }
 
#if !RIBBON_IN_FRAMEWORK
        private EventHandler _canExecuteChangedHandler;
#endif
 
        #endregion Commanding
 
        #region RibbonControlService Properties
 
        /// <summary>
        ///     DependencyProperty for SmallImageSource property.
        /// </summary>
        public static readonly DependencyProperty SmallImageSourceProperty =
            RibbonControlService.SmallImageSourceProperty.AddOwner(typeof(RibbonGallery));
 
        /// <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 ToolTipTitle property.
        /// </summary>
        public static readonly DependencyProperty ToolTipTitleProperty =
            RibbonControlService.ToolTipTitleProperty.AddOwner(typeof(RibbonGallery), 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(RibbonGallery), 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(RibbonGallery), 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(RibbonGallery), 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(RibbonGallery), 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(RibbonGallery), 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); }
        }
 
        /// <summary>
        ///     DependencyProperty for Ribbon property.
        /// </summary>
        public static readonly DependencyProperty RibbonProperty =
            RibbonControlService.RibbonProperty.AddOwner(typeof(RibbonGallery));
 
        /// <summary>
        ///     This property is used to access visual style brushes defined on the Ribbon class.
        /// </summary>
        public Ribbon Ribbon
        {
            get { return RibbonControlService.GetRibbon(this); }
        }
 
        #endregion RibbonControlService Properties
 
        #region Scrolling
 
        /// <summary>
        ///  This method allows a particular item to be scrolled into view
        /// </summary>
        /// <remarks>
        ///   Note that this method currently does not support the virtualization
        /// </remarks>
        /// <param name="item"></param>
        public void ScrollIntoView(object item)
        {
            if (item != null)
            {
                // This is a fast path to scroll to the selected or the highlighted item
 
                if (VerifyEqual(item, SelectedItem) && _selectedContainers.Count > 0)
                {
                    _selectedContainers[0].BringIntoView();
                }
                else if (VerifyEqual(item, HighlightedItem) && _highlightedContainer != null)
                {
                    _highlightedContainer.BringIntoView();
                }
                else
                {
                    // If this a request for an arbitrary item then we need to
                    // find its container and then scroll that into view.
 
                    RibbonGalleryCategory category = null;
                    RibbonGalleryItem galleryItem = null;
 
                    bool ignoreItemContainerGeneratorStatus = true;
                    if (ContainsItem(item, ignoreItemContainerGeneratorStatus, out category, out galleryItem) &&
                        galleryItem != null)
                    {
                        galleryItem.BringIntoView();
                    }
                }
            }
        }
 
        private static void OnLoaded(object sender, RoutedEventArgs e)
        {
            // A RibbonGallery is always hosted within a drop down control
            // viz. RibbonMenuButton, RibbonComboBox, RibbonMenuItem, etc.
            // In all of these cases we wish to scroll the selected item
            // into view when the drop down first opens. Hence this clause
            // in RibbonGallery.
 
            RibbonGallery gallery = (RibbonGallery)sender;
            gallery.ScrollIntoView(gallery.SelectedItem);
        }
 
        private static void OnUnloaded(object sender, RoutedEventArgs e)
        {
            RibbonGallery gallery = (RibbonGallery)sender;
 
            // Invalidate these layout calculations, so that these are recalculated when drop down is opened.
            // We would need invalidation if any Items are removed from the parent RibbonMenuButton causing its constraint to change.
            gallery.IsArrangeWidthValid = false;
            gallery.IsMaxColumnWidthValid = false;
        }
 
        internal bool ShouldGalleryItemsAcquireFocus
        {
            get { return _bits[(int)Bits.ShouldGalleryItemsAcquireFocus]; }
            set { _bits[(int)Bits.ShouldGalleryItemsAcquireFocus] = value; }
        }
 
        internal bool HasHighlightChangedViaMouse
        {
            get { return _bits[(int)Bits.HighlightChangedViaMouse]; }
            set { _bits[(int)Bits.HighlightChangedViaMouse] = value; }
        }
 
        #endregion Scrolling
 
        #region QAT
 
        /// <summary>
        ///   DependencyProperty for QuickAccessToolBarId property.
        /// </summary>
        public static readonly DependencyProperty QuickAccessToolBarIdProperty =
            RibbonControlService.QuickAccessToolBarIdProperty.AddOwner(typeof(RibbonGallery));
 
        /// <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(RibbonGallery),
            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 Data
 
        private enum Bits
        {
            IsSelectionChangeActive = 0x1,
            IsHighlightChangeActive = 0x2,
            ShouldForceCoerceSelectedItem = 0x4,
            ShouldForceCoerceSelectedValue = 0x8,
            ShouldForceCoerceHighlightedItem = 0x10,
            IsSynchronizedWithCurrentItemInternal = 0x20,
            CanExecute = 0x40,
            ShouldExecuteCommand = 0x80,
            ShouldGalleryItemsAcquireFocus = 0x100,
            HighlightChangedViaMouse = 0x200,
            FilterMenuButtonTemplateIsBound = 0x400,
        }
 
        // Packed boolean information
        private BitVector32 _bits = new BitVector32((int)Bits.CanExecute | (int)Bits.ShouldExecuteCommand | (int)Bits.ShouldGalleryItemsAcquireFocus);
        private Collection<RibbonGalleryItem> _selectedContainers = new Collection<RibbonGalleryItem>();
        private RibbonGalleryItem _highlightedContainer;
 
        // Filtering
        private ObservableCollection<object> _categoryFilters = new ObservableCollection<object>();
        private static object _allFilter = Microsoft.Windows.Controls.SR.RibbonGallery_AllFilter;
        private const string _filterMenuButtonTemplatePartName = "PART_FilterMenuButton";
        private const string FilterContentPaneTemplatePartName = "PART_FilterContentPane";
        private const string ScrollViewerTemplatePartName = "PART_ScrollViewer";
        private const string ItemsHostPanelName = "ItemsHostPanel";
        private const string ItemsHostName = "ItemsPresenter";
        private ItemsPresenter _itemsPresenter;
        private RibbonFilterMenuButton _filterMenuButton;
        private ContentPresenter _filterContentPane;
        private ScrollViewer _scrollViewer;
 
        #endregion Data
    }
}