// TelerikWPFRibbonBuilder.cs
// Copyright (c) 2017 Bentley Systems, Incorporated. All Rights Reserved.

using Haestad.Support.Storage;
using Haestad.Support.Support;
using Haestad.Support.Library;
using Haestad.Support.Win32Api;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Telerik.Windows;
using Telerik.Windows.Controls;
using Telerik.Windows.Controls.RibbonView;

namespace Haestad.Framework.UI.Integration
{
    /// <summary>
    /// A class that exposes a uniform API for building up the default layout for a Telerik WPF ribbon without the
    /// need to use the form designer or XML.
    /// </summary>
    public class TelerikWPFRibbonBuilder : IImageProvider, IQueryRibbonTabIndex
    {
        #region Constructor
        /// <summary>
        /// Instantiates a new Telerik WPF Ribbon Builder with the specified level of keyboard navigation support.
        /// </summary>
        /// <param name="ribbon">An instance of the ribbon that is associated with the builder.</param>
        /// <param name="ribbonCallbacks">An implementation of IRibbonCallbacks that services requirements for Ribbon operations.</param>
        /// <param name="telerikTheme">An instance of the required Telerik theme.</param>
        /// <param name="applicationName">The localized name of the application. e.g., "Bentley WaterGEMS".</param>
        /// <param name="keyboardNavigationSupportFlags">A bitflag to indicate the required level of keyboard navigation support.</param>
        public TelerikWPFRibbonBuilder(RadRibbonView ribbon, IRibbonCallbacks ribbonCallbacks, Theme telerikTheme, string applicationName, KeyNavSupportFlags keyboardNavigationSupportFlags)
        {
            Ribbon = ribbon;
            RibbonCallbacks = ribbonCallbacks;
            TelerikTheme = telerikTheme;
            KeyboardNavigationSupportFlags = keyboardNavigationSupportFlags;

            InitializeRibbon(applicationName);
        }
        #endregion

        #region Events
        public event EventHandler<Telerik.Windows.RadRoutedEventArgs> ApplicationMenuOpenStateChanged;
        public event EventHandler<TabChangeEventArgs> RibbonTabChanged;
        #endregion

        #region Event Handlers

        #region Work arounds for Telerik Bugs
        // Note: <whar> Closes a Telerik RadContextMenu after a menu item it contains is clicked.
        // It's this kind of stuff that makes you scratch your head in amazement (the fact this is even needed).
        // Still needed even with latest Telerik code (2016_1_217)
        private void MenuItem_Click(object sender, RadRoutedEventArgs e)
        {
            RadMenuItem menuItem = (RadMenuItem)sender;

            while (menuItem.Parent is RadMenuItem)
                menuItem = (RadMenuItem)menuItem.Parent;

            RadContextMenu contextMenu = (RadContextMenu)menuItem.Parent;
            contextMenu.IsOpen = false;

            HideRibbonPopUpIfOpen();

        }

        // Note: <whar> Closes the Telerik Ribbon (if it's minimized and is dropped down) after a button it contains is clicked.
        // It's this kind of stuff that makes you scratch your head in amazement (the fact this is even needed).
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            HideRibbonPopUpIfOpen();
        }

        // Note: <whar> Clears the Telerik KeyTipService session when a menu closes, else menu key tips stay visible (even happens in WPF only).
        // It's this kind of stuff that makes you scratch your head in amazement (the fact this is even needed).
        private void ContextMenu_Closed(object sender, RoutedEventArgs e)
        {
            // NET6MIGRATION
            //if (Ribbon.AssociatedKeyTipService != null)
            //    Ribbon.AssociatedKeyTipService.ClearSession();

            //Ribbon.IsMinimizedPopupOpen = false;
        }
        #endregion

        #region Quick Access Toolbar
        private void QuickAccessToolBarMenuItem_Click(object sender, RadRoutedEventArgs e)
        {
            QATCustomizationMenuItem menuItem = sender as QATCustomizationMenuItem;
            if (menuItem == null)
                return;

            IRibbonUIElement ribbonUIElement = menuItem.RibbonUIElement;
            ribbonUIElement.IsVisible = !ribbonUIElement.IsVisible;

            if (ribbonUIElement.IsVisible)
                ribbonUIElement.AssociatedUIElement.Visibility = Visibility.Visible;
            else
                ribbonUIElement.AssociatedUIElement.Visibility = Visibility.Collapsed;

            EnsureConsistentQuickAccessToolBarSeparatorVisibility();

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.QuickAccessToolbar))
                RibbonKeyTipManager.UpdateQuickAccessToolbarKeyTips(Ribbon.QuickAccessToolBar.Items);
        }

        private void QuickAccessToolBar_Loaded(object sender, RoutedEventArgs e)
        {
            QuickAccessToolBar quickAccessToolBar = sender as QuickAccessToolBar;
            if (quickAccessToolBar != null)
            {
                RadRibbonDropDownButton dropDownButton = quickAccessToolBar.FindChildByType<RadRibbonDropDownButton>();
                if (dropDownButton == null)
                    return;

                StackPanel dropDownContent = dropDownButton.DropDownContent as StackPanel;
                if (dropDownContent == null)
                    return;

                if (TelerikQATMenuItems.Count == 0)
                    PopulateTelerikQATMenuCache(dropDownContent);

                RadGroupHeader groupHeader = GetQATMenuGroupHeader(dropDownContent);
                dropDownContent.Children.Clear();

                AddQATMenuGroupHeader(dropDownContent, groupHeader);

                QATContextMenu.Items.Clear();
                AddUserSelectableQATCommandsToQATContextMenu();

                QATContextMenu.Items.Add(new RadMenuSeparatorItem());

                AddTelerikQATMenuItemsToQATContextMenu();
                dropDownContent.Children.Add(QATContextMenu);
            }
        }
        #endregion

        #region Ribbon
        private void Ribbon_ToolBarPositionChanged(object sender, RadRoutedEventArgs e)
        {
            RadRibbonView ribbon = sender as RadRibbonView;
            if (sender == null)
                return;

            RibbonGlobalUserSettingsElement ribbonGlobalUserSettingsElement = RibbonGlobalUserSettingsManager.ElementAt(0) as RibbonGlobalUserSettingsElement;
            ribbonGlobalUserSettingsElement.IsQuickAccessToolBarAboveRibbon = ribbon.QuickAccessToolBarPosition == QuickAccessToolBarPosition.AboveRibbon;
        }

        private void Ribbon_MinimizedChanged(object sender, RadRoutedEventArgs e)
        {
            RadRibbonView ribbon = sender as RadRibbonView;
            if (sender == null)
                return;

            RibbonGlobalUserSettingsElement ribbonGlobalUserSettingsElement = RibbonGlobalUserSettingsManager.ElementAt(0) as RibbonGlobalUserSettingsElement;
            ribbonGlobalUserSettingsElement.IsRibbonMinimized = ribbon.IsMinimized;
        }

        private void Ribbon_ApplicationMenuOpenStateChanged(object sender, Telerik.Windows.RadRoutedEventArgs e)
        {
            if (ApplicationMenuOpenStateChanged != null)
                ApplicationMenuOpenStateChanged(sender, e);
        }

        private void Ribbon_SelectionChanged(object sender, RadSelectionChangedEventArgs e)
        {
            if (e.AddedItems != null && e.AddedItems.Count == 1 && e.AddedItems[0] is RadRibbonTab)
            {
                RadRibbonTab newTab = (RadRibbonTab)e.AddedItems[0];
                SetCurrentRibbonTab(newTab);
            }
        }
        #endregion

        #endregion

        #region IImageProvider Members
        public BitmapSource GetImage(bool largeImage, string standardGraphicResourceKey)
        {
            if (largeImage)
            {
                if (CheckImageCache(LargeImages, standardGraphicResourceKey))
                    return LargeImages[standardGraphicResourceKey];
            }
            else
            {
                if (CheckImageCache(SmallImages, standardGraphicResourceKey))
                    return SmallImages[standardGraphicResourceKey];
            }

            return null;
        }
        #endregion

        #region IQueryRibbonTabIndex Members
        public int GetIndexForTab(RadRibbonTab ribbonTab)
        {
            for (int i = 0; i < Ribbon.Items.Count; ++i)
            {
                if (Ribbon.Items[i] is RadRibbonTab)
                {
                    if (Ribbon.Items[i].Equals(ribbonTab))
                        return i;
                }
            }
            return Int32.MaxValue;
        }
        #endregion

        #region Public Methods
        #region Add Methods (for adding named objects)
        /// <summary>
        /// Adds a new (non-contextual) tab to the ribbon bar.
        /// </summary>
        /// <param name="newTabName">The unique name of the new tab to add.</param>
        /// <param name="tabText">The localized text for the tab. Uses an ampersand to define access text.</param>
        /// <returns>The new tab. (Can also be obtained later by calling GetRibbonTab).</returns>
        public RadRibbonTab AddTabToRibbonBar(string newTabName, string tabText)
        {
            newTabName = CheckControlName(newTabName);

            RadRibbonTab ribbonTab = new RadRibbonTab();
            ribbonTab.Name = newTabName;
            ribbonTab.Header = StringLibrary.CleanTextResource(tabText);
            Ribbon.Items.Add(ribbonTab);

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.TabsAndControls))
                RibbonKeyTipManager.RegisterRibbonTab(ribbonTab, tabText);

            RegisterFrameworkElement(ribbonTab);

            return ribbonTab;
        }

        /// <summary>
        /// Adds a new ribbon group to a tab.
        /// A ribbon group is a group that groups buttons, button groups and has a label.
        /// </summary>
        /// <param name="tabName">The name of the tab to add the group to. The tab must already have been added.</param>
        /// <param name="newGroupName">The name of the new group. Must be a unique control name.</param>
        /// <param name="newGroupText">The localized text to display for the group.</param>
        /// <param name="collapsedStandardGraphicResourceKey">An optional graphic resource key to use for a 16x16 icon when the ribbon group is collapsed.</param>
        /// <returns>The instantiated ribbon group. (Can also be obtained later by calling GetRibbonGroup).</returns>
        public RadRibbonGroup AddRibbonGroupToTab(string tabName, string newGroupName, string newGroupText, string collapsedStandardGraphicResourceKey)
        {
            RadRibbonTab ribbonTab = GetRibbonTab(tabName);
            return AddRibbonGroupToTab(ribbonTab, newGroupName, newGroupText, collapsedStandardGraphicResourceKey);
        }

        /// <summary>
        /// Adds a new ribbon group to a tab.
        /// A ribbon group is a group that groups buttons, button groups and has a label.
        /// </summary>
        /// <param name="ribbonTab">The tab instance to add the ribbon group to.</param>
        /// <param name="newGroupName">The name of the new group. Must be a unique control name.</param>
        /// <param name="newGroupText">The localized text to display for the group.</param>
        /// <param name="collapsedStandardGraphicResourceKey">An optional graphic resource key to use for a 16x16 icon when the ribbon group is collapsed.</param>
        /// <returns>The instantiated ribbon group. (Can also be obtained later by calling GetRibbonGroup).</returns>
        public RadRibbonGroup AddRibbonGroupToTab(RadRibbonTab ribbonTab, string newGroupName, string newGroupText, string collapsedStandardGraphicResourceKey)
        {
            newGroupName = CheckControlName(newGroupName);

            RadRibbonGroup ribbonGroup = new RadRibbonGroup();
            ribbonGroup.Name = newGroupName;
            ribbonGroup.Header = StringLibrary.CleanTextResource(newGroupText);

            if (!StringLibrary.IsBlank(collapsedStandardGraphicResourceKey))
            {
                BitmapSource groupImage = GetImage(true, collapsedStandardGraphicResourceKey);
                ribbonGroup.Icon = groupImage;
            }

            ribbonTab.Items.Add(ribbonGroup);

            RegisterFrameworkElement(ribbonGroup);

            return ribbonGroup;
        }

        /// <summary>
        /// Adds a wrap panel to a ribbon group. Wrap panels are used when a group needs to be layed out that contains longer UI elements such as
        /// combo boxes. Combo-boxes and other larger UI elements can be added directly to the wrap panel. Buttons are normally added to a wrap panel
        /// by first adding a horizontal button group to the wrap panel and adding buttons to the horizontal button group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the existing ribbon group to add the new wrap panel to.</param>
        /// <param name="newWrapPanelName">The name of the new wrap panel. Must be a unique control name.</param>
        /// <returns>The instantiated wrap panel. (Can also be obtained later by calling GetWrapPanel).</returns>
        public RadOrderedWrapPanel AddWrapPanelToRibbonGroup(string ribbonGroupName, string newWrapPanelName)
        {
            RadRibbonGroup ribbonGroup = GetRibbonGroup(ribbonGroupName);
            return AddWrapPanelToRibbonGroup(ribbonGroup, newWrapPanelName);
        }

        /// <summary>
        /// Adds a wrap panel to a ribbon group. Wrap panels are used when a group needs to be layed out that contains longer UI elements such as
        /// combo boxes. Combo-boxes and other larger UI elements can be added directly to the wrap panel. Buttons are normally added to a wrap panel
        /// by first adding a horizontal button group to the wrap panel and adding buttons to the horizontal button group.
        /// </summary>
        /// <param name="ribbonGroup">The existing ribbon group to add the new wrap panel to.</param>
        /// <param name="newWrapPanelName">The name of the new wrap panel. Must be a unique control name.</param>
        /// <returns>The instantiated wrap panel. (Can also be obtained later by calling GetWrapPanel).</returns>
        public RadOrderedWrapPanel AddWrapPanelToRibbonGroup(RadRibbonGroup ribbonGroup, string newWrapPanelName)
        {
            RadOrderedWrapPanel wrapPanel = new RadOrderedWrapPanel();
            wrapPanel.Name = newWrapPanelName;
            ribbonGroup.Items.Add(wrapPanel);
            RegisterFrameworkElement(wrapPanel);
            return wrapPanel;
        }

        /// <summary>
        /// Adds a horizontal button group to a wrap panel.
        /// </summary>
        /// <param name="wrapPanelName">The name of the wrap panel to add the horizontal button group to.</param>
        /// <param name="newButtonGroupName">The name of the new horizontal button group. Must be a unique control name.</param>
        /// <returns>The instantiated horizontal button group. (Can also be obtained later by calling GetHorizontalButtonGroup).</returns>
        public RadRibbonHorizontalButtonGroup AddHorizontalButtonGroupToWrapPanel(string wrapPanelName, string newButtonGroupName)
        {
            RadOrderedWrapPanel wrapPanel = GetWrapPanel(wrapPanelName);
            return AddHorizontalButtonGroupToWrapPanel(wrapPanel, newButtonGroupName, false);
        }

        /// <summary>
        /// Adds a horizontal button group to a wrap panel.
        /// </summary>
        /// <param name="wrapPanel">The wrap panel to add the horizontal button group to.</param>
        /// <param name="newButtonGroupName">The name of the new horizontal button group. Must be a unique control name.</param>
        /// <returns>The instantiated horizontal button group. (Can also be obtained later by calling GetHorizontalButtonGroup).</returns>
        public RadRibbonHorizontalButtonGroup AddHorizontalButtonGroupToWrapPanel(RadOrderedWrapPanel wrapPanel, string newButtonGroupName)
        {
            return AddHorizontalButtonGroupToWrapPanel(wrapPanel, newButtonGroupName, false);
        }
        /// <summary>
        /// Adds a horizontal button group to a wrap panel.
        /// </summary>
        /// <param name="wrapPanel">The wrap panel to add the horizontal button group to.</param>
        /// <param name="newButtonGroupName">The name of the new horizontal button group. Must be a unique control name.</param>
		/// <param name="separatorBefore">Whether or not to include a separator before the button group.</param>
		/// <returns>The instantiated horizontal button group. (Can also be obtained later by calling GetHorizontalButtonGroup).</returns>
		public RadRibbonHorizontalButtonGroup AddHorizontalButtonGroupToWrapPanel(RadOrderedWrapPanel wrapPanel, string newButtonGroupName, bool separatorBefore)
        {
            newButtonGroupName = CheckControlName(newButtonGroupName);

            RadRibbonHorizontalButtonGroup buttonGroup = new RadRibbonHorizontalButtonGroup();
            buttonGroup.Name = newButtonGroupName;

            if (separatorBefore)
            {
                buttonGroup.SetValue(RadButtonGroup.BorderThicknessProperty, new Thickness(1.0, 0.0, 0.0, 0.0));
                buttonGroup.SetValue(Control.PaddingProperty, new Thickness(4.0, 0.0, 0.0, 0.0));
            }
            else
                buttonGroup.SetValue(RadButtonGroup.BorderThicknessProperty, new Thickness(0.0));

            wrapPanel.Children.Add(buttonGroup);
            RegisterFrameworkElement(buttonGroup);

            return buttonGroup;
        }

        /// <summary>
        /// Adds a new combo box to a wrap panel.
        /// </summary>
        /// <param name="wrapPanelName">The name of the wrap panel to add the combo to.</param>
        /// <param name="newComboBoxName">The name of the new combo box. Must be a unique control name.</param>
        /// <param name="toolTipTitle">The localized title text to display on the button tooltip.</param>
        /// <param name="toolTipDescription">The localized description text to display on the button tooltip.</param>
        /// <param name="width">The required width of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="height">The required height of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="dropDownWidth">The required width of the combo box drop down. Set to Double.NaN for auto-size.</param>
        /// <param name="maximumDropDownHeight">The maximum drop down height for the combo box.</param>
        /// <param name="fontSize">The desired font size for the combo box.</param>
        /// <returns>The instantiated combo box. (Can also be obtained later by calling GetComboBox).</returns>
        public RadComboBox AddComboBoxToWrapPanel(string wrapPanelName, string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadOrderedWrapPanel wrapPanel = GetWrapPanel(wrapPanelName);
            return AddComboBoxToWrapPanel(wrapPanel, newComboBoxName, toolTipTitle, toolTipDescription, width, height, dropDownWidth, maximumDropDownHeight, fontSize);
        }

        /// <summary>
        /// Adds a new combo box to a wrap panel.
        /// </summary>
        /// <param name="wrapPanel">The wrap panel to add the combo to.</param>
        /// <param name="newComboBoxName">The name of the new combo box. Must be a unique control name.</param>
        /// <param name="toolTipTitle">The localized title text to display on the button tooltip.</param>
        /// <param name="toolTipDescription">The localized description text to display on the button tooltip.</param>
        /// <param name="width">The required width of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="height">The required height of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="dropDownWidth">The required width of the combo box drop down. Set to Double.NaN for auto-size.</param>
        /// <param name="maximumDropDownHeight">The maximum drop down height for the combo box.</param>
        /// <param name="fontSize">The desired font size for the combo box.</param>
        /// <returns>The instantiated combo box. (Can also be obtained later by calling GetComboBox).</returns>
        public RadComboBox AddComboBoxToWrapPanel(RadOrderedWrapPanel wrapPanel, string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadComboBox radComboBox = NewRadComboBox(newComboBoxName, toolTipTitle, toolTipDescription, width, height, dropDownWidth, maximumDropDownHeight, fontSize);
            wrapPanel.Children.Add(radComboBox);
            RegisterFrameworkElement(radComboBox);
            return radComboBox;
        }

        /// <summary>
        /// Adds a new combo box to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group to add the combo to.</param>
        /// <param name="newComboBoxName">The name of the new combo box. Must be a unique control name.</param>
        /// <param name="toolTipTitle">The localized title text to display on the button tooltip.</param>
        /// <param name="toolTipDescription">The localized description text to display on the button tooltip.</param>
        /// <param name="width">The required width of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="height">The required height of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="dropDownWidth">The required width of the combo box drop down. Set to Double.NaN for auto-size.</param>
        /// <param name="maximumDropDownHeight">The maximum drop down height for the combo box.</param>
        /// <param name="fontSize">The desired font size for the combo box.</param>
        /// <returns>The instantiated combo box. (Can also be obtained later by calling GetComboBox).</returns>
        public RadComboBox AddComboBoxToRibbonGroup(string ribbonGroupName, string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadRibbonGroup ribbonGroup = GetRibbonGroup(ribbonGroupName);
            return AddComboBoxToRibbonGroup(ribbonGroup, newComboBoxName, toolTipTitle, toolTipDescription, width, height, dropDownWidth, maximumDropDownHeight, fontSize);
        }

        /// <summary>
        /// Adds a new combo box to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroup">The ribbon group to add the combo to.</param>
        /// <param name="newComboBoxName">The name of the new combo box. Must be a unique control name.</param>
        /// <param name="toolTipTitle">The localized title text to display on the button tooltip.</param>
        /// <param name="toolTipDescription">The localized description text to display on the button tooltip.</param>
        /// <param name="width">The required width of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="height">The required height of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="dropDownWidth">The required width of the combo box drop down. Set to Double.NaN for auto-size.</param>
        /// <param name="maximumDropDownHeight">The maximum drop down height for the combo box.</param>
        /// <param name="fontSize">The desired font size for the combo box.</param>
        /// <returns>The instantiated combo box. (Can also be obtained later by calling GetComboBox).</returns>
        public RadComboBox AddComboBoxToRibbonGroup(RadRibbonGroup ribbonGroup, string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadComboBox radComboBox = NewRadComboBox(newComboBoxName, toolTipTitle, toolTipDescription, width, height, dropDownWidth, maximumDropDownHeight, fontSize);
            ribbonGroup.Items.Add(radComboBox);
            RegisterFrameworkElement(radComboBox);
            return radComboBox;
        }

        /// <summary>
        /// Adds a new combo box to the right hand side of the top tab strip, but to the left of the existing Minimize/Help buttons. Buttons are added right to left.
        /// </summary>
        /// <param name="newComboBoxName">The name of the new combo box. Must be a unique control name.</param>
        /// <param name="toolTipTitle">The localized title text to display on the button tooltip.</param>
        /// <param name="toolTipDescription">The localized description text to display on the button tooltip.</param>
        /// <param name="width">The required width of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="height">The required height of the combo box. Set to Double.NaN for auto-size.</param>
        /// <param name="dropDownWidth">The required width of the combo box drop down. Set to Double.NaN for auto-size.</param>
        /// <param name="maximumDropDownHeight">The maximum drop down height for the combo box.</param>
        /// <param name="fontSize">The desired font size for the combo box.</param>
        /// <returns>The instantiated combo box. (Can also be obtained later by calling GetComboBox).</returns>
        public RadComboBox AddComboBoxToTopTabStrip(string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadComboBox radComboBox = NewRadComboBox(newComboBoxName, toolTipTitle, toolTipDescription, width, height, dropDownWidth, maximumDropDownHeight, fontSize);
            StackPanel stackPanel = Ribbon.TabStripAdditionalContent as StackPanel;
            AddTopTabStripPaddingElement();
            stackPanel.Children.Insert(0, radComboBox);
            RegisterFrameworkElement(radComboBox);
            return radComboBox;
        }

        /// <summary>
        /// Adds a button to the right hand side of the top tab strip, but to the left of the existing Minimize/Help buttons. Buttons are added right to left.
        /// </summary>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToTopTabStrip(IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToTopTabStrip(ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to the right hand side of the top tab strip, but to the left of the existing Minimize/Help buttons. Buttons are added right to left.
        /// </summary>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToTopTabStrip(IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            return AddButtonToTopTabStrip(ribbonUICommand, buttonSize, buttonStyle, newButtonName, false);
        }

        /// <summary>
        /// Adds a button to the right hand side of the top tab strip, to the left of the existing Minimize/Help buttons, or inbetween the Minimize/Help buttons as specified. 
        /// Buttons are added right to left.
        /// </summary>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <param name="betweenMinimizeAndHelp">Whether to add the new button between the existing ribbon minimize and help buttons.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToTopTabStrip(IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName, bool betweenMinimizeAndHelp)
        {
            IRibbonButton button = NewButton(ribbonUICommand, String.Empty, buttonSize, buttonStyle, newButtonName, false);
            Control buttonControl = (Control)button;

            if (betweenMinimizeAndHelp)
            {
                // NET6MIGRATION
                StackPanel stackPanel = Ribbon.TabStripAdditionalContent as StackPanel;
                stackPanel.Children.Insert(0, buttonControl);
            }
            else
            {
                StackPanel stackPanel = Ribbon.TabStripAdditionalContent as StackPanel;
                AddTopTabStripPaddingElement(); // TODO 1: <whar> Review when/if we start adding buttons up there.
                stackPanel.Children.Insert(0, buttonControl);
            }

            return buttonControl;
        }

        /// <summary>
        /// Adds a button to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToRibbonGroup(string ribbonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToRibbonGroup(ribbonGroupName, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToRibbonGroup(string ribbonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            RadRibbonGroup ribbonGroup = GetRibbonGroup(ribbonGroupName);
            return AddButtonToRibbonGroup(ribbonGroup, ribbonUICommand, buttonSize, buttonStyle, newButtonName);
        }

        /// <summary>
        /// Adds a button to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroup">The ribbon group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToRibbonGroup(RadRibbonGroup ribbonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToRibbonGroup(ribbonGroup, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroup">The ribbon group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToRibbonGroup(RadRibbonGroup ribbonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            IRibbonButton button = NewButton(ribbonUICommand, String.Empty, buttonSize, buttonStyle, newButtonName, false);
            ribbonGroup.Items.Add(button);
            AfterAddButtonToRibbonTab(ribbonUICommand, button, buttonSize);
            return (Control)button;
        }

        /// <summary>
        /// Adds a button group to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group to add the button group to.</param>
        /// <param name="newButtonGroupName">The name of the new button group. Must be a unique control name.</param>
        /// <returns>The instantiated button group. (Can also be obtained later by calling GetButtonGroup).</returns>
        public RadRibbonButtonGroup AddButtonGroupToRibbonGroup(string ribbonGroupName, string newButtonGroupName)
        {
            RadRibbonGroup ribbonGroup = GetRibbonGroup(ribbonGroupName);
            return AddButtonGroupToRibbonGroup(ribbonGroup, newButtonGroupName);
        }

        /// <summary>
        /// Adds a button group to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group to add the button group to.</param>
        /// <param name="newButtonGroupName">The name of the new button group. Must be a unique control name.</param>
        /// <param name="separatorBefore">Whether or not to include a separator before the button group.</param>
        /// <returns>The instantiated button group. (Can also be obtained later by calling GetButtonGroup).</returns>
        public RadRibbonButtonGroup AddButtonGroupToRibbonGroup(string ribbonGroupName, string newButtonGroupName, bool separatorBefore)
        {
            RadRibbonGroup ribbonGroup = GetRibbonGroup(ribbonGroupName);
            return AddButtonGroupToRibbonGroup(ribbonGroup, newButtonGroupName, separatorBefore);
        }

        /// <summary>
        /// Adds a button group to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroup">The ribbon group to add the button group to.</param>
        /// <param name="newButtonGroupName">The name of the new button group. Must be a unique control name.</param>
        /// <returns>The instantiated button group. (Can also be obtained later by calling GetButtonGroup).</returns>
        public RadRibbonButtonGroup AddButtonGroupToRibbonGroup(RadRibbonGroup ribbonGroup, string newButtonGroupName)
        {
            return AddButtonGroupToRibbonGroup(ribbonGroup, newButtonGroupName, false);
        }

        /// <summary>
        /// Adds a button group to a ribbon group.
        /// </summary>
        /// <param name="ribbonGroup">The ribbon group to add the button group to.</param>
        /// <param name="newButtonGroupName">The name of the new button group. Must be a unique control name.</param>
        /// <param name="separatorBefore">Whether or not to include a separator before the button group.</param>
        /// <returns>The instantiated button group. (Can also be obtained later by calling GetButtonGroup).</returns>
        public RadRibbonButtonGroup AddButtonGroupToRibbonGroup(RadRibbonGroup ribbonGroup, string newButtonGroupName, bool separatorBefore)
        {
            newButtonGroupName = CheckControlName(newButtonGroupName);

            RadRibbonButtonGroup buttonGroup = new RadRibbonButtonGroup();
            buttonGroup.Name = newButtonGroupName;

            if (separatorBefore)
            {
                Telerik.Windows.Controls.Separator separator = new Telerik.Windows.Controls.Separator();
                separator.Width = 1.0; // Note: <whar> Important.
                ribbonGroup.Items.Add(separator);
            }

            ribbonGroup.Items.Add(buttonGroup);
            RegisterFrameworkElement(buttonGroup);

            return buttonGroup;
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToButtonGroup(buttonGroupName, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonLabelOverride">An optional string to specify a label to use instead of the one on the IRibbonUICommand. e.g., abbreviation.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, string buttonLabelOverride, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToButtonGroup(buttonGroupName, ribbonUICommand, buttonLabelOverride, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            return AddButtonToButtonGroup(buttonGroupName, ribbonUICommand, String.Empty, buttonSize, buttonStyle, newButtonName);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonLabelOverride">An optional string to specify a label to use instead of the one on the IRibbonUICommand. e.g., abbreviation.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, string buttonLabelOverride, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            RadRibbonButtonGroup buttonGroup = GetButtonGroup(buttonGroupName);
            return AddButtonToButtonGroup(buttonGroup, ribbonUICommand, buttonLabelOverride, buttonSize, buttonStyle, newButtonName);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroup">The button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(RadRibbonButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToButtonGroup(buttonGroup, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroup">The button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonLabelOverride">An optional string to specify a label to use instead of the one on the IRibbonUICommand. e.g., abbreviation.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(RadRibbonButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, string buttonLabelOverride, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToButtonGroup(buttonGroup, ribbonUICommand, buttonLabelOverride, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroup">The button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(RadRibbonButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            return AddButtonToButtonGroup(buttonGroup, ribbonUICommand, String.Empty, buttonSize, buttonStyle, newButtonName);
        }

        /// <summary>
        /// Adds a button to a button group.
        /// </summary>
        /// <param name="buttonGroup">The button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonLabelOverride">An optional string to specify a label to use instead of the one on the IRibbonUICommand. e.g., abbreviation.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToButtonGroup(RadRibbonButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, string buttonLabelOverride, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            IRibbonButton button = NewButton(ribbonUICommand, buttonLabelOverride, buttonSize, buttonStyle, newButtonName, false);

            // Note: <whar> Fix for issue Kcul noted; large buttons in button groups get too close to each other.
            Control buttonControl = (Control)button;
            const double EXTRA_PADDING = 3.0;
            if (buttonSize == ButtonSize.Large)
            {
                buttonControl.Padding = new Thickness(buttonControl.Padding.Left + EXTRA_PADDING, buttonControl.Padding.Top,
                    buttonControl.Padding.Right + EXTRA_PADDING, buttonControl.Padding.Bottom);
            }

            buttonGroup.Children.Add(buttonControl);
            AfterAddButtonToRibbonTab(ribbonUICommand, button, buttonSize);

            return buttonControl;
        }

        /// <summary>
        /// Adds a button to a horizontal button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the horizontal button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToHorizontalButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToHorizontalButtonGroup(buttonGroupName, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a horizontal button group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the horizontal button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToHorizontalButtonGroup(string buttonGroupName, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            RadRibbonHorizontalButtonGroup buttonGroup = GetHorizontalButtonGroup(buttonGroupName);
            return AddButtonToHorizontalButtonGroup(buttonGroup, ribbonUICommand, buttonSize, buttonStyle, newButtonName);
        }

        /// <summary>
        /// Adds a button to a horizontal button group.
        /// </summary>
        /// <param name="buttonGroup">The horizontal button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToHorizontalButtonGroup(RadRibbonHorizontalButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle)
        {
            return AddButtonToHorizontalButtonGroup(buttonGroup, ribbonUICommand, buttonSize, buttonStyle, String.Empty);
        }

        /// <summary>
        /// Adds a button to a horizontal button group.
        /// </summary>
        /// <param name="buttonGroup">The horizontal button group to add the button to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="buttonSize">The required size of the button.</param>
        /// <param name="buttonStyle">The required style of the button.</param>
        /// <param name="newButtonName">The name of the new button. Must be a unique control name.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddButtonToHorizontalButtonGroup(RadRibbonHorizontalButtonGroup buttonGroup, IRibbonUICommand ribbonUICommand, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName)
        {
            IRibbonButton button = NewButton(ribbonUICommand, String.Empty, buttonSize, buttonStyle, newButtonName, false);
            buttonGroup.Items.Add(button);
            AfterAddButtonToRibbonTab(ribbonUICommand, button, buttonSize);
            return (Control)button;
        }

        /// <summary>
        /// Adds a menu item to a drop down button.
        /// </summary>
        /// <param name="existingButtonName">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToDropDownButton(string existingButtonName, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddMenuItemToDropDownButton(existingButtonName, ribbonUICommand, separatorAfter, String.Empty);
        }

        /// <summary>
        /// Adds a menu item to a drop down button.
        /// </summary>
        /// <param name="existingButtonName">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToDropDownButton(string existingButtonName, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            Control existingButton = GetButton(existingButtonName);
            return AddMenuItemToDropDownButton(existingButton, ribbonUICommand, separatorAfter, newMenuItemName);
        }

        /// <summary>
        /// Adds a menu item to a drop down button.
        /// </summary>
        /// <param name="existingButton">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToDropDownButton(Control existingButton, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddMenuItemToDropDownButton(existingButton, ribbonUICommand, separatorAfter, String.Empty);
        }

        /// <summary>
        /// Adds a menu item to a drop down button.
        /// </summary>
        /// <param name="existingButton">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToDropDownButton(Control existingButton, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            RibbonButtonStyleInfo buttonStyleInfo = GetButtonStyleInfo(existingButton as IRibbonButton);
            CheckButtonIsDropDown(buttonStyleInfo);
            CheckAllRequiredParametersForDropDownToggleButton(buttonStyleInfo, ribbonUICommand.ToolTipTitle, ribbonUICommand.HotKey);

            RadMenuItem menuItem = null;
            if (buttonStyleInfo.ButtonStyle == ButtonStyle.DropDownToggle)
                menuItem = NewToggleMenuItem(ribbonUICommand, (RadRibbonToggleSplitButton)existingButton, newMenuItemName);
            else
                menuItem = NewMenuItem(ribbonUICommand, newMenuItemName, false, false);

            AddTopLevelMenuItem(existingButton, buttonStyleInfo, menuItem, separatorAfter);

            RegisterControlHotKey(ribbonUICommand);
            RegisterDispatcherElement(ribbonUICommand);
            RegisterFrameworkElement(menuItem);
            RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, menuItem);

            return menuItem;
        }

        /// <summary>
        /// Adds a checked menu item to a drop down button. The same as a regular menu, but can be checked and is distinguishable 
        /// as a unique element type in the RibbonUICommand implementation of GetLinkedElements() allowing for more flexible custom
        /// command implementations and behaviors of UI elements linked to the same IRibbonUICommand.
        /// </summary>
        /// <param name="existingButtonName">The name of the drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddCheckedMenuItemToDropDownButton(string existingButtonName, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddCheckedMenuItemToDropDownButton(existingButtonName, ribbonUICommand, separatorAfter, String.Empty);
        }

        /// <summary>
        /// Adds a checked menu item to a drop down button. The same as a regular menu, but can be checked and is distinguishable 
        /// as a unique element type in the RibbonUICommand implementation of GetLinkedElements() allowing for more flexible custom
        /// command implementations and behaviors of UI elements linked to the same IRibbonUICommand.
        /// </summary>
        /// <param name="existingButton">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddCheckedMenuItemToDropDownButton(Control existingButton, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddCheckedMenuItemToDropDownButton(existingButton, ribbonUICommand, separatorAfter);
        }

        /// <summary>
        /// Adds a checked menu item to a drop down button. The same as a regular menu, but can be checked and is distinguishable 
        /// as a unique element type in the RibbonUICommand implementation of GetLinkedElements() allowing for more flexible custom
        /// command implementations and behaviors of UI elements linked to the same IRibbonUICommand.
        /// </summary>
        /// <param name="existingButtonName">The name of the drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddCheckedMenuItemToDropDownButton(string existingButtonName, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            Control existingButton = GetButton(existingButtonName);
            return AddCheckedMenuItemToDropDownButton(existingButton, ribbonUICommand, separatorAfter, newMenuItemName);
        }

        /// <summary>
        /// Adds a checked menu item to a drop down button. The same as a regular menu, but can be checked and is distinguishable 
        /// as a unique element type in the RibbonUICommand implementation of GetLinkedElements() allowing for more flexible custom
        /// command implementations and behaviors of UI elements linked to the same IRibbonUICommand.
        /// </summary>
        /// <param name="existingButton">The drop down button to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the button. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddCheckedMenuItemToDropDownButton(Control existingButton, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            RibbonButtonStyleInfo buttonStyleInfo = GetButtonStyleInfo(existingButton as IRibbonButton);
            CheckButtonIsDropDown(buttonStyleInfo);
            CheckButtonIsNotDropDownToggle(buttonStyleInfo);

            RadMenuItem menuItem = NewMenuItem(ribbonUICommand, newMenuItemName, true, false);
            AddTopLevelMenuItem(existingButton, buttonStyleInfo, menuItem, separatorAfter);

            RegisterControlHotKey(ribbonUICommand);
            RegisterDispatcherElement(ribbonUICommand);
            RegisterFrameworkElement(menuItem);
            RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, menuItem);

            return menuItem;
        }

        /// <summary>
        /// Adds a menu item to another menu item. For fly-out (aka nested) menus.
        /// </summary>
        /// <param name="existingMenuItemName">The name of an existing menu item to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToMenuItem(string existingMenuItemName, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddMenuItemToMenuItem(existingMenuItemName, ribbonUICommand, separatorAfter, String.Empty);
        }

        /// <summary>
        /// Adds a menu item to another menu item. For fly-out (aka nested) menus.
        /// </summary>
        /// <param name="existingMenuItemName">The name of an existing menu item to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToMenuItem(string existingMenuItemName, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            RadMenuItem existingMenuItem = GetMenuItem(existingMenuItemName);
            return AddMenuItemToMenuItem(existingMenuItem, ribbonUICommand, separatorAfter, newMenuItemName);
        }

        /// <summary>
        /// Adds a menu item to another menu item. For fly-out (aka nested) menus.
        /// </summary>
        /// <param name="existingMenuItem">The menu item to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToMenuItem(RadMenuItem existingMenuItem, IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            return AddMenuItemToMenuItem(existingMenuItem, ribbonUICommand, separatorAfter);
        }

        /// <summary>
        /// Adds a menu item to another menu item. For fly-out (aka nested) menus.
        /// </summary>
        /// <param name="existingMenuItem">The menu item to add the menu item to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu. One IRibbonUICommand can be used to instantiate multiple ribbon control instances.</param>
        /// <param name="separatorAfter">Whether a separator should appear after the menu item.</param>
        /// <param name="newMenuItemName">The name of the new menu item. Must be a unique control name.</param>
        /// <returns>The added menu item. (Can also be obtained later by calling GetMenuItem).</returns>
        public RadMenuItem AddMenuItemToMenuItem(RadMenuItem existingMenuItem, IRibbonUICommand ribbonUICommand, bool separatorAfter, string newMenuItemName)
        {
            newMenuItemName = CheckControlName(newMenuItemName);

            // TODO 1: <whar> I think this was blocked since a previous method in RadRibbonToggleSplitButton (MenuItemExistsInButtonContextMenu) was used to check
            // whether the default menu item was actually living under the button in question, but now such a check probably needs to happen on the owned menu
            // (and sub-menu) commands, but then maybe by the nature of the way things work, this check is not required (the call is internal and the menu is the
            // one calling back on it's OwnerButton), so maybe it just works and all I need to do is remove this check and do some testing.
            if (existingMenuItem is RadToggleMenuItem)
                throw new NotSupportedException("Nested menu items for the RadRibbonToggleSplitButton (ButtonStyle = DropDownToggle) not yet supported."); // I18N. OK.

            RadMenuItem menuItem = NewMenuItem(ribbonUICommand, newMenuItemName, false, true);

            AddSubLevelMenuItem(existingMenuItem, menuItem as SubMenuItem, separatorAfter);

            RegisterControlHotKey(ribbonUICommand);
            RegisterDispatcherElement(ribbonUICommand);
            RegisterFrameworkElement(menuItem);
            RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, menuItem);

            return menuItem;
        }

        /// <summary>
        /// Removes a fly out menu item from a parent menu item.
        /// </summary>
        /// <param name="parentMenuItemName">The unique name of the parent menu item.</param>
        /// <param name="childMenuItemName">The unique name of the child (fly-out) menu item that is to be removed.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu item being removed.</param>
        public void RemoveMenuItemFromMenuItem(string parentMenuItemName, string childMenuItemName, IRibbonUICommand ribbonUICommand)
        {
            RadMenuItem parentMenuItem = GetMenuItem(parentMenuItemName);
            RadMenuItem childMenuItem = GetMenuItem(childMenuItemName);
            RemoveMenuItemFromMenuItem(parentMenuItem, childMenuItem, ribbonUICommand);
        }

        /// <summary>
        /// Removes a fly out menu item from a parent menu item.
        /// </summary>
        /// <param name="parentMenuItem">The parent menu item.</param>
        /// <param name="childMenuItem">The child (fly-out) menu item that is to be removed.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu item being removed.</param>
        public void RemoveMenuItemFromMenuItem(RadMenuItem parentMenuItem, RadMenuItem childMenuItem, IRibbonUICommand ribbonUICommand)
        {
            if (parentMenuItem.Items.Contains(childMenuItem))
            {
                CleanUpSeparators(parentMenuItem.Items, childMenuItem);

                parentMenuItem.Items.Remove(childMenuItem);

                UnregisterFrameworkElement(childMenuItem);
                RibbonSearchManager.RemoveReferenceToCommand(ribbonUICommand, childMenuItem);
                if (ribbonUICommand is IRibbonLinkedUICommand)
                    ((IRibbonLinkedUICommand)ribbonUICommand).RemoveLinkedControl(childMenuItem);
            }
        }

        /// <summary>
        /// Removes a menu item from an onwer button with context menu.
        /// </summary>
        /// <param name="parentButtonName">The unique name of the parent button.</param>
        /// <param name="menuItemName">The unique name of the menu item that is to be removed.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu.</param>
		public void RemoveMenuItemFromDropDownButton(string parentButtonName, string menuItemName, IRibbonUICommand ribbonUICommand)
		{
			Control button = GetButton(parentButtonName);
			RadMenuItem menuItem = GetMenuItem(menuItemName);
			RemoveMenuItemFromDropDownButton(button, menuItem, ribbonUICommand);
		}

        /// <summary>
        /// Removes a menu item from an onwer button with context menu.
        /// </summary>
        /// <param name="parentButton">The parent button.</param>
        /// <param name="menuItem">The menu item that is to be removed.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the menu.</param>
		public void RemoveMenuItemFromDropDownButton(Control parentButton, RadMenuItem menuItem, IRibbonUICommand ribbonUICommand)
		{
			RibbonButtonStyleInfo buttonStyleInfo = GetButtonStyleInfo(parentButton as IRibbonButton);
            CheckButtonIsDropDown(buttonStyleInfo);

            RadContextMenu contextMenu = TelerikLibrary.GetContextMenu(parentButton, buttonStyleInfo, null);

            if (contextMenu != null)
            {
                if (contextMenu.Items.Contains(menuItem))
                {
                    CleanUpSeparators(contextMenu.Items, menuItem);

                    contextMenu.Items.Remove(menuItem);
                    
                    UnregisterFrameworkElement(menuItem);
                    RibbonSearchManager.RemoveReferenceToCommand(ribbonUICommand, menuItem);
                    if (ribbonUICommand is IRibbonLinkedUICommand)
                        ((IRibbonLinkedUICommand)ribbonUICommand).RemoveLinkedControl(menuItem);
                }
            }
        }

        /// <summary>
        /// Adds a new button to the ribbon quick access toolbar.
        /// </summary>
        /// <param name="ribbonUIButton">The IRibbonUIButton for the quick access toolbar button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddQuickAccessToolbarButton(IRibbonUIButton ribbonUIButton)
        {
            return AddQuickAccessToolbarButton(ribbonUIButton, String.Empty);
        }

        /// <summary>
        /// Adds a new button to the ribbon quick access toolbar.
        /// </summary>
        /// <param name="ribbonUIButton">The IRibbonUIButton for the quick access toolbar button.</param>
        /// <param name="newButtonName">The unique name of the new button.</param>
        /// <returns>The instantiated button. (Can also be obtained later by calling GetButton).</returns>
        public Control AddQuickAccessToolbarButton(IRibbonUIButton ribbonUIButton, string newButtonName)
        {
            IRibbonButton button = NewButton(ribbonUIButton.AssociatedCommand, String.Empty, ButtonSize.Small, // Note: <whar> Only small buttons in the QAT.
                ribbonUIButton.ButtonStyle, newButtonName, true);

            Ribbon.QuickAccessToolBar.Items.Add(button);

            Control buttonControl = (Control)button;
            if (RibbonExternalConfiguration.Instance.RibbonDebuggingEnabled)
            {
                buttonControl.BorderThickness = new Thickness(1.0);
                buttonControl.BorderBrush = Brushes.Red;
            }

            if (!ribbonUIButton.IsVisible)
                buttonControl.Visibility = Visibility.Collapsed;

            if (ribbonUIButton.SeparatorAfter)
                Ribbon.QuickAccessToolBar.Items.Add(new RadMenuSeparatorItem());

            QATRibbonUIElements.Add(ribbonUIButton);
            ribbonUIButton.AssociatedUIElement = buttonControl;

            return buttonControl;
        }

        /// <summary>
        /// Updates an existing (already added) quick access toolbar button with any customized settings changes.
        /// This method is designed for updating button properties during a reset to defaults operation.
        /// </summary>
        /// <param name="ribbonUIButton">The IRibbonUIButton for the quick access toolbar button.</param>
        public void UpdateQuickAccessToolbarButton(IRibbonUIButton ribbonUIButton)
        {
            UpdateQATContextMenuItem(ribbonUIButton);
            UpdateQATRibbonUIElements(ribbonUIButton);

            // Note: <whar> Right now user customizations only affect visibility.

            if (ribbonUIButton.IsVisible)
                ribbonUIButton.AssociatedUIElement.Visibility = Visibility.Visible;
            else
                ribbonUIButton.AssociatedUIElement.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Adds a new backstage item, configured as a button.
        /// </summary>
        /// <param name="ribbonUICommand">The ribbon UI command for the button. Needs to have a valid Execute implementation. Should not implement IRibbonBackstageUICommand.</param>
        /// <param name="separatorAfter">Whether a separator should appear after this command.</param>
        /// <returns>The instantiated RadRibbonBackstageItem</returns>
        public RadRibbonBackstageItem AddBackStageItemButtonType(IRibbonUICommand ribbonUICommand, bool separatorAfter)
        {
            if (ribbonUICommand is IRibbonBackstageUICommand)
                throw new ArgumentException("ribbonUICommand implements IRibbonBackstageUICommand. Please use the AddBackStageItemTabType method."); // I18N. OK.

            RadRibbonBackstageItem backStageItem = AddBackstageItem(ribbonUICommand, separatorAfter, true);
            RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, backStageItem);

            return backStageItem;
        }

        /// <summary>
        /// Adds a new backstage item, configured as a tab, that can accept a WPF user control as right hand side content.
        /// </summary>
        /// <param name="backstageUICommand">The backstage user command that defines the right hand side user control and answers which IRibbonUICommands are associated with the user control.</param>
        /// <param name="separatorAfter">Whether a separator should appear after this command.</param>
        /// <returns>The instantiated RadRibbonBackstageItem</returns>
        public RadRibbonBackstageItem AddBackStageItemTabType(IRibbonBackstageUICommand backstageUICommand, bool separatorAfter)
        {
            UserControl backstageControl = backstageUICommand.GetBackstageUserControl();
            if (backstageControl == null)
                throw new ArgumentException("backstageUICommand must answer a valid UserControl."); // I18N. OK.

            // Note: <whar> For simple Labels, we set the text color to be consistent with the theme.
            // For more complicated WPF elements (that we have control over) we do resource binding in the controls' xaml directly.
            // (See Haestad.Drawing.UI.Integration.WPF.Controls.ImageButton for an example).
            ApplyThemeLabelColors(backstageControl);

            RadRibbonBackstageItem backStageItem = AddBackstageItem(backstageUICommand, separatorAfter, false);

            // Note: <whar> Work around issue where the user control goes behind the title bar.
            const double TOP_PADDING = 30.0;
            const double OTHER_PADDING = 2.0;
            backstageControl.Padding = new Thickness(OTHER_PADDING, TOP_PADDING, OTHER_PADDING, OTHER_PADDING);

            backStageItem.IsSelectable = true;
            backStageItem.Content = backstageControl;

            foreach (IRibbonUICommand ribbonUICommand in backstageUICommand.BackstageUserControlCommands)
            {
                if (ribbonUICommand is IRibbonLinkedUICommand)
                    ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedBackstageItem(backStageItem);

                RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, backStageItem, backStageItem.Header.ToString()); // Note: <whar> Also allowing search on tab text.
            }

            return backStageItem;
        }

        /// <summary>
        /// Adds a dialog launcher to a Ribbon Group. This is the little launcher icon at the bottom right hand side of a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the group to add the launcher to.</param>
        /// <param name="ribbonUICommand">The IRibbonUICommand for the dialog launcher command.</param>
        public void AddDialogLauncherToRibbonGroup(string ribbonGroupName, IRibbonUICommand ribbonUICommand)
        {
            RadRibbonGroup group = GetRibbonGroup(ribbonGroupName);

            if (ribbonUICommand != null)
            {
                group.DialogLauncherVisibility = Visibility.Visible;
                group.DialogLauncherCommand = ribbonUICommand;

                if (ribbonUICommand is IRibbonLinkedUICommand)
                    ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedRibbonGroup(group);

                ribbonUICommand.UseHotKeyInToolTip = GetShouldProcessHotKey(ribbonUICommand.HotKey);
                SetFrameworkElementScreenTip(group, ribbonUICommand.GetCleanToolTipTitle(), ribbonUICommand.GetCleanToolTipDescription(), false);

                 if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.DialogLaunchers))
                    RibbonKeyTipManager.RegisterTabLevelControl(group, ribbonUICommand.Label, ButtonSize.Small);

                RegisterControlHotKey(ribbonUICommand);
                RegisterDispatcherElement(ribbonUICommand);

                RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, group);
            }
        }

        /// <summary>
        /// Removes a dialog launcher (ICommand) from a Ribbon Group. This is the little launcher icon at the bottom right hand side of a ribbon group.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the group to remove the launcher from.</param>
        public void RemoveDialogLauncherFromRibbonGroup(string ribbonGroupName)
        {
            RadRibbonGroup group = GetRibbonGroup(ribbonGroupName);

            if (group.DialogLauncherCommand != null)
            {
                group.DialogLauncherVisibility = Visibility.Collapsed;
                group.DialogLauncherCommand = null;
            }
        }
        #endregion

        #region Get Methods (for getting named objects)
        /// <summary>
        /// Gets an existing tab control, previously added, by name.
        /// </summary>
        /// <param name="tabName">The name of the tab that was previously added.</param>
        /// <returns>The tab control. Will fail if you pass in the name of a different type of control.</returns>
        public RadRibbonTab GetRibbonTab(string tabName)
        {
            if (!GetControlExists(tabName))
                return null;

            return (RadRibbonTab)RibbonFrameworkElements[tabName];
        }

        /// <summary>
        /// Gets an existing ribbon group control, previously added, by name.
        /// A ribbon group is a group that groups buttons, button groups and has a label. It is added to tabs.
        /// </summary>
        /// <param name="ribbonGroupName">The name of the ribbon group that was previously added.</param>
        /// <returns>The ribbon group control. Will fail if you pass in the name of a different type of control.</returns>
        public RadRibbonGroup GetRibbonGroup(string ribbonGroupName)
        {
            if (!GetControlExists(ribbonGroupName))
                return null;

            return (RadRibbonGroup)RibbonFrameworkElements[ribbonGroupName];
        }

        /// <summary>
        /// Gets an existing wrap panel control, previously added, by name.
        /// A wrap panel is a panel that contains and arranges button groups. It prefers 2 horizontal lines, but reverts to 3 when less horizontal space is available.
        /// </summary>
        /// <param name="wrapPanelName">The name of the wrap panel that was previously added.</param>
        /// <returns>The wrap panel control. Will fail if you pass in the name of a different type of control.</returns>
        public RadOrderedWrapPanel GetWrapPanel(string wrapPanelName)
        {
            if (!GetControlExists(wrapPanelName))
                return null;

            return (RadOrderedWrapPanel)RibbonFrameworkElements[wrapPanelName];
        }

        /// <summary>
        /// Gets an existing button group control, previously added, by name.
        /// A button group is a group that groups buttons vertically, where possible, subject to button size. Used within a ribbon group.
        /// </summary>
        /// <param name="buttonGroupName">The name of the button group that was previously added.</param>
        /// <returns>The button group control. Will fail if you pass in the name of a different type of control.</returns>
        public RadRibbonButtonGroup GetButtonGroup(string buttonGroupName)
        {
            if (!GetControlExists(buttonGroupName))
                return null;

            return (RadRibbonButtonGroup)RibbonFrameworkElements[buttonGroupName];
        }

        /// <summary>
        /// Gets an existing horizontal button group control, previously added, by name.
        /// A horizontal button group is a group that groups buttons horizontally, irrespective of button size. Normally used within a wrap panel.
        /// </summary>
        /// <param name="buttonGroupName">The name of the horizontal button group that was previously added.</param>
        /// <returns>The horizontal button group control. Will fail if you pass in the name of a different type of control.</returns>
        public RadRibbonHorizontalButtonGroup GetHorizontalButtonGroup(string buttonGroupName)
        {
            if (!GetControlExists(buttonGroupName))
                return null;

            return (RadRibbonHorizontalButtonGroup)RibbonFrameworkElements[buttonGroupName];
        }

        /// <summary>
        /// Gets an existing combo box control, previously added, by name.
        /// </summary>
        /// <param name="comboBoxName">The name of the combo box that was previously added.</param>
        /// <returns>The combo box control. Will fail if you pass in the name of a different type of control.</returns>
        public RadComboBox GetComboBox(string comboBoxName)
        {
            if (!GetControlExists(comboBoxName))
                return null;

            FrameworkElement comboBox = RibbonFrameworkElements[comboBoxName];

            if (!(comboBox is RadComboBox))
                throw new Exception("Not a combo box element: " + comboBoxName); // I18N. OK. 

            return (RadComboBox)comboBox;
        }

        /// <summary>
        /// Gets an existing button control, previously added, by name.
        /// A button is a control that the user can interact with by clicking on it.
        /// </summary>
        /// <param name="buttonName">The name of the button that was previously added.</param>
        /// <returns>The button control. Will fail if you pass in the name of a different type of control.</returns>
        public Control GetButton(string buttonName)
        {
            if (!GetControlExists(buttonName))
                return null;

            FrameworkElement button = RibbonFrameworkElements[buttonName];

            if (button is IRibbonButton)
            {
                IRibbonButton ribbonButton = (IRibbonButton)button;

                RibbonButtonStyleInfo buttonStyleInfo = GetButtonStyleInfo(ribbonButton);
                if (buttonStyleInfo != null)
                    return (Control)button;
            }

            throw new Exception("Not a button element: " + buttonName); // I18N. OK.  
        }

        public RadMenuItem GetMenuItem(string menuItemName)
        {
            if (!GetControlExists(menuItemName))
                return null;

            FrameworkElement menuItem = RibbonFrameworkElements[menuItemName];
            if (menuItem is RadMenuItem)
                return (RadMenuItem)menuItem;

            throw new Exception("Not a menu item:  " + menuItemName);   //I18N. OK.
        }

        /// <summary>
        /// Gets extended information about an existing button control, previously added, by name.
        /// </summary>
        /// <param name="button">The button to get the extended style info for. Call GetButton to acquire this object by name.</param>
        /// <returns>The extended information about the button. Will return null if the control does not exist or is not a button.</returns>
        public RibbonButtonStyleInfo GetButtonStyleInfo(IRibbonButton button)
        {
            if (ButtonInformation.ContainsKey(button))
                return ButtonInformation[button];
            else
                return null;
        }

        /// <summary>
        /// Gets an existing back stage item control, previously added, by name.
        /// </summary>
        /// <param name="backStageItemName">The name of the back stage item that was previously added.</param>
        /// <returns>The back stage item. Will fail if you pass in the name of a different type of control.</returns>
        public RadRibbonBackstageItem GetBackStageItem(string backStageItemName)
        {
            if (!GetControlExists(backStageItemName))
                return null;

            FrameworkElement backStageItem = RibbonFrameworkElements[backStageItemName];

            if (backStageItem is RadRibbonBackstageItem)
                return (RadRibbonBackstageItem)backStageItem;

            throw new Exception("Not a back stage item element: " + backStageItemName); // I18N. OK.  
        }
        #endregion

        #region Other Methods
        /// <summary>
        /// Updates the non application title component of the Ribbon title.
        /// </summary>
        /// <param name="windowTitleText">The window title text."</param>
        public void UpdateWindowTitle(string windowTitleText)
        {
            Ribbon.Title = windowTitleText;
        }

        /// <summary>
        /// Saves ribbon settings.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but should be called in MainWindow_Closed for WPF.
        /// LoadRibbonSettings is not public because it is called internally.
        /// </summary>
        public void SaveRibbonSettings()
        {
            IList<IStorageElement> ribbonStorageElements = RibbonCallbacks.GetRibbonStorageElements();
            IStorageWriteContext storageWriteContext = RibbonCallbacks.GetStorageWriteContext();

            ribbonStorageElements.Add(RibbonGlobalUserSettingsManager);

            ribbonStorageElements.Add(DropDownToggleSettingsManager);
            UpdateDropDownToggleSettingsManager();
            
            StorageManagerHelper.SaveStorageManager(RibbonSettingsStorageManager, RibbonSettingsStorageFile, storageWriteContext);
        }

        /// <summary>
        /// Does any required post layout work that is required, such as applying or finalizing any user customizations.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but when using WPF should be called at the end of the
        /// implementation of RibbonCallbacks::LayoutRibbon().
        /// </summary>
        public void AfterRibbonLayoutComplete()
        {
            // TODO 3: <whar> In future may expose a hook to clients for any post-layout customization clean up or implementation, 
            // but for now a client is expected to use the builder methods that support IRibbonUIElement and derived interfaces and
            // which layout elements in the ribbon subject to user customizations.

            EnsureConsistentQuickAccessToolBarSeparatorVisibility();

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.QuickAccessToolbar))
                RibbonKeyTipManager.UpdateQuickAccessToolbarKeyTips(Ribbon.QuickAccessToolBar.Items);

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.TabsAndControls))
                RibbonKeyTipManager.AfterAllTabLevelControlsRegistered();

            UpdateDropDownToggleSettingsManagerDefaults();
            ApplyDropDownToggleButtonDefaultCommands();
        }

        /// <summary>
        /// Applies ribbon global settings such as the QAT position and minimized state and hooks necessary ribbon events to maintain track of that state
        /// plus hooks ribbon events for managing events exposed through IRibbonServices.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but when using WPF should be called after the parent form has loaded.
        /// Cannot call earlier than this, I don't think, due to layout logic initially requiring defaults, but feel free to try.
        /// </summary>
        public void AfterParentFormLoaded()
        {
            // Note: <whar> Cannot call this logic from AfterRibbonLayoutComplete() because it is too early in the initialization sequence.

            ApplyRibbonGlobalUserSettings();

            Ribbon.MinimizedChanged += Ribbon_MinimizedChanged;
            Ribbon.ToolBarPositionChanged += Ribbon_ToolBarPositionChanged;

            SetCurrentRibbonTab(Ribbon.SelectedTab);

            Ribbon.ApplicationMenuOpenStateChanged += Ribbon_ApplicationMenuOpenStateChanged;
            Ribbon.SelectionChanged += Ribbon_SelectionChanged;
        }

        /// <summary>
        /// Resets all ribbon customization managers to default settings and applies (except for managers that are instantiated in client code) all
        /// user customized ribbon settings. Clients are responsible for applying settings from managers that are instantiated in client code 
        /// such as the QATButtonManager.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but when using WPF, clients need to call this method in their implementation
        /// of IRibbonService.ResetToDefaults()
        /// </summary>
        public void ResetRibbonSettings()
        {
            IList<IStorageElement> ribbonStorageElements = RibbonCallbacks.GetRibbonStorageElements();
            ribbonStorageElements.Add(RibbonGlobalUserSettingsManager);
            ribbonStorageElements.Add(DropDownToggleSettingsManager);

            foreach (IStorageElement storageElement in ribbonStorageElements)
            {
                if (storageElement is RibbonCustomizationManagerBase)
                    ((RibbonCustomizationManagerBase)storageElement).ResetToDefaults();
            }

            ApplyRibbonGlobalUserSettings();
            ApplyDropDownToggleButtonDefaultCommands();
        }

        /// <summary>
        /// Executes any necessary post reset logic.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but when using WPF, clients need to call this method in their implementation
        /// of IRibbonService.ResetToDefaults()
        /// </summary>
        public void AfterResetRibbonSettings()
        {
            EnsureConsistentQuickAccessToolBarSeparatorVisibility();

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.QuickAccessToolbar))
                RibbonKeyTipManager.UpdateQuickAccessToolbarKeyTips(Ribbon.QuickAccessToolBar.Items);
        }

        /// <summary>
        /// Answers the default command name for the specified DropDownToggle button.
        /// *NOT* required to be called if using the TelerikWPFRibbonHost, but when using WPF, clients need to call this method in their implementation
        /// of IRibbonServices.ResetToDefaults()
        /// </summary>
        /// <param name="buttonName">The name of the DropDownToggle button to get the default command for.</param>
        /// <returns>The default command name, else String.Empty if the button does not exist, is not a DropDownToggle button or has no default command (which can happen if called too early).</returns>
        public string GetDefaultCommandNameForDropDownToggleButton(string buttonName)
        {
            Control button = GetButton(buttonName);
            if (button != null && button is RadRibbonToggleSplitButton)
            {
                IRibbonUICommand defaultCommand = ((RadRibbonToggleSplitButton)button).DefaultCommand;
                if (defaultCommand == null)
                    return String.Empty;
                else
                    return defaultCommand.Name;
            }
            else
                return String.Empty;
        }
        #endregion
        #endregion

        #region Internal Methods

        #region Hot Keys
        /// <summary>
        /// Process a key press for ribbon hot keys.
        /// </summary>
        /// <param name="key">The key that is associated with the key press.</param>
        /// <param name="modifierKeys">The modifier keys pressed at the time of the key press.</param>
        /// <returns>Whether the hot key was handled.</returns>
        internal bool HandleRadRibbonKeyPress(Key key, ModifierKeys modifierKeys)
        {
            if (GetShouldBlockHotKey())
                return false;

            string keyString = RibbonHotKeyHelper.ToString(modifierKeys, key);

            if (Ribbon.IsBackstageOpen)
            {
                if (BackstageHotKeyRegister.ContainsKey(keyString))
                {
                    BackstageHotKeyRegister[keyString].Execute(null);
                    return true;
                }
            }
            else if (RibbonHotKeyRegister.ContainsKey(keyString))
            {
                RibbonHotKeyRegister[keyString].Execute(null);
                return true;
            }

            return false;
        }
        #endregion

        internal void SetFocusToRibbon()
        {
            Ribbon.Focus();
        }
        #endregion

        #region Private Methods

        #region Hot Keys (Shared)

        private bool GetShouldBlockHotKey()
        {
            // Note: <whar> Same behavior as Office 2016. If editing the search term, hot keys don't work (except CTRL-A in the search text box).
            if (RibbonSearchManager.SearchComboIsActive)
                return true;

            if (!User32Dll.IsWindowEnabled(RibbonCallbacks.ParentFormHandle))
                return true;

            if (RibbonCallbacks.IsParentFormInLockDown)
                return true;

            return false;
        }

        private bool GetShouldProcessHotKey(RibbonHotKey hotKey)
        {
            if (!KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.HotKeys))
                return false;

            if (!hotKey.IsValidShortCut)
                return false;

            return true;
        }

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private void RegisterControlHotKey(IRibbonUICommand ribbonUICommand)
        {
            RegisterControlHotKey(ribbonUICommand, false);
        }

        private void RegisterControlHotKey(IRibbonUICommand ribbonUICommand, bool allowWhenBackstageIsOpen)
        {
            if (!GetShouldProcessHotKey(ribbonUICommand.HotKey))
                return;

            if (allowWhenBackstageIsOpen) // TODO 1: <whar> Is this ONLY allow when backstage open??? I think that it how it has been implemented.
            {
                if (!BackstageHotKeyRegister.ContainsKey(ribbonUICommand.HotKey.ToString()))
                    BackstageHotKeyRegister.Add(ribbonUICommand.HotKey.ToString(), ribbonUICommand);
            }
            else
            {
                if (!RibbonHotKeyRegister.ContainsKey(ribbonUICommand.HotKey.ToString()))
                    RibbonHotKeyRegister.Add(ribbonUICommand.HotKey.ToString(), ribbonUICommand);
            }
        }
        #endregion

        #region Initialization
        private void InitializeRibbon(string applicationName)
        {
            // Note: <whar> This seems to need to be the first thing done here, else the Help button does not get localized.
            InitializeLocalizationSupport();
            InitializeTheme();

            // Note: <whar> Settings are loaded well before any layout takes place, so that customizations can be applied during layout.
            // User customized layout is supported by builder methods working with IRibbonUIButton (or derived interfaces).
            // [Currently only QAT add methods support this, but it can be easily extended in future.]
            // It should also be pointed out that some customizations need to happen after layout, (QAT position, toggle button default 
            // commands etc) but those are taken care of generically, and do not require client side manager implementations (e.g., 
            // QATButtonManager needs the product command factory and so needs to be instantiated/initialized in higher layers.
            LoadRibbonSettings();

            const int SMALL_BUTTONS_PER_COLUMN = 3;
            RadCollapsiblePanel.SetSmallButtonsPerColumn(Ribbon, SMALL_BUTTONS_PER_COLUMN);
            Ribbon.ApplicationButtonContent = TextManager.Current["hfui_fileTabText"];
            Ribbon.AllowDrop = true;
            Ribbon.ApplicationName = String.Empty;

            UpdateWindowTitle(applicationName);

            InitializeQuickAccessToolBar();
            InitializeBackStage();
            InitializeTabStrip();
            InitializeFocusSettings();
            InitializeKeyboardSupport();
            InitializeHelpButton();
        }

        private void InitializeLocalizationSupport()
        {
            LocalizationManager.Manager = new RibbonLocalizationManager();
        }

        private void LoadRibbonSettings()
        {
            IList<IStorageElement> ribbonStorageElements = RibbonCallbacks.GetRibbonStorageElements();
            ribbonStorageElements.Add(RibbonGlobalUserSettingsManager);
            ribbonStorageElements.Add(DropDownToggleSettingsManager);

            // Note: <whar> Have to do this before reading, else if the read never happens (e.g., missing file) then the defaults never
            // get cached (for managers that can cache before loading).
            foreach (RibbonCustomizationManagerBase customizationManager in ribbonStorageElements)
                customizationManager.CacheDefaultElements();

            IStorageWriteContext storageWriteContext = RibbonCallbacks.GetStorageWriteContext();
            StorageManagerHelper.ReadStorageManager(RibbonSettingsStorageManager, RibbonSettingsStorageFile, ribbonStorageElements, storageWriteContext, true);
        }

        private void InitializeTheme()
        {
            #region WARNING ON THEMES (read if thinking of changing theme support)
            // TODO 1: <whar> There are two different theme mechanisms in Telerik.
            // 1. StyleManager based (which we are using now).
            // 2. Implicit themes; requires using noXaml binaries and then using specific build steps to include Xaml resources into a resource dictionary (using App.xaml).
            //
            // Earlier I was thinking that we'd need to use implicit themes to get some of the potential future behaviors that we want
            // such as the ability to change themes (e.g., touch theme) or for users to customize themes (although less likely that we'll do this).
            // NOW (as at 3-28-2016) I am thinking that the StyleManager themes are all we'll really need and we should be able to add Touch support
            // using the existing Windows8Touch theme (and tweaking it) rather than moving to implicit themes.
            //
            // Irrespective of the above, if you are working on themes, first talk to Whar to discuss firstly WHY, and secondly HOW.

            // EDIT: 4/22/2016.
            // Out of curosity and the fact the VisualStudio2013 theme that uStn uses is ONLY available when using Implict themes I gave this a real
            // serious go, to see what was necessary. I spent the best part of two days on it and came up empty handed. I could NOT get the Implicit themes
            // to work at all. Some details of this pain can be found here:
            // http://www.telerik.com/forums/how-to-use-the-noxaml-binaries#n_1O7gjwKkmixUQ_vOHFbA
            // 
            // Another thing I noticed is that Microstation which does seem to use Implicit themes, is shipping literally every Telerik dll.
            // This is OVER 100MB. I'm not sure if that's due to Implicit themes or not, but since the current Telerik footprint for us is only
            // 25% of that, I would be concerned if changing themeing mechanism alone, introduced this additional burden.

            // EDIT: 4/25/2016.
            // I did eventually get Implicit themes to work, with our own version of the Telerik code.
            // It required some additional hoop jumping to create a new SharedResources class that could load the Telerik xaml resources.
            // The theme works and runs, but there are *multiple regressions*. The backstage no longer works at all and none of our derived (enhanced)
            // Telerik controls draw, presumably due to lack of style being defined for those classes (but somehow it works fine with the StyleManager).
            // Right now I pretty much believe that the PAIN of going to Implicit themes is too high for the level of GAIN (I'm not actually sure what the
            // gain is, to be honest; all I see are problems). I would think that since we have Telerik source we can pretty much do what we want using the 
            // existing StyleManager. Maybe in future when we are developing custom WPF controls that we want to inherit from Telerik themes we will change 
            // our minds, but then again, we might not either.
            // (These changes are shelved in change-list 210869; may require syncing back to around the date of the shelve to test without merge conflicts.)
            #endregion

            // Note: <whar> For some reason, that only Telerik will be able to tell you, we need to have BOTH these lines, for things to work out.
            StyleManager.ApplicationTheme = TelerikTheme;
            StyleManager.SetTheme(Ribbon, TelerikTheme);
        }

        private void InitializeQuickAccessToolBar()
        {
            Ribbon.QuickAccessToolBar = new QuickAccessToolBar();
            Ribbon.QuickAccessToolBar.Loaded += QuickAccessToolBar_Loaded;
            Ribbon.QuickAccessToolBar.Focusable = false; // Note: <whar> Stop the focus rectangle when using keyboard support.
        }

        private void InitializeBackStage()
        {
            Ribbon.Backstage = new RadRibbonBackstage();
            Ribbon.Backstage.BorderThickness = new Thickness(0.0);
            Ribbon.Backstage.Margin = new Thickness(0.0);
            Ribbon.Backstage.BackstagePosition = BackstagePosition.Office2013;
            Ribbon.Backstage.Focusable = false;

            if (RibbonExternalConfiguration.Instance.RibbonDebuggingEnabled)
            {
                Ribbon.Backstage.Background = Brushes.Green;
                AddBackArrowBackStageItem(Colors.Black, Colors.Goldenrod);
            }
            else
            {
                Brush backStageBackBrush = new SolidColorBrush(Windows8Palette.Palette.AccentColor);
                Ribbon.Backstage.Background = backStageBackBrush;
                AddBackArrowBackStageItem(Windows8Palette.Palette.MainColor, Windows8Palette.Palette.BasicColor);
            }          
        }

        private RadRibbonBackstageItem AddBackArrowBackStageItem(Color normalColor, Color highlightColor)
        {
            const string BACKARROW_BACKSTAGEITEM_NAME = "__backStageBackButton__"; // I18N. OK.
            CheckControlName(BACKARROW_BACKSTAGEITEM_NAME);

            RadRibbonBackstageItem backStageItem = new BackArrowRadRibbonBackstageItem(normalColor, highlightColor);
            backStageItem.Name = BACKARROW_BACKSTAGEITEM_NAME;
            backStageItem.Header = String.Empty;
            backStageItem.CloseOnClick = true;
            backStageItem.IsSelectable = false;
            backStageItem.IsTabStop = false;
            backStageItem.Focusable = false;

            Ribbon.Backstage.Items.Add(backStageItem);

            RegisterFrameworkElement(backStageItem);

            return backStageItem;
        }

        private void InitializeTabStrip()
        {
            Ribbon.HelpButtonVisibility = Visibility.Visible;
            Ribbon.MinimizeButtonVisibility = Visibility.Visible;
            InitializeTopTabStripAdditionalContent();
        }

        private void InitializeTopTabStripAdditionalContent()
        {
            StackPanel stackPanel = new StackPanel();
            stackPanel.Orientation = Orientation.Horizontal;
            Ribbon.TabStripAdditionalContent = stackPanel;

            stackPanel = new StackPanel();
            stackPanel.Orientation = Orientation.Horizontal;
            // NET6MIGRATION
            Ribbon.TabStripAdditionalContent = stackPanel;

            InitializeRibbonSearchCombo();
        }

        private void InitializeRibbonSearchCombo()
        {
            const double WIDTH = 200.0;
            const double MAXIMUM_DROP_DOWN_HEIGHT = 400.0;
            const double FONT_SIZE = 11.0;
            const string SEARCH_COMBO_NAME = "__search_combo"; // I18N. OK.

            RadComboBox searchCombo = AddComboBoxToTopTabStrip(
                SEARCH_COMBO_NAME, TextManager.Current["hfui_searchComboToolTipTitle"], TextManager.Current["hfui_searchComboToolTipDescription"],
                WIDTH, Double.NaN, Double.NaN, MAXIMUM_DROP_DOWN_HEIGHT, FONT_SIZE);

            m_ribbonSearchManager = new RibbonSearchManager(searchCombo, Ribbon, this, this);
        }

        private void InitializeFocusSettings()
        {
            if (KeyboardNavigationSupportFlags > KeyNavSupportFlags.HotKeys)
            {
                // Note: <whar> Part of a solution for getting hosted WPF to react right off the bat, to keyboard events.
                // https://jasonalls.wordpress.com/2012/08/10/capturing-wpf-usercontrol-key-events-when-hosted-in-a-winforms-elementhost-control/
                Ribbon.Focusable = true;

                Ribbon.FocusVisualStyle = null;
            }
        }

        private void InitializeKeyboardSupport()
        {
            // Note: <whar> Only need this for RibbonNavigation and higher (HotKeys does not rely on it).
            if (KeyboardNavigationSupportFlags >= KeyNavSupportFlags.RibbonNavigation)
            {
                // NET6MIGRATION
                Telerik.Windows.Controls.RibbonView.KeyTipService.SetIsKeyTipsEnabled(Ribbon, true);

                IList<KeyGesture> accessKeys = new List<KeyGesture>();
                accessKeys.Add(new KeyGesture(Key.LeftAlt));
                accessKeys.Add(new KeyGesture(Key.RightAlt));
                Telerik.Windows.Controls.RibbonView.KeyTipService.SetAccessKeys(Ribbon, accessKeys);

                RibbonKeyTipManager.RegisterFileTab(Ribbon.Backstage, TextManager.Current["hfui_fileTabText"]);

                // TODO 1: <whar> It's the dream to switch from keyboard hook based hot keys to the alternate method, but that method does not
                // seem to handle ALT only hot keys so well, which is needed for key tips to work propertly with the Ribbon, so for now, unless there
                // is pressure to move to the alternate method (some intractable issue) then I think I'll be leaving it as-is. TFS#479810 may have to
                // stay around for now.

                RegisterControlHotKey(new RibbonLeftAltNavigationActivationUICommand(Ribbon));
                RegisterControlHotKey(new RibbonRightAltNavigationActivationUICommand(Ribbon));
            }
            else
                RegisterControlHotKey(new RibbonShowBackstageUICommand(Ribbon)); // Note: <whar> Alt+F to show the backstage, if not provided through key tip support.

            RegisterControlHotKey(new RibbonSearchUICommand(RibbonSearchManager.SearchCombo));
        }

        private void InitializeHelpButton()
        {
            // Note: <whar> I swear that Telerik deliberately make what should be trivial, difficult, as some form of perverted pleasure.
            // No, the obvious approach of setting the Command on the HelpButton DOES NOT WORK. Why would it? You have to set a HelpCommand
            // property on the Ribbon instead. Makes perfect sense, to Telerik.
            //Ribbon.HelpButton.Command = RibbonCallbacks.GetProductHelpCommand(); // What you expect to do.
            Ribbon.HelpCommand = RibbonCallbacks.GetProductHelpCommand(); // What you actually need to do.

            Ribbon.HelpButtonImageSource = GetImage(false, "help"); // I18N. OK (No reference to Haestad.Framework.Windows.Forms and we don't want to add one).

            IRibbonUICommand ribbonUICommand = Ribbon.HelpCommand as IRibbonUICommand;
            if (ribbonUICommand != null)
            {
                RegisterDispatcherElement(ribbonUICommand);
                // NET6MIGRATION
                //if (ribbonUICommand is IRibbonLinkedUICommand)
                //    ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedRegularButton((RadRibbonButton)Ribbon.HelpButton);

                // Note: <whar> Uncomment to support searching the Ribbon for the Help button immediately to the right of the search combo.
                // RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, Ribbon.HelpButton as FrameworkElement, String.Empty);
            }
        }

        private RibbonGlobalUserSettingsManager InitializeRibbonGlobalUserSettingsManager()
        {
            RibbonGlobalUserSettingsManager ribbonGlobalUserSettingsManager = new RibbonGlobalUserSettingsManager();
            ribbonGlobalUserSettingsManager.AddRibbonDefaultSettings(false, true);
            return ribbonGlobalUserSettingsManager;
        }

        private DropDownToggleSettingsManager InitializeDropDownToggleSettingsManager()
        {
            // Note: <whar> Default commands are implicitly the first menu command added to a drop down toggle button, so we need
            // to do nothing at all here, other than return an empty manager. If there are saved settings they'll be loaded, else
            // we acquire the harded coded defaults applied by the layout code and the above mentioned implicit logic.
            return new DropDownToggleSettingsManager();
        }

        private void InitializeStorageManager()
        {
            const string fileName = "Haestad.Framework.UI.Integration.RibbonSettings.xml"; // DO NOT LOCALIZE <whar>
            string settingsPath = Path.Combine(RibbonCallbacks.LocalUserSettingsDirectory, fileName);

            if (File.Exists(fileName))
                FileLibrary.SetReadWriteSafely(fileName);

            m_ribbonSettingsStorageFile = new XmlStorageFileBase("RibbonSettings", settingsPath); // DO NOT LOCALIZE <whar>
            m_ribbonSettingsStorageManager = new StorageManagerBase(m_ribbonSettingsStorageFile);
        }
        #endregion

        #region Themes
        /// <summary>
        /// Applies to any Label controls found in the user control, the appropriate text color according to the ribbon theme.
        /// </summary>
        /// <param name="userControl"></param>
        private void ApplyThemeLabelColors(UserControl userControl)
        {
            if (userControl.Content == null)
                return;

            UIElement uiElement = userControl.Content as UIElement;
            if (uiElement == null)
                return;

            SetStylesForLabels(uiElement);
        }

        private void SetStylesForLabels(UIElement uiElement)
        {
            if (uiElement is Panel)
            {
                Panel panel = uiElement as Panel;
                UIElementCollection uiElementCollection = panel.Children;

                foreach (UIElement child in uiElementCollection)
                    SetStylesForLabels(child);
            }
            else if (uiElement is System.Windows.Controls.Label)
            {
                System.Windows.Controls.Label label = uiElement as System.Windows.Controls.Label;
                label.Style = NewLabelForegroundStyle(typeof(System.Windows.Controls.Label));
            }
            else if (uiElement is ContentControl)
            {
                ContentControl contentControl = uiElement as ContentControl;
                if (contentControl.Content == null)
                    return;

                UIElement subContent = contentControl.Content as UIElement;
                if (subContent == null)
                    return;

                SetStylesForLabels(subContent);
            }
        }

        private Style NewLabelForegroundStyle(Type controlType)
        {
            Style style = new Style(controlType);
            style.Setters.Add(new Setter(System.Windows.Controls.Label.ForegroundProperty, new SolidColorBrush(RibbonExternalConfiguration.Instance.StrongColor)));
            return style;
        }
        #endregion

        private void CleanUpSeparators(ItemCollection menuItems, RadMenuItem menuItem)
        {
            // Can have 3 possible cases where the menuItem in question has a separator directly below it.

            // Case 1
            // ------------------------------------
            //       [ Menu Item           ]
            //       -----------------------    <-- Separator (needs to be removed when menu is removed)
            //       [ Other Menu Item     ]

            // Case 2
            // ------------------------------------
            //       [ Other Menu Item     ]
            //       -----------------------    <-- Separator
            //       [ Menu Item           ]
            //       -----------------------    <-- Separator (needs to be removed when menu is removed)
            //       [ Other Menu Item     ]

            // Case 3
            // ------------------------------------
            //       [ Other Menu Item     ]
            //       [ Menu Item           ]
            //       -----------------------    <-- Separator (should *not* be removed when menu is removed)
            //       [ Other Menu Item     ]

            // In case 1 and 2 we remove the separator below.
            // In case 3 we leave the separator in place.

            int contextMenuIndex = menuItems.IndexOf(menuItem);
            int indexAbove = contextMenuIndex - 1;
            int indexBelow = contextMenuIndex + 1;

            bool itemBelowIsSeparator = (menuItems.Count > indexBelow && menuItems[indexBelow] is RadMenuSeparatorItem);
            if (!itemBelowIsSeparator)
                return; // Not any of the above listed cases.

            // Is either case 1, 2 or 3 if we get here.
            // So check for case 3. If not case 3, remove the separator.

            bool itemAboveIsMenu = (indexAbove > -1 && menuItems.Count > indexAbove && menuItems[indexAbove] is RadMenuItem);
            bool isCase3 = itemAboveIsMenu && itemBelowIsSeparator;

            if (!isCase3)
            {
                RadMenuSeparatorItem separatorBelow = menuItems[indexBelow] as RadMenuSeparatorItem;
                if (separatorBelow != null)
                    menuItems.Remove(separatorBelow);
            }
        }

        private void AddTopTabStripPaddingElement()
        {
            System.Windows.Controls.Label label = new System.Windows.Controls.Label();
            const double CONTROL_PADDING = 8.0;
            label.Width = CONTROL_PADDING;

            StackPanel stackPanel = Ribbon.TabStripAdditionalContent as StackPanel;
            stackPanel.Children.Insert(0, label);
        }

        private void AddTopLevelMenuItem(Control button, RibbonButtonStyleInfo buttonStyleInfo, RadMenuItem menuItem, bool separatorAfter)
        {
            RadContextMenu contextMenu = TelerikLibrary.GetContextMenu(button, buttonStyleInfo, new RoutedEventHandler(ContextMenu_Closed));

            contextMenu.Items.Add(menuItem);
            SetAccessKeyForTopLevelMenuItem(contextMenu, menuItem);

            if (separatorAfter)
                contextMenu.Items.Add(new RadMenuSeparatorItem());

            if (menuItem is RadToggleMenuItem && button is RadRibbonToggleSplitButton && ((RadRibbonToggleSplitButton)button).HasNoDefaultCommand)
                ((RadRibbonToggleSplitButton)button).SetDefaultCommand(menuItem.Command as IRibbonUICommand);
        }

        private void SetAccessKeyForTopLevelMenuItem(RadContextMenu contextMenu, RadMenuItem menuItem)
        {
            if (!KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.Menus))
                return;

            int count = TelerikLibrary.GetCountOfTopLevelMenuItems(contextMenu);
            // NET6MIGRATION
            Telerik.Windows.Controls.RibbonView.KeyTipService.SetAccessText(menuItem, count.ToString());
        }

        private void AddSubLevelMenuItem(RadMenuItem parentMenuItem, SubMenuItem subMenuItem, bool separatorAfter)
        {
            parentMenuItem.Items.Add(subMenuItem);

            subMenuItem.ParentMenuItem = parentMenuItem;

            SetAccessKeyForSubLevelMenuItem(parentMenuItem, subMenuItem);

            if (separatorAfter)
                parentMenuItem.Items.Add(new RadMenuSeparatorItem());
        }

        private void SetAccessKeyForSubLevelMenuItem(RadMenuItem parentMenuItem, RadMenuItem subMenuItem)
        {
            if (!KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.Menus))
                return;

            int count = TelerikLibrary.GetCountOfSubMenuItems(parentMenuItem);
            // NET6MIGRATION
            Telerik.Windows.Controls.RibbonView.KeyTipService.SetAccessText(subMenuItem, count.ToString());
        }

        // TODO 1: <whar> At some point we may wish to use IRibbonUICommand here, but it might be a special version for combos.
        private RadComboBox NewRadComboBox(string newComboBoxName, string toolTipTitle, string toolTipDescription,
            double width, double height, double dropDownWidth, double maximumDropDownHeight, double fontSize)
        {
            RadComboBox radComboBox = new RadComboBox();
            radComboBox.Name = newComboBoxName;

            radComboBox.Width = width;
            radComboBox.Height = height;

            if (!Double.IsNaN(dropDownWidth))
                radComboBox.DropDownWidth = new GridLength(dropDownWidth, GridUnitType.Pixel);

            radComboBox.MaxDropDownHeight = maximumDropDownHeight;

            radComboBox.HorizontalAlignment = HorizontalAlignment.Left;
            radComboBox.HorizontalContentAlignment = HorizontalAlignment.Left;
            radComboBox.VerticalAlignment = VerticalAlignment.Bottom;
            radComboBox.FontSize = fontSize;

            SetFrameworkElementScreenTip(radComboBox, toolTipTitle, toolTipDescription, false);

            return radComboBox;
        }

        private IRibbonButton NewButton(IRibbonUICommand ribbonUICommand, string buttonLabelOverride, ButtonSize buttonSize, ButtonStyle buttonStyle, string newButtonName, bool isQuickAccessButton)
        {
            bool uniqueNameSpecified = newButtonName != String.Empty;
            string buttonName = CheckControlName(uniqueNameSpecified ? newButtonName : GetNewControlName(ribbonUICommand));

            BitmapSource buttonImage = null;
            string buttonStandardGraphicResourceKey = buttonSize == ButtonSize.Large ? ribbonUICommand.LargeGraphicResourceKey : ribbonUICommand.GraphicResourceKey;
            if (!StringLibrary.IsBlank(buttonStandardGraphicResourceKey) && buttonStyle != ButtonStyle.RegularTextOnly)
                buttonImage = GetImage(buttonSize == ButtonSize.Large, buttonStandardGraphicResourceKey);

            IRibbonButton button = null;
            switch (buttonStyle)
            {
                case ButtonStyle.DropDownOnly:
                    button = new RadRibbonDropDownButtonEx();
                    if (ribbonUICommand is IRibbonLinkedUICommand)
                        ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedDropDownOnlyButton((RadRibbonDropDownButton)button);
                    SetRadDropDownButtonElementProperties((RadRibbonDropDownButton)button, buttonSize, buttonName, GetButtonLabel(ribbonUICommand, buttonLabelOverride), buttonImage);

                    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                    // Note: <whar> Kris added this line. It wasn't really needed before (and if you have a command that shows UI such as a dialog, 
                    // it's real funky since that will open when clicking on the dropdown; normally nothing is supposed to happen other than the
                    // drop down menu appearing).
                    // Kris says: "I did have to make a tweak in H.F.UI.Integration so the command is assigned to a DropDownOnly button. Without it
                    // the enable state was not being set since it needs the ICommand to call CanExecute."
                    ((RadRibbonDropDownButton)button).Command = ribbonUICommand;
                    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

                    // Note: <whar> Related to the above change, this is now needed to keep these "no effect" commands from showing in Ribbon Search.
                    if (ribbonUICommand is IRibbonSearchLinkedUICommand)
                        ((IRibbonSearchLinkedUICommand)ribbonUICommand).HiddenFromSearch = true;

                    break;

                case ButtonStyle.DropDown:
                case ButtonStyle.DropDownToggle:
                    if (buttonStyle == ButtonStyle.DropDownToggle)
                    {
                        button = new RadRibbonToggleSplitButton(this, RibbonCallbacks, uniqueNameSpecified);
                        if (ribbonUICommand is IRibbonLinkedUICommand)
                            ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedDropDownToggleButton((RadRibbonToggleSplitButton)button);
                    }
                    else
                    {
                        button = new RadRibbonSplitButtonEx();
                        if (ribbonUICommand is IRibbonLinkedUICommand)
                            ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedDropDownButton((RadRibbonSplitButton)button);
                    }
                    SetRadSplitButtonElementProperties((RadRibbonSplitButton)button, buttonSize, buttonName, GetButtonLabel(ribbonUICommand, buttonLabelOverride), buttonImage);
                    ((RadRibbonSplitButton)button).Command = ribbonUICommand;
                    ((RadRibbonSplitButton)button).Click += Button_Click;
                    break;

                case ButtonStyle.Toggle:
                    button = new RadRibbonToggleButtonEx();
                    if (ribbonUICommand is IRibbonLinkedUICommand)
                        ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedToggleButton((RadRibbonToggleButtonEx)button);
                    SetRadToggleButtonProperties((RadRibbonToggleButton)button, buttonSize, buttonName, GetButtonLabel(ribbonUICommand, buttonLabelOverride), buttonImage);
                    ((RadRibbonToggleButton)button).Command = ribbonUICommand;
                    ((RadRibbonToggleButtonEx)button).InternalClick += Button_Click;
                    break;

                case ButtonStyle.Regular:
                case ButtonStyle.RegularTextOnly:
                    button = new RadRibbonButtonEx();
                    if (ribbonUICommand is IRibbonLinkedUICommand)
                        ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedRegularButton((RadRibbonButton)button);
                    SetRadRibbonButtonProperties((RadRibbonButton)button, buttonSize, buttonName, GetButtonLabel(ribbonUICommand, buttonLabelOverride), buttonImage);
                    ((RadRibbonButton)button).Command = ribbonUICommand;
                    ((RadRibbonButton)button).Click += Button_Click;
                    break;
            }

            ribbonUICommand.UseHotKeyInToolTip = GetShouldProcessHotKey(ribbonUICommand.HotKey);
            SetFrameworkElementScreenTip((FrameworkElement)button, ribbonUICommand.GetCleanToolTipTitle(), ribbonUICommand.GetCleanToolTipDescription(), isQuickAccessButton);

            RegisterControlHotKey(ribbonUICommand);
            RegisterDispatcherElement(ribbonUICommand);
            RegisterButton(button, new RibbonButtonStyleInfo(buttonSize, buttonStyle));
            RibbonSearchManager.AddReferenceToCommand(ribbonUICommand, button as FrameworkElement, buttonLabelOverride);

            return button;
        }

        private void AfterAddButtonToRibbonTab(IRibbonUICommand ribbonUICommand, IRibbonButton button, ButtonSize buttonSize)
        {
            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.TabsAndControls))
                RibbonKeyTipManager.RegisterTabLevelControl((Control)button, buttonSize == ButtonSize.Small ? ribbonUICommand.Label : button.Text, buttonSize);
        }

        private string GetButtonLabel(IRibbonUICommand ribbonUICommand, string buttonLabelOverride)
        {
            if (StringLibrary.IsBlank(buttonLabelOverride))
                return ribbonUICommand.GetCleanLabel();

            return StringLibrary.CleanTextResource(buttonLabelOverride);
        }

        private RadRibbonBackstageItem AddBackstageItem(IRibbonUICommand ribbonUICommand, bool separatorAfter, bool closeOnClick)
        {
            string backStageItemName = GetNewControlName(ribbonUICommand);

            RadRibbonBackstageItem backStageItem = new RadRibbonBackstageItem();
            backStageItem.Name = backStageItemName;
            backStageItem.Header = ribbonUICommand.GetCleanLabel();
            backStageItem.CloseOnClick = closeOnClick;
            backStageItem.IsSelectable = false;
            backStageItem.IsTabStop = false;
            backStageItem.Focusable = false;
            backStageItem.Command = ribbonUICommand;

            if (RibbonExternalConfiguration.Instance.RibbonDebuggingEnabled)
            {
                backStageItem.Background = Brushes.Red;
                backStageItem.HeaderForeground = Brushes.Yellow;
            }
            else
            {
                backStageItem.Background = new SolidColorBrush(Windows8Palette.Palette.AccentColor);
                backStageItem.HeaderForeground = new SolidColorBrush(Windows8Palette.Palette.MainColor);
            }

            Ribbon.Backstage.Items.Add(backStageItem);

            if (separatorAfter)
            {
                RadRibbonBackstageItem separator = new RadRibbonBackstageItem();
                separator.IsGroupSeparator = true;
                Ribbon.Backstage.Items.Add(separator);
            }

            if (ribbonUICommand is IRibbonLinkedUICommand)
                ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedBackstageItem(backStageItem);

            if (KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags.Backstage))
                RibbonKeyTipManager.RegisterBackstageItem(backStageItem, ribbonUICommand.Label);

            RegisterControlHotKey(ribbonUICommand, true);
            RegisterDispatcherElement(ribbonUICommand);
            RegisterFrameworkElement(backStageItem);

            return backStageItem;
        }

        private RadToggleMenuItem NewToggleMenuItem(IRibbonUICommand ribbonUICommand, RadRibbonToggleSplitButton ownerButton, string newMenuItemName)
        {
            // TODO 1: <whar> If we want to support nested (sub) toggle menu items, will need to work on a ToggleSubMenuItem class.
            // It is doubtful, however, that we will need this support.

            RadToggleMenuItem menuItem = new RadToggleMenuItem(ownerButton);
            ribbonUICommand.UseHotKeyInToolTip = GetShouldProcessHotKey(ribbonUICommand.HotKey); // Note: <whar> Must be before SetMenuItemProperties.
            SetMenuItemProperties(menuItem, ribbonUICommand, newMenuItemName, false);
            if (ribbonUICommand is IRibbonLinkedUICommand)
                ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedToggleMenu(menuItem);
            return menuItem;
        }

        private RadMenuItem NewMenuItem(IRibbonUICommand ribbonUICommand, string newMenuItemName, bool checkedMenuItem, bool isSubMenuItem)
        {
            RadMenuItem menuItem = null;
            if (isSubMenuItem)
                menuItem = new SubMenuItem();
            else
                menuItem = new RadMenuItem();

            ribbonUICommand.UseHotKeyInToolTip = GetShouldProcessHotKey(ribbonUICommand.HotKey); // Note: <whar> Must be before SetMenuItemProperties.
            SetMenuItemProperties(menuItem, ribbonUICommand, newMenuItemName, checkedMenuItem);
            if (ribbonUICommand is IRibbonLinkedUICommand)
            {
                if (checkedMenuItem)
                    ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedCheckedMenu(menuItem);
                else
                    ((IRibbonLinkedUICommand)ribbonUICommand).AddLinkedMenu(menuItem);
            }
            return menuItem;
        }

        private void SetMenuItemProperties(RadMenuItem menuItem, IRibbonUICommand ribbonUICommand, string newMenuItemName, bool checkedMenuItem)
        {
            menuItem.HorizontalContentAlignment = HorizontalAlignment.Stretch;
            menuItem.Header = NewMenuItemHeader(menuItem, ribbonUICommand);

            if (!StringLibrary.IsBlank(ribbonUICommand.GraphicResourceKey) && !checkedMenuItem)
            {
                const int SMALL_ICON_WIDTH = 16;
                const int SMALL_ICON_HEIGHT = 16;
                Image image = new Image();
                image.Source = ImageLibrary.ConvertBitmapToBitmapSource(RibbonCallbacks.GetIcon(ribbonUICommand.GraphicResourceKey));
                image.Width = SMALL_ICON_WIDTH;
                image.Height = SMALL_ICON_HEIGHT;
                menuItem.Icon = image;
            }

            menuItem.IsChecked = false;
            menuItem.Name = CheckControlName(newMenuItemName == String.Empty ? GetNewControlName(ribbonUICommand) : newMenuItemName);
            menuItem.Click += MenuItem_Click; // Note: <whar> Important. Hook this even BEFORE the client handler so we can close the context menu
            // before the client click handler is executed, else the context menu stays open!!

            menuItem.Command = ribbonUICommand;
        }

        // If you think Telerik would provide the this support, think again.
        // http://www.telerik.com/forums/radmenu-item-how-do-we-display-accesskey-next-to-the-menuitem-label (no answer)
        // http://www.telerik.com/forums/show-hotkeys-in-radmenuitem (people keep saying this should be standard, Telerik keep providing excuses).
        // At least WPF is pretty cool, so long as we resist the urge to use that XAML guff, at least on simple UI tweaks and layouts.
        private Grid NewMenuItemHeader(RadMenuItem menuItem, IRibbonUICommand ribbonCommand)
        {
            Grid grid = new Grid();

            ColumnDefinition starColumn = new ColumnDefinition();
            grid.ColumnDefinitions.Add(starColumn);

            ColumnDefinition autoColumn = new ColumnDefinition();
            autoColumn.Width = GridLength.Auto;
            grid.ColumnDefinitions.Add(autoColumn);

            TextBlock textBlock = new TextBlock();
            grid.Children.Add(textBlock);
            textBlock.Text = ribbonCommand.GetCleanLabel();

            if (ribbonCommand.UseHotKeyInToolTip && ribbonCommand.HotKey.IsValidShortCut)
            {
                const double MINIMUM_LEFT_PADDING = 48.0;
                textBlock = new TextBlock();
                textBlock.Margin = new Thickness(MINIMUM_LEFT_PADDING, 0.0, 0.0, 0.0);
                textBlock.TextAlignment = TextAlignment.Right;
                textBlock.HorizontalAlignment = HorizontalAlignment.Stretch;
                grid.Children.Add(textBlock);
                textBlock.Text = ribbonCommand.HotKey.ToString();
                Grid.SetColumn(textBlock, 1);
            }

            return grid;
        }

        private bool CheckImageCache(IDictionary<string, BitmapSource> images, string standardGraphicResourceKey)
        {
            if (!images.ContainsKey(standardGraphicResourceKey))
            {
                System.Drawing.Icon icon = RibbonCallbacks.GetIcon(standardGraphicResourceKey);
                if (icon == null)
                    return false;
                else
                    images.Add(standardGraphicResourceKey, ImageLibrary.ConvertBitmapToBitmapSource(icon));
            }

            return true;
        }

        private void CheckButtonIsDropDown(RibbonButtonStyleInfo buttonStyleInfo)
        {
            if (buttonStyleInfo.ButtonStyle != ButtonStyle.DropDown && buttonStyleInfo.ButtonStyle != ButtonStyle.DropDownOnly && buttonStyleInfo.ButtonStyle != ButtonStyle.DropDownToggle)
                throw new NotSupportedException("Only buttons of type DropDown, DropDownOnly and DropDownToggle support menus."); // I18N. OK.
        }

        private void CheckButtonIsNotDropDownToggle(RibbonButtonStyleInfo buttonStyleInfo)
        {
            if (buttonStyleInfo.ButtonStyle == ButtonStyle.DropDownToggle)
                throw new Exception("Checked menu items not supported for ButtonStyle.DropDownToggle"); // I18N. OK.
        }

        private void CheckAllRequiredParametersForDropDownToggleButton(RibbonButtonStyleInfo buttonStyleInfo, string toggleToolTipTitle, RibbonHotKey hotKey)
        {
            if (buttonStyleInfo.ButtonStyle != ButtonStyle.DropDownToggle)
                return;

            if (hotKey.IsValidShortCut)
            {
                // Note: <whar> Reason being, the hot key needs to be shown in the tooltip for the default DropDownToggle button command.
                // Having a hot key with no tooltip to show the user the hot key, is funky.

                if (StringLibrary.IsBlank(toggleToolTipTitle))
                    throw new NotSupportedException("If specifying a hot key for a menu item associated with a DownToggleButton, you must also specify the tooltip " +
                        "so that the hot key can be displayed in the tooltip when the menu item is the default command."); // I18N. OK.
            }
        }

        // Note: <whar> SetRadSplitButtonElementProperties, SetRadDropDownButtonElementProperties, SetRadRibbonButtonProperties and SetRadToggleButtonProperties
        // have identical implementations, however, there is no common base class for all button types that I can pass into a single generic method. There is an interface, IRibbonButton,
        // that has most (but not all) of the properties that I need to set, but it is a read-only interface! Thus, due to Telerik, I have to write the same method four times to support
        // the four different concrete types of button. The only way to fix this would be to edit the Telerik source and make IRibbonButton support property setters, but then there are
        // properties like "IsAutoSize" that are not common to any class or interface, so I'd need four overloads for that anyway. Ugh. Telerik, you stink!

        private void SetRadSplitButtonElementProperties(RadRibbonSplitButton button, ButtonSize buttonSize, string name, string buttonText, BitmapSource buttonImage)
		{	
            button.Name = name;
			button.IsAutoSize = false;

			switch (buttonSize)
			{
				case ButtonSize.Small:
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Small;
					button.SmallImage = buttonImage;
					break;
				case ButtonSize.Medium:
					button.CollapseToSmall = CollapseThreshold.WhenGroupIsSmall;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Medium;
					button.SmallImage = buttonImage;
					button.Text = buttonText;
					break;
				case ButtonSize.Large:
					button.CollapseToSmall = CollapseThreshold.Never;
					button.CollapseToMedium = CollapseThreshold.Never;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Large;
					button.LargeImage = buttonImage;
					button.Text = buttonText;
					const double LARGE_BUTTON_TOP_PADDING = 6.0;
					button.Padding = new Thickness(0.0, LARGE_BUTTON_TOP_PADDING, 0.0, 0.0);
					break;
			}
		}

		private void SetRadDropDownButtonElementProperties(RadRibbonDropDownButton button, ButtonSize buttonSize, string name, string buttonText, BitmapSource buttonImage)
		{
			button.Name = name;
			button.IsAutoSize = false;

			switch (buttonSize)
			{
				case ButtonSize.Small:
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Small;
					button.SmallImage = buttonImage;
					break;
				case ButtonSize.Medium:
					button.CollapseToSmall = CollapseThreshold.WhenGroupIsSmall;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Medium;
					button.SmallImage = buttonImage;
					button.Text = buttonText;
					break;
				case ButtonSize.Large:
					button.CollapseToSmall = CollapseThreshold.Never;
					button.CollapseToMedium = CollapseThreshold.Never;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Large;
					button.LargeImage = buttonImage;
					button.Text = buttonText;
					const double LARGE_BUTTON_TOP_PADDING = 6.0;
					button.Padding = new Thickness(0.0, LARGE_BUTTON_TOP_PADDING, 0.0, 0.0);
					break;
			}
		}

		private void SetRadRibbonButtonProperties(RadRibbonButton button, ButtonSize buttonSize, string name, string buttonText, BitmapSource buttonImage)
		{
			button.Name = name;
			button.IsAutoSize = false;

			switch (buttonSize)
			{
				case ButtonSize.Small:
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Small;
					button.SmallImage = buttonImage;
					break;
				case ButtonSize.Medium:
					button.CollapseToSmall = CollapseThreshold.WhenGroupIsSmall;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Medium;
					button.SmallImage = buttonImage;
					button.Text = buttonText;
					break;
				case ButtonSize.Large:
					button.CollapseToSmall = CollapseThreshold.Never;
					button.CollapseToMedium = CollapseThreshold.Never;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Large;
					button.LargeImage = buttonImage;
					button.Text = buttonText;
					const double LARGE_BUTTON_TOP_PADDING = 6.0;
					button.Padding = new Thickness(0.0, LARGE_BUTTON_TOP_PADDING, 0.0, 0.0);
					break;
			}
		}

		private void SetRadToggleButtonProperties(RadRibbonToggleButton button, ButtonSize buttonSize, string name, string buttonText, BitmapSource buttonImage)
		{
			button.Name = name;
			button.IsAutoSize = false;

			switch (buttonSize)
			{
				case ButtonSize.Small:
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Small;
					button.SmallImage = buttonImage;
					break;
				case ButtonSize.Medium:
					button.CollapseToSmall = CollapseThreshold.WhenGroupIsSmall;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Medium;
					button.SmallImage = buttonImage;
					button.Text = buttonText;
					break;
				case ButtonSize.Large:
					button.CollapseToSmall = CollapseThreshold.Never;
					button.CollapseToMedium = CollapseThreshold.Never;
					button.Size = Telerik.Windows.Controls.RibbonView.ButtonSize.Large;
					button.LargeImage = buttonImage;
					button.Text = buttonText;
					const double LARGE_BUTTON_TOP_PADDING = 6.0;
					button.Padding = new Thickness(0.0, LARGE_BUTTON_TOP_PADDING, 0.0, 0.0);
					break;
			}
		}

        private void PopulateTelerikQATMenuCache(StackPanel dropDownContent)
        {
            for (int i = 1; i < dropDownContent.Children.Count; ++i)
            {
                RadMenuItem telerikMenuItem = dropDownContent.Children[i] as RadMenuItem;
                if (telerikMenuItem != null)
                    TelerikQATMenuItems.Add(telerikMenuItem);
            }
        }

        private void AddQATMenuGroupHeader(StackPanel dropDownContent, RadGroupHeader groupHeader)
        {
            if (groupHeader != null)
                dropDownContent.Children.Add(groupHeader);
        }

        private RadGroupHeader GetQATMenuGroupHeader(StackPanel dropDownContent)
        {
            RadGroupHeader groupHeader = null;
            if (dropDownContent.Children.Count > 0 && (dropDownContent.Children[0] is RadGroupHeader))
                groupHeader = dropDownContent.Children[0] as RadGroupHeader;
            return groupHeader;
        }

        private void AddUserSelectableQATCommandsToQATContextMenu()
        {
            foreach (IRibbonUIElement ribbonUIElement in QATRibbonUIElements)
            {
                RadMenuItem radMenuItem = NewQATMenuItem(ribbonUIElement);
                QATContextMenu.Items.Add(radMenuItem);
            }
        }

        private void AddTelerikQATMenuItemsToQATContextMenu()
        {
            foreach (RadMenuItem radMenuItem in TelerikQATMenuItems)
                QATContextMenu.Items.Add(radMenuItem);
        }

        private RadMenuItem NewQATMenuItem(IRibbonUIElement ribbonUIElement)
        {
            QATCustomizationMenuItem menuItem = new QATCustomizationMenuItem();

            IRibbonUICommand ribbonUICommand = ribbonUIElement.AssociatedCommand;

            menuItem.Name = CheckControlName(GetNewControlName(ribbonUICommand, true));
            menuItem.Header = ribbonUICommand.GetCleanLabel();
            menuItem.IsCheckable = true;
            menuItem.IsChecked = ribbonUIElement.IsVisible;
            menuItem.Click += QuickAccessToolBarMenuItem_Click;

            menuItem.RibbonUIElement = ribbonUIElement;

            return menuItem;
        }

        private void UpdateQATContextMenuItem(IRibbonUIButton ribbonUIButton)
        {
            foreach (QATCustomizationMenuItem menuItem in QATContextMenu.Items)
            {
                if (menuItem.RibbonUIElement.AssociatedCommandName == ribbonUIButton.AssociatedCommandName)
                {
                    menuItem.RibbonUIElement = ribbonUIButton;
                    menuItem.IsChecked = ribbonUIButton.IsVisible;
                    break;
                }
            }
        }

        private void UpdateQATRibbonUIElements(IRibbonUIButton replacementRibbonUIButton)
        {
            int foundIndex = 0;
            for (int i = 0; i < QATRibbonUIElements.Count; ++i)
            {
                IRibbonUIElement existingRibbonUIElement = QATRibbonUIElements[i];
                if (existingRibbonUIElement.AssociatedCommandName == replacementRibbonUIButton.AssociatedCommandName)
                {
                    foundIndex = i;
                    break;
                }
            }

            QATRibbonUIElements.RemoveAt(foundIndex);
            QATRibbonUIElements.Insert(foundIndex, replacementRibbonUIButton);
        }

        /// <summary>
        /// Walks the Quick Access ToolBar to ensure that separator visibility is consistent with button visibility.
        /// </summary>
        private void EnsureConsistentQuickAccessToolBarSeparatorVisibility()
        {
            ItemCollection toolBarItems = Ribbon.QuickAccessToolBar.Items;

            bool lastItemWasVisibleControl = false;
            for (int i = 0; i < toolBarItems.Count; i++)
            {
                object toolBarItem = toolBarItems[i];

                if (TelerikLibrary.GetIsVisibleSeparator(toolBarItem) && !lastItemWasVisibleControl)
                    ((FrameworkElement)toolBarItem).Visibility = Visibility.Collapsed;
                else if (TelerikLibrary.GetIsVisibleSeparator(toolBarItem) && lastItemWasVisibleControl)
                {
                    if (i == toolBarItems.Count - 1)
                        ((FrameworkElement)toolBarItem).Visibility = Visibility.Collapsed;
                    else
                        lastItemWasVisibleControl = false;
                }
                else if (TelerikLibrary.GetIsInvisibleSeparator(toolBarItem) && lastItemWasVisibleControl)
                {
                    ((FrameworkElement)toolBarItem).Visibility = Visibility.Visible;
                    lastItemWasVisibleControl = false;
                }
                else if (TelerikLibrary.GetIsVisibleControl(toolBarItem))
                    lastItemWasVisibleControl = true;
            }
        }

        /// <summary>
        /// Updates the drop down toggle settings manager with the current settings (purging the old), before saving.
        /// </summary>
        private void UpdateDropDownToggleSettingsManager()
        {
            DropDownToggleSettingsManager.Elements().Clear();
            IEnumerator<KeyValuePair<string, string>> enumerator = RadRibbonToggleSplitButton.DefaultCommandRegister.GetEnumerator();
            while (enumerator.MoveNext())
            {
                string buttonName = enumerator.Current.Key;
                string defaultCommandName = enumerator.Current.Value;
                DropDownToggleSettingsManager.AddDropDownToggleButton(buttonName, defaultCommandName);
            }
        }

        /// <summary>
        /// Updates the drop down toggle settings manager defaults with the default implicit settings.
        /// </summary>
        private void UpdateDropDownToggleSettingsManagerDefaults()
        {
            DropDownToggleSettingsManager.CacheElements();

            IEnumerator<KeyValuePair<string, string>> enumerator = RadRibbonToggleSplitButton.DefaultCommandRegister.GetEnumerator();
            while (enumerator.MoveNext())
            {
                string buttonName = enumerator.Current.Key;
                string defaultCommandName = enumerator.Current.Value;
                DropDownToggleSettingsManager.AddDropDownToggleButton(buttonName, defaultCommandName);
            }

            DropDownToggleSettingsManager.StoreDefaultsAndRestoreCache();
        }

        private void ApplyDropDownToggleButtonDefaultCommands()
        {
            foreach (DropDownToggleSettingsElement dropDownToggleSettingsElement in DropDownToggleSettingsManager.Elements())
            {
                RadRibbonToggleSplitButton dropDownToggleButton = GetButton(dropDownToggleSettingsElement.ButtonName) as RadRibbonToggleSplitButton;
                if (dropDownToggleButton != null)
                {
                    IRibbonUICommand ribbonUICommand = RibbonCallbacks.GetUICommand(dropDownToggleSettingsElement.DefaultCommandName);
                    if (ribbonUICommand != null)
                        dropDownToggleButton.SetDefaultCommand(ribbonUICommand);
                }
            }
        }

        /// <summary>
        /// Used to apply initial settings and also update settings when resetting to defaults.
        /// </summary>
        private void ApplyRibbonGlobalUserSettings()
        {
            RibbonGlobalUserSettingsElement ribbonGlobalUserSettingsElement = RibbonGlobalUserSettingsManager.ElementAt(0) as RibbonGlobalUserSettingsElement;
            SetRibbonMinimizedState(ribbonGlobalUserSettingsElement.IsRibbonMinimized);
            SetQuickAccessToolBarPosition(ribbonGlobalUserSettingsElement.IsQuickAccessToolBarAboveRibbon);
        }

        private void SetRibbonMinimizedState(bool minimized)
        {
            Ribbon.IsMinimized = minimized;
        }

        private void SetQuickAccessToolBarPosition(bool aboveRibbon)
        {
            if (aboveRibbon)
                Ribbon.QuickAccessToolBarPosition = QuickAccessToolBarPosition.AboveRibbon;
            else
                Ribbon.QuickAccessToolBarPosition = QuickAccessToolBarPosition.BelowRibbon;
        }

        private string GetNewControlName(IRibbonUICommand ribbonUICommand)
        {
            return GetNewControlName(ribbonUICommand, false);
        }

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private string GetNewControlName(IRibbonUICommand ribbonUICommand, bool isQATCustomizationMenu)
        {
            string qatIdentifier = String.Empty;
            if (isQATCustomizationMenu)
                qatIdentifier = "_qat_menu_"; // I18N. OK.

            int count = 1;
            string trialControlName = ribbonUICommand.Name.Trim() + qatIdentifier + count.ToString();

            while (RibbonFrameworkElements.ContainsKey(trialControlName))
            {
                count++;
                trialControlName = ribbonUICommand.Name + count.ToString();
            }
            return trialControlName;
        }

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private bool KeyboardNavigationSupportLevelIncludes(KeyNavSupportFlags keyboardNavigationSupportFlags)
        {
            // Note: <whar> Special case; no flags set.
            if (keyboardNavigationSupportFlags == KeyNavSupportFlags.None)
                return KeyboardNavigationSupportFlags == KeyNavSupportFlags.None;

            return ((KeyboardNavigationSupportFlags & keyboardNavigationSupportFlags) == keyboardNavigationSupportFlags);
        }

        private void SetFrameworkElementScreenTip(FrameworkElement element, string toolTipTitle, string toolTipDescription, bool isQuickAccessButton)
        {
            TelerikLibrary.SetFrameworkElementToolTip(element, toolTipTitle, toolTipDescription);

            // Note: <whar> Needed to override the "by-design" bug in Telerik's QAT default ScreenTip position.
            if (isQuickAccessButton)
                SetScreenTipPlacement(element);
        }

        private void SetScreenTipPlacement(FrameworkElement element)
        {
            const double X_Y_OFFSET = 10.0;
            element.SetValue(ScreenTip.PlacementProperty, System.Windows.Controls.Primitives.PlacementMode.MousePoint);
            element.SetValue(ScreenTip.VerticalOffsetProperty, X_Y_OFFSET);
            element.SetValue(ScreenTip.HorizontalOffsetProperty, X_Y_OFFSET);    
        }

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private bool GetControlExists(string controlName)
		{
            if (!RibbonFrameworkElements.ContainsKey(controlName))
                return false;

            return true;
		}

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private string CheckControlName(string controlName)
		{
            string controlNameToUse = StringLibrary.CleanTextResource(controlName);

            if (RibbonFrameworkElements.ContainsKey(controlNameToUse))
                throw new Exception("Must use a unique control name: " + controlNameToUse); // I18N. OK.

            return controlNameToUse;
		}

		private void RegisterButton(IRibbonButton button, RibbonButtonStyleInfo buttonStyleInfo)
		{
			ButtonInformation.Add(button, buttonStyleInfo);
			RegisterFrameworkElement((FrameworkElement)button);
		}

        // Note: <whar> Covered by reflective unit test. If signature changes, test will fail.
        private void RegisterFrameworkElement(FrameworkElement control)
		{
			RibbonFrameworkElements.Add(control.Name, control);
		}

		private void UnregisterFrameworkElement(FrameworkElement control)
		{
			if (RibbonFrameworkElements.ContainsKey(control.Name))
				RibbonFrameworkElements.Remove(control.Name);
		}

        /// <summary>
        /// Registers the Dispatcher element for the command. Only needed for invokable commands. Does not need to be unregistered.
        /// </summary>
        /// <param name="ribbonUICommand">The IRibbonUICommand instance to register.</param>
        private void RegisterDispatcherElement(IRibbonUICommand ribbonUICommand)
        {
            if (ribbonUICommand is IInvokableUICommand)
                ((IInvokableUICommand)ribbonUICommand).SetDispatcherElement(Ribbon);
        }

        private void HideRibbonPopUpIfOpen()
        {
            // NET6MIGRATION
            //if (Ribbon.IsMinimized && Ribbon.IsMinimizedPopupOpen)
            //    Ribbon.IsMinimizedPopupOpen = false;
        }

        private void SetCurrentRibbonTab(RadRibbonTab newTab)
        {
            if (newTab == null)
                return;

            if (CurrentRibbonTab != newTab)
            {
                string previousTabLabel = CurrentRibbonTab == null ? TabChangeEventArgs.NONE : CurrentRibbonTab.Name;
                string newTabLabel = newTab.Name;

                CurrentRibbonTab = newTab;

                if (RibbonTabChanged != null)
                {
                    TabChangeEventArgs tabChangeEventArgs = new TabChangeEventArgs(previousTabLabel, newTabLabel);
                    RibbonTabChanged(this, tabChangeEventArgs);
                }
            }
        }
        #endregion

        #region Private Properties
        private RadRibbonView Ribbon
        {
            get;
            set;
        }

        private IRibbonCallbacks RibbonCallbacks
        {
            get;
            set;
        }

        private Theme TelerikTheme
		{
			get;
			set;
		}

        private KeyNavSupportFlags KeyboardNavigationSupportFlags
        {
            get;
            set;
        }

		private IDictionary<IRibbonButton, RibbonButtonStyleInfo> ButtonInformation
		{
			get
			{
				if (m_buttonStyleInformation == null)
					m_buttonStyleInformation = new Dictionary<IRibbonButton, RibbonButtonStyleInfo>(DEFAULT_DICTIONARY_SIZE);
				return m_buttonStyleInformation;
			}
		}

		private IDictionary<string, FrameworkElement> RibbonFrameworkElements
		{
			get
			{
				if (m_ribbonFrameworkElements == null)
					m_ribbonFrameworkElements = new Dictionary<string, FrameworkElement>(DEFAULT_DICTIONARY_SIZE);
				return m_ribbonFrameworkElements;
			}
		}

        private IDictionary<string, IRibbonUICommand> RibbonHotKeyRegister
        {
            get
            {
                if (m_ribbonHotKeyRegister == null)
                    m_ribbonHotKeyRegister = new Dictionary<string, IRibbonUICommand>(DEFAULT_DICTIONARY_SIZE);
                return m_ribbonHotKeyRegister;
            }
        }

        private IDictionary<string, IRibbonUICommand> BackstageHotKeyRegister
        {
            get
            {
                if (m_backstageHotKeyRegister == null)
                    m_backstageHotKeyRegister = new Dictionary<string, IRibbonUICommand>(DEFAULT_DICTIONARY_SIZE);
                return m_backstageHotKeyRegister;
            }
        }

        private RadContextMenu QATContextMenu
        {
            get
            {
                if (m_qatContextMenu == null)
                {
                    m_qatContextMenu = new RadContextMenu();
                    m_qatContextMenu.BorderThickness = new Thickness(0.0);
                }
                return m_qatContextMenu;
            }
        }

        /// <summary>
        /// A list of IRibbonUIElements used to build the initial QAT context menu.
        /// </summary>
        private IList<IRibbonUIElement> QATRibbonUIElements
        {
            get
            {
                const int DEFAULT_COMMAND_COUNT = 16;
                if (m_qatRibbonUIElements == null)
                    m_qatRibbonUIElements = new List<IRibbonUIElement>(DEFAULT_COMMAND_COUNT);
                return m_qatRibbonUIElements;
            }
        }

        private IList<RadMenuItem> TelerikQATMenuItems
        {
            get
            {
                const int TELERIK_QAT_MENUITEM_COUNT = 5;
                if (m_telerikQATMenuItems == null)
                    m_telerikQATMenuItems = new List<RadMenuItem>(TELERIK_QAT_MENUITEM_COUNT);
                return m_telerikQATMenuItems;
            }
        }

        private IStorageManager RibbonSettingsStorageManager
        {
            get
            {
                if (m_ribbonSettingsStorageManager == null)
                    InitializeStorageManager();
                return m_ribbonSettingsStorageManager;
            }
        }

        private IStorageFile RibbonSettingsStorageFile
        {
            get
            {
                if (m_ribbonSettingsStorageFile == null)
                    InitializeStorageManager();
                return m_ribbonSettingsStorageFile;
            }
        }

        private IDictionary<string, BitmapSource> SmallImages
		{
			get
			{
				if (m_smallImages == null)
					m_smallImages = new Dictionary<string, BitmapSource>(DEFAULT_IMAGE_DICATIONARY_SIZE);
				return m_smallImages;
			}
		}

		private IDictionary<string, BitmapSource> LargeImages
		{
			get
			{
				if (m_largeImages == null)
					m_largeImages = new Dictionary<string, BitmapSource>(DEFAULT_IMAGE_DICATIONARY_SIZE);
				return m_largeImages;
			}
		}

        private RibbonGlobalUserSettingsManager RibbonGlobalUserSettingsManager
        {
            get
            {
                if (m_ribbonGlobalUserSettingsManager == null)
                    m_ribbonGlobalUserSettingsManager = InitializeRibbonGlobalUserSettingsManager();
                return m_ribbonGlobalUserSettingsManager;
            }
        }

        private DropDownToggleSettingsManager DropDownToggleSettingsManager
        {
            get
            {
                if (m_dropDownToggleSettingsManager == null)
                    m_dropDownToggleSettingsManager = InitializeDropDownToggleSettingsManager();
                return m_dropDownToggleSettingsManager;
            }
        }

        private RibbonKeyTipManager RibbonKeyTipManager
        {
            get
            {
                if (m_ribbonKeyTipManager == null)
                    m_ribbonKeyTipManager = new RibbonKeyTipManager();
                return m_ribbonKeyTipManager;
            }
        }

        private RibbonSearchManager RibbonSearchManager
        {
            get {  return m_ribbonSearchManager; }
        }

        private RadRibbonTab CurrentRibbonTab
        {
            get;
            set;
        }
        #endregion

        #region Private Fields
        private IDictionary<IRibbonButton, RibbonButtonStyleInfo> m_buttonStyleInformation;
		private IDictionary<string, FrameworkElement> m_ribbonFrameworkElements;
        private IDictionary<string, IRibbonUICommand> m_ribbonHotKeyRegister;
        private IDictionary<string, IRibbonUICommand> m_backstageHotKeyRegister;
        private RadContextMenu m_qatContextMenu;
        private IList<IRibbonUIElement> m_qatRibbonUIElements;
        private IList<RadMenuItem> m_telerikQATMenuItems;
        private IStorageManager m_ribbonSettingsStorageManager;
        private IStorageFile m_ribbonSettingsStorageFile;
        private IDictionary<string, BitmapSource> m_smallImages;
		private IDictionary<string, BitmapSource> m_largeImages;
        private RibbonGlobalUserSettingsManager m_ribbonGlobalUserSettingsManager;
        private DropDownToggleSettingsManager m_dropDownToggleSettingsManager;
        private RibbonKeyTipManager m_ribbonKeyTipManager;
        private RibbonSearchManager m_ribbonSearchManager;
        #endregion

        #region Private Constants
        private const int DEFAULT_DICTIONARY_SIZE = 32;
        private const int DEFAULT_IMAGE_DICATIONARY_SIZE = 128;
		#endregion
    }
}