|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls.Ribbon
#else
namespace Microsoft.Windows.Controls.Ribbon
#endif
{
#region Using declarations
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
#if RIBBON_IN_FRAMEWORK
using System.Windows.Controls.Ribbon.Primitives;
using Microsoft.Windows.Controls;
#else
using Microsoft.Windows.Automation.Peers;
using Microsoft.Windows.Controls.Ribbon.Primitives;
#endif
using MS.Internal;
#endregion Using declarations
/// <summary>
/// The main Ribbon control which consists of multiple tabs, each of which
/// containing groups of controls. The Ribbon also provides improved context
/// menus, enhanced screen tips, and keyboard shortcuts.
/// </summary>
[StyleTypedProperty(Property = "ContextualTabGroupStyle", StyleTargetType = typeof(RibbonContextualTabGroup))]
[StyleTypedProperty(Property = "TabHeaderStyle", StyleTargetType = typeof(RibbonTabHeader))]
[TemplatePart(Name = Ribbon.ContextualTabGroupItemsControlTemplateName, Type = typeof(RibbonContextualTabGroupItemsControl))]
[TemplatePart(Name = Ribbon.TitlePanelTemplateName, Type = typeof(RibbonTitlePanel))]
[TemplatePart(Name = Ribbon.TitleHostTemplateName, Type = typeof(ContentPresenter))]
[TemplatePart(Name = Ribbon.QatHostTemplateName, Type = typeof(Grid))]
[TemplatePart(Name = Ribbon.HelpPaneTemplateName, Type = typeof(ContentPresenter))]
[TemplatePart(Name = Ribbon.ItemsPresenterPopupTemplateName, Type = typeof(Popup))]
public class Ribbon : Selector
{
#region Fields
private const double CollapseWidth = 300.0; // The minimum allowed width before the Ribbon will be collapsed.
private const double CollapseHeight = 250.0; // The minimum allowed height before the Ribbon will be collapsed.
private bool _selectedTabClicked = false; // A flag used for tracking whether the selected tab has been clicked recently.
private Popup _itemsPresenterPopup; // The Popup containing Ribbon's ItemsPresenter.
private RibbonTabHeaderItemsControl _tabHeaderItemsControl; // The headers items control.
private ObservableCollection<object> _tabHeaderItemsSource = new ObservableCollection<object>(); // ItemsSource for the headers items control.
private RibbonContextualTabGroupItemsControl _groupHeaderItemsControl; // Contextual tab group header items control.
private ObservableCollection<RibbonContextualTabGroup> _tabGroupHeaders; // Collection of ContextualTabGroups
private Dictionary<int, int> _tabIndexToDisplayIndexMap = new Dictionary<int, int>(); // A map from collection index to display index of tab items.
private Dictionary<int, int> _tabDisplayIndexToIndexMap = new Dictionary<int, int>(); // A map from display index to collection index of tab items.
private double _mouseWheelCumulativeDelta = 0; // The aggregate of mouse wheel delta since the last mouse wheel tab selection change.
private const double MouseWheelSelectionChangeThreshold = 100; // The threshold of mouse wheel delta to change tab selection.
UIElement _qatTopHost = null; // ContentPresenter hosting QuickAccessToolBar
UIElement _titleHost = null; // ContentPresenter hosting the Title
UIElement _helpPaneHost = null; // ContentPresenter hosting the HelpPaneContent
ItemsPresenter _itemsPresenter = null;
private bool _inContextMenu = false;
private bool _retainFocusOnEscape = false;
KeyTipService.KeyTipFocusEventHandler _keyTipEnterFocusHandler = null;
KeyTipService.KeyTipFocusEventHandler _keyTipExitRestoreFocusHandler = null;
private const string ContextualTabGroupItemsControlTemplateName = "PART_ContextualTabGroupItemsControl";
private const string TitlePanelTemplateName = "PART_TitlePanel";
private const string TitleHostTemplateName = "PART_TitleHost";
private const string QatHostTemplateName = "QatTopHost";
private const string HelpPaneTemplateName = "PART_HelpPane";
private const string ItemsPresenterPopupTemplateName = "PART_ITEMSPRESENTERPOPUP";
#endregion
#region Constuctors
/// <summary>
/// Initializes static members of the Ribbon class. This also overrides the
/// default style and adds command bindings for some Window control commands.
/// </summary>
static Ribbon()
{
Type ownerType = typeof(Ribbon);
DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
ItemsPanelProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new ItemsPanelTemplate(new FrameworkElementFactory(typeof(RibbonTabsPanel)))));
FocusManager.IsFocusScopeProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
BorderBrushProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnBorderBrushChanged)));
EventManager.RegisterClassHandler(ownerType, Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(OnClickThroughThunk));
EventManager.RegisterClassHandler(ownerType, Mouse.LostMouseCaptureEvent, new MouseEventHandler(OnLostMouseCaptureThunk));
EventManager.RegisterClassHandler(ownerType, RibbonControlService.DismissPopupEvent, new RibbonDismissPopupEventHandler(OnDismissPopupThunk));
EventManager.RegisterClassHandler(ownerType, RibbonQuickAccessToolBar.CloneEvent, new RibbonQuickAccessToolBarCloneEventHandler(OnCloneThunk));
ContextMenuProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(RibbonHelper.OnContextMenuChanged, RibbonHelper.OnCoerceContextMenu));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.AddToQuickAccessToolBarCommand, AddToQATExecuted, AddToQATCanExecute));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.MaximizeRibbonCommand, MaximizeRibbonExecuted, MaximizeRibbonCanExecute));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.MinimizeRibbonCommand, MinimizeRibbonExecuted, MinimizeRibbonCanExecute));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.RemoveFromQuickAccessToolBarCommand, RemoveFromQATExecuted, RemoveFromQATCanExecute));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.ShowQuickAccessToolBarAboveRibbonCommand, ShowQATAboveExecuted, ShowQATAboveCanExecute));
CommandManager.RegisterClassCommandBinding(ownerType,
new CommandBinding(RibbonCommands.ShowQuickAccessToolBarBelowRibbonCommand, ShowQATBelowExecuted, ShowQATBelowCanExecute));
KeyboardNavigation.DirectionalNavigationProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(KeyboardNavigationMode.Contained));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(KeyboardNavigationMode.Cycle));
EventManager.RegisterClassHandler(ownerType, FrameworkElement.ContextMenuOpeningEvent, new ContextMenuEventHandler(OnContextMenuOpeningThunk), true);
EventManager.RegisterClassHandler(ownerType, FrameworkElement.ContextMenuClosingEvent, new ContextMenuEventHandler(OnContextMenuClosingThunk), true);
}
/// <summary>
/// Initializes a new instance of the Ribbon class and hooks the Loaded event
/// to perform class initialization.
/// </summary>
public Ribbon()
{
this.Loaded += new RoutedEventHandler(this.OnLoaded);
PresentationSource.AddSourceChangedHandler(this, new SourceChangedEventHandler(OnSourceChangedHandler));
RibbonControlService.SetRibbon(this, this);
// Attach EnterFocus and ExitRestoreFocus events of KeyTip Service
_keyTipEnterFocusHandler = new KeyTipService.KeyTipFocusEventHandler(OnKeyTipEnterFocus);
KeyTipService.Current.KeyTipEnterFocus += _keyTipEnterFocusHandler;
_keyTipExitRestoreFocusHandler = new KeyTipService.KeyTipFocusEventHandler(OnKeyTipExitRestoreFocus);
KeyTipService.Current.KeyTipExitRestoreFocus += _keyTipExitRestoreFocusHandler;
ItemContainerGenerator.StatusChanged += new EventHandler(OnItemContainerGeneratorStatusChanged);
IsVisibleChanged += new DependencyPropertyChangedEventHandler(HandleIsVisibleChanged);
}
#endregion
#region Public Events
/// <summary>
/// Callbacks for the ExpandedEvent.
/// </summary>
public event RoutedEventHandler Expanded
{
add { AddHandler(ExpandedEvent, value, false); }
remove { RemoveHandler(ExpandedEvent, value); }
}
/// <summary>
/// Raised when the Ribbon is expanded (IsCollapsed changes to False).
/// </summary>
public static readonly RoutedEvent ExpandedEvent =
EventManager.RegisterRoutedEvent(
"Expanded",
RoutingStrategy.Direct,
typeof(RoutedEventHandler),
typeof(Ribbon));
/// <summary>
/// Callbacks for the CollapsedEvent.
/// </summary>
public event RoutedEventHandler Collapsed
{
add { AddHandler(CollapsedEvent, value, false); }
remove { RemoveHandler(CollapsedEvent, value); }
}
/// <summary>
/// Raised when the Ribbon is collapsed (IsCollapsed changes to True).
/// </summary>
public static readonly RoutedEvent CollapsedEvent =
EventManager.RegisterRoutedEvent(
"Collapsed",
RoutingStrategy.Direct,
typeof(RoutedEventHandler),
typeof(Ribbon));
#endregion
#region Public Properties
/// <summary>
/// Gets or sets the Visibility for the Icon of the RibbonWindow that contains this Ribbon.
/// </summary>
public Visibility WindowIconVisibility
{
get { return (Visibility)GetValue(WindowIconVisibilityProperty); }
set { SetValue(WindowIconVisibilityProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for WindowIconVisibility. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty WindowIconVisibilityProperty = DependencyProperty.Register(
"WindowIconVisibility",
typeof(Visibility),
typeof(Ribbon),
new UIPropertyMetadata(Visibility.Visible, OnWindowIconVisibilityChanged));
/// <summary>
/// Gets a value indicating whether the Ribbon is currently hosted in a RibbonWindow.
/// </summary>
public bool IsHostedInRibbonWindow
{
get { return (bool)GetValue(IsHostedInRibbonWindowProperty); }
private set { SetValue(IsHostedInRibbonWindowPropertyKey, value); }
}
/// <summary>
/// DependencyPropertyKey for read only DependencyProperty IsHostedInRibbonWindow.
/// </summary>
private static readonly DependencyPropertyKey IsHostedInRibbonWindowPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsHostedInRibbonWindow",
typeof(bool),
typeof(Ribbon),
new FrameworkPropertyMetadata(false));
/// <summary>
/// Using a DependencyProperty as the backing store for IsHostedInRibbonWindow. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsHostedInRibbonWindowProperty =
IsHostedInRibbonWindowPropertyKey.DependencyProperty;
/// <summary>
/// Gets or sets the Ribbon's application menu.
/// </summary>
public RibbonApplicationMenu ApplicationMenu
{
get { return (RibbonApplicationMenu)GetValue(ApplicationMenuProperty); }
set { SetValue(ApplicationMenuProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for ApplicationMenuProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ApplicationMenuProperty =
DependencyProperty.Register(
"ApplicationMenu",
typeof(RibbonApplicationMenu),
typeof(Ribbon),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnApplicationMenuChanged)));
/// <summary>
/// Gets or sets the Ribbon's QuickAccessToolbar.
/// </summary>
public RibbonQuickAccessToolBar QuickAccessToolBar
{
get { return (RibbonQuickAccessToolBar)GetValue(QuickAccessToolBarProperty); }
set { SetValue(QuickAccessToolBarProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for QuickAccessToolBarProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty QuickAccessToolBarProperty =
DependencyProperty.Register(
"QuickAccessToolBar",
typeof(RibbonQuickAccessToolBar),
typeof(Ribbon),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnQuickAccessToolBarChanged)));
public object HelpPaneContent
{
get { return GetValue(HelpPaneContentProperty); }
set { SetValue(HelpPaneContentProperty, value); }
}
public static readonly DependencyProperty HelpPaneContentProperty =
DependencyProperty.Register(
"HelpPaneContent",
typeof(object),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
public DataTemplate HelpPaneContentTemplate
{
get { return (DataTemplate)GetValue(HelpPaneContentTemplateProperty); }
set { SetValue(HelpPaneContentTemplateProperty, value); }
}
public static readonly DependencyProperty HelpPaneContentTemplateProperty =
DependencyProperty.Register(
"HelpPaneContentTemplate",
typeof(DataTemplate),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value indicating whether the Ribbon is minimized. When the Ribbon
/// is minimized its tabs must be clicked in order for their contents to be displayed
/// in a Popup.
/// </summary>
public bool IsMinimized
{
get { return (bool)GetValue(IsMinimizedProperty); }
set { SetValue(IsMinimizedProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for IsMinimizedProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsMinimizedProperty =
DependencyProperty.Register(
"IsMinimized",
typeof(bool),
typeof(Ribbon),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsMinimizedChanged), new CoerceValueCallback(CoerceIsMinimized)));
/// <summary>
/// Gets or sets a value indicating whether the RibbonTab's Popup is displayed.
/// </summary>
public bool IsDropDownOpen
{
get { return (bool)GetValue(IsDropDownOpenProperty); }
set { SetValue(IsDropDownOpenProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for IsDropDownOpenProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register(
"IsDropDownOpen",
typeof(bool),
typeof(Ribbon),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsDropDownOpenChanged), new CoerceValueCallback(OnCoerceIsDropDownOpen)));
/// <summary>
/// Gets/Sets a value indicating whether the Ribbon is collapsed.
/// </summary>
public bool IsCollapsed
{
get { return (bool)GetValue(IsCollapsedProperty); }
set { SetValue(IsCollapsedProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for IsCollapsed. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty IsCollapsedProperty =
DependencyProperty.Register("IsCollapsed", typeof(bool), typeof(Ribbon),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsCollapsedChanged), new CoerceValueCallback(CoerceIsCollapsed)));
/// <summary>
/// Gets or sets the Title of the Ribbon.
/// </summary>
public object Title
{
get { return GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for TitleProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register(
"Title",
typeof(object),
typeof(Ribbon),
new FrameworkPropertyMetadata(null, null, new CoerceValueCallback(OnCoerceTitle)));
public DataTemplate TitleTemplate
{
get { return (DataTemplate)GetValue(TitleTemplateProperty); }
set { SetValue(TitleTemplateProperty, value); }
}
public static readonly DependencyProperty TitleTemplateProperty =
DependencyProperty.Register(
"TitleTemplate",
typeof(DataTemplate),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value indicating whether to show the QuickAccessToolbar on top of the Ribbon.
/// </summary>
public bool ShowQuickAccessToolBarOnTop
{
get { return (bool)GetValue(ShowQuickAccessToolBarOnTopProperty); }
set { SetValue(ShowQuickAccessToolBarOnTopProperty, value); }
}
/// <summary>
/// Using a DependencyProperty as the backing store for ShowQuickAccessToolbarOnTopProperty. This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty ShowQuickAccessToolBarOnTopProperty =
DependencyProperty.Register(
"ShowQuickAccessToolBarOnTop",
typeof(bool),
typeof(Ribbon),
new FrameworkPropertyMetadata(true));
/// <summary>
/// ItemsSource for ContextualTabGroups
/// </summary>
public IEnumerable ContextualTabGroupsSource
{
get { return (IEnumerable)GetValue(ContextualTabGroupsSourceProperty); }
set { SetValue(ContextualTabGroupsSourceProperty, value); }
}
/// <summary>
/// The DependencyProperty for the ContextualTabGroupsSource property.
/// </summary>
public static readonly DependencyProperty ContextualTabGroupsSourceProperty
= DependencyProperty.Register("ContextualTabGroupsSource",
typeof(IEnumerable),
typeof(Ribbon),
new FrameworkPropertyMetadata((IEnumerable)null, new PropertyChangedCallback(OnContextualTabGroupsSourceChanged)));
///<summary>
/// Gets a Collection of the Ribbon's RibbonContextualTabGroups.
///</summary>
[Bindable(true)]
public Collection<RibbonContextualTabGroup> ContextualTabGroups
{
get
{
if (_tabGroupHeaders == null)
{
_tabGroupHeaders = new ObservableCollection<RibbonContextualTabGroup>();
_tabGroupHeaders.CollectionChanged += new NotifyCollectionChangedEventHandler(this.OnContextualTabGroupsCollectionChanged);
}
return _tabGroupHeaders;
}
}
public DataTemplate ContextualTabGroupHeaderTemplate
{
get { return (DataTemplate)GetValue(ContextualTabGroupHeaderTemplateProperty); }
set { SetValue(ContextualTabGroupHeaderTemplateProperty, value); }
}
public static readonly DependencyProperty ContextualTabGroupHeaderTemplateProperty =
DependencyProperty.Register("ContextualTabGroupHeaderTemplate", typeof(DataTemplate), typeof(Ribbon), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyContextualTabGroupPropertyChanged)));
public Style ContextualTabGroupStyle
{
get { return (Style)GetValue(ContextualTabGroupStyleProperty); }
set { SetValue(ContextualTabGroupStyleProperty, value); }
}
public static readonly DependencyProperty ContextualTabGroupStyleProperty =
DependencyProperty.Register("ContextualTabGroupStyle", typeof(Style), typeof(Ribbon), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyContextualTabGroupPropertyChanged)));
/// <summary>
/// Gets or sets a value of the BorderBrush brush used in a "Hover" state of the Ribbon controls.
/// </summary>
public Brush MouseOverBorderBrush
{
get { return (Brush)GetValue(MouseOverBorderBrushProperty); }
set { SetValue(MouseOverBorderBrushProperty, value); }
}
/// <summary>
/// DependencyProperty for MouseOverBorderBrush property.
/// </summary>
public static readonly DependencyProperty MouseOverBorderBrushProperty =
DependencyProperty.Register(
"MouseOverBorderBrush",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the background brush used in a "Hover" state of the Ribbon controls.
/// </summary>
public Brush MouseOverBackground
{
get { return (Brush)GetValue(MouseOverBackgroundProperty); }
set { SetValue(MouseOverBackgroundProperty, value); }
}
/// <summary>
/// DependencyProperty for MouseOverBackground property.
/// </summary>
public static readonly DependencyProperty MouseOverBackgroundProperty =
DependencyProperty.Register(
"MouseOverBackground",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the BorderBrush brush used in a "Pressed" state of the Ribbon controls.
/// </summary>
public Brush PressedBorderBrush
{
get { return (Brush)GetValue(PressedBorderBrushProperty); }
set { SetValue(PressedBorderBrushProperty, value); }
}
/// <summary>
/// DependencyProperty for PressedBorderBrush property.
/// </summary>
public static readonly DependencyProperty PressedBorderBrushProperty =
DependencyProperty.Register(
"PressedBorderBrush",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the background brush used in a "Pressed" state of the Ribbon controls.
/// </summary>
public Brush PressedBackground
{
get { return (Brush)GetValue(PressedBackgroundProperty); }
set { SetValue(PressedBackgroundProperty, value); }
}
/// <summary>
/// DependencyProperty for PressedBackground property.
/// </summary>
public static readonly DependencyProperty PressedBackgroundProperty =
DependencyProperty.Register(
"PressedBackground",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the BorderBrush brush used in a "Checked" state of the Ribbon controls.
/// </summary>
public Brush CheckedBorderBrush
{
get { return (Brush)GetValue(CheckedBorderBrushProperty); }
set { SetValue(CheckedBorderBrushProperty, value); }
}
/// <summary>
/// DependencyProperty for CheckedBorderBrush property.
/// </summary>
public static readonly DependencyProperty CheckedBorderBrushProperty =
DependencyProperty.Register(
"CheckedBorderBrush",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the background brush used in a "Checked" state of the Ribbon controls.
/// </summary>
public Brush CheckedBackground
{
get { return (Brush)GetValue(CheckedBackgroundProperty); }
set { SetValue(CheckedBackgroundProperty, value); }
}
/// <summary>
/// DependencyProperty for CheckedBackground property.
/// </summary>
public static readonly DependencyProperty CheckedBackgroundProperty =
DependencyProperty.Register(
"CheckedBackground",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the BorderBrush brush used in a "Focused" state of the Ribbon controls.
/// To place keyboard focus on a ribbon control, press ALT-"KeyTip letter" and navigate with arrow keys.
/// </summary>
public Brush FocusedBorderBrush
{
get { return (Brush)GetValue(FocusedBorderBrushProperty); }
set { SetValue(FocusedBorderBrushProperty, value); }
}
/// <summary>
/// DependencyProperty for FocusedBorderBrush property.
/// </summary>
public static readonly DependencyProperty FocusedBorderBrushProperty =
DependencyProperty.Register(
"FocusedBorderBrush",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
/// <summary>
/// Gets or sets a value of the background brush used in a "Focused" state of the Ribbon controls.
/// </summary>
public Brush FocusedBackground
{
get { return (Brush)GetValue(FocusedBackgroundProperty); }
set { SetValue(FocusedBackgroundProperty, value); }
}
/// <summary>
/// DependencyProperty for FocusedBackground property.
/// </summary>
public static readonly DependencyProperty FocusedBackgroundProperty =
DependencyProperty.Register(
"FocusedBackground",
typeof(Brush),
typeof(Ribbon),
new FrameworkPropertyMetadata(null));
public Style TabHeaderStyle
{
get { return (Style)GetValue(TabHeaderStyleProperty); }
set { SetValue(TabHeaderStyleProperty, value); }
}
// Using a DependencyProperty as the backing store for TabHeaderStyle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TabHeaderStyleProperty =
DependencyProperty.Register("TabHeaderStyle", typeof(Style), typeof(Ribbon), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyTabHeaderPropertyChanged)));
public DataTemplate TabHeaderTemplate
{
get { return (DataTemplate)GetValue(TabHeaderTemplateProperty); }
set { SetValue(TabHeaderTemplateProperty, value); }
}
// Using a DependencyProperty as the backing store for TabHeaderTemplate. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TabHeaderTemplateProperty =
DependencyProperty.Register("TabHeaderTemplate", typeof(DataTemplate), typeof(Ribbon), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnNotifyTabHeaderPropertyChanged)));
#endregion
#region Private Data
/// <summary>
/// Cached Window hosting the Ribbon.
/// </summary>
private Window _window = null;
#endregion
#region Internal Properties
/// <summary>
/// RibbonTitlePanel instance for this Ribbon.
/// </summary>
internal RibbonTitlePanel RibbonTitlePanel
{
get;
private set;
}
/// <summary>
/// RibbonContextualTabGroupItemsControl instance for this Ribbon.
/// </summary>
internal RibbonContextualTabGroupItemsControl ContextualTabGroupItemsControl
{
get
{
return _groupHeaderItemsControl;
}
}
internal UIElement QatTopHost
{
get
{
return _qatTopHost;
}
}
internal UIElement TitleHost
{
get
{
return _titleHost;
}
}
internal UIElement HelpPaneHost
{
get { return _helpPaneHost; }
}
/// <summary>
/// A map between collection index and display index of tab items.
/// </summary>
internal Dictionary<int, int> TabIndexToDisplayIndexMap
{
get
{
return _tabIndexToDisplayIndexMap;
}
}
/// <summary>
/// A map between display index and collection index of tab items.
/// </summary>
internal Dictionary<int, int> TabDisplayIndexToIndexMap
{
get
{
return _tabDisplayIndexToIndexMap;
}
}
/// <summary>
/// RibbonTabHeaderItemsControl instance of this Ribbon.
/// </summary>
internal RibbonTabHeaderItemsControl RibbonTabHeaderItemsControl
{
get
{
return _tabHeaderItemsControl;
}
}
internal Popup ItemsPresenterPopup
{
get
{
return _itemsPresenterPopup;
}
}
#endregion
#region Public Methods
/// <summary>
/// Invoked whenever the control's template is applied.
/// </summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_itemsPresenter = GetTemplateChild("ItemsPresenter") as ItemsPresenter;
_itemsPresenterPopup = this.GetTemplateChild(ItemsPresenterPopupTemplateName) as Popup;
_tabHeaderItemsControl = this.GetTemplateChild("TabHeaderItemsControl") as RibbonTabHeaderItemsControl;
if (_tabHeaderItemsControl != null && _tabHeaderItemsControl.ItemsSource == null)
{
_tabHeaderItemsControl.ItemsSource = _tabHeaderItemsSource;
}
_groupHeaderItemsControl = this.GetTemplateChild(Ribbon.ContextualTabGroupItemsControlTemplateName) as RibbonContextualTabGroupItemsControl;
if (_groupHeaderItemsControl != null && _groupHeaderItemsControl.ItemsSource == null)
{
if (ContextualTabGroupsSource != null && ContextualTabGroups.Count > 0)
{
throw new InvalidOperationException(Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSourceInvalid);
}
if (ContextualTabGroupsSource != null)
{
ContextualTabGroupItemsControl.ItemsSource = ContextualTabGroupsSource;
}
else if (ContextualTabGroups != null)
{
ContextualTabGroupItemsControl.ItemsSource = ContextualTabGroups;
}
}
this.RibbonTitlePanel = this.GetTemplateChild(Ribbon.TitlePanelTemplateName) as RibbonTitlePanel;
_qatTopHost = this.GetTemplateChild(Ribbon.QatHostTemplateName) as UIElement;
_titleHost = this.GetTemplateChild(Ribbon.TitleHostTemplateName) as UIElement;
_helpPaneHost = this.GetTemplateChild(Ribbon.HelpPaneTemplateName) as UIElement;
PropertyHelper.TransferProperty(this, ContextMenuProperty); // Coerce to get a default ContextMenu if none has been specified.
PropertyHelper.TransferProperty(this, RibbonControlService.CanAddToQuickAccessToolBarDirectlyProperty);
}
#endregion
#region Internal Methods
/// <summary>
/// A callback for handling clicks to a RibbonTabHeader.
/// </summary>
/// <param name="ribbonTab">The RibbonTabHeader that was clicked.</param>
/// <param name="e">The event data.</param>
internal void NotifyMouseClickedOnTabHeader(RibbonTabHeader tabHeader, MouseButtonEventArgs e)
{
if (_tabHeaderItemsControl == null)
return;
int index = _tabHeaderItemsControl.ItemContainerGenerator.IndexFromContainer(tabHeader);
if (e.ClickCount == 1)
{
// Single clicking should:
//
// 1. If maximized, select the tab for clicked tabheader.
// 2. If minimized and clicking the previously selected tabheader.
// * Toggle the pop-up for the selected tab.
// 3. If minimized and clicking an un-selected tabheader.
// * Display the pop-up for the clicked tab.
if ((SelectedIndex < 0) || SelectedIndex != index)
{
this.SelectedIndex = index;
if (this.IsMinimized)
{
this.IsDropDownOpen = true;
}
_selectedTabClicked = false;
}
else
{
if (this.IsMinimized)
{
this.IsDropDownOpen = !this.IsDropDownOpen;
}
_selectedTabClicked = true;
}
}
else if (e.ClickCount == 2)
{
// Double-clicking should:
//
// 1. If clicking a tab that is being clicked in its 'selected' state for
// the second time, toggle its 'IsMinimized' behavior.
// 2. Otherwise do nothing.
if (_selectedTabClicked == true || this.IsMinimized)
{
IsMinimized = !IsMinimized;
IsDropDownOpen = false;
_selectedTabClicked = false;
}
else
{
_selectedTabClicked = true;
}
}
else if (e.ClickCount == 3)
{
// Triple-clicking should do the following for initial conditions (1st click):
//
// 1. If minimized:
// * Maximize and select the tab.
// 2. If maximized and the tab was initially selected.
// * Minimize and display the pop-up.
// 3. If maximized and the tab was NOT initially selected.
// * Minimize do not display any pop-ups.
if (_selectedTabClicked == true)
{
IsMinimized = !IsMinimized;
IsDropDownOpen = false;
}
else
{
this.IsDropDownOpen = true;
}
}
}
/// <summary>
/// Notify the Ribbon that the RibbonContextualTabGroup was clicked.
/// </summary>
/// <param name="group">The RibbonContextualTabGroup that was clicked.</param>
internal void NotifyMouseClickedOnContextualTabGroup(RibbonContextualTabGroup tabGroupHeader)
{
RibbonTab firstVisibleTab = tabGroupHeader.FirstVisibleTab;
if (firstVisibleTab != null)
{
// If Ribbon is minimized - we should open it first
IsMinimized = false;
// Select first visible tab of the group
firstVisibleTab.IsSelected = true;
}
}
/// <summary>
/// Notify the Ribbon that the ContextualTabGroupHeader property changed of RibbonTab
/// </summary>
internal void NotifyTabContextualTabGroupHeaderChanged()
{
if (_tabHeaderItemsControl != null)
{
Panel headerItemsHost = _tabHeaderItemsControl.InternalItemsHost;
if (headerItemsHost != null)
{
headerItemsHost.InvalidateMeasure();
headerItemsHost.InvalidateArrange();
}
}
}
/// <summary>
/// Notify the Ribbon that the Header property changed on RibbonTab.
/// </summary>
internal void NotifyTabHeaderChanged()
{
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
RefreshHeaderCollection();
}
}
internal void NotifyTabHeadersScrollOwnerChanged(ScrollViewer oldScrollViewer, ScrollViewer newScrollViewer)
{
if (oldScrollViewer != null )
{
oldScrollViewer.ScrollChanged -= new ScrollChangedEventHandler(OnTabHeadersScrollChanged);
}
if (newScrollViewer != null)
{
newScrollViewer.ScrollChanged += new ScrollChangedEventHandler(OnTabHeadersScrollChanged);
}
}
private void OnTabHeadersScrollChanged(object d, ScrollChangedEventArgs e)
{
if (ContextualTabGroupItemsControl != null)
{
// When scrollbars appear for the TabHeaders, collapse the ContextualTabGroups.
ContextualTabGroupItemsControl.ForceCollapse = !(DoubleUtil.GreaterThanOrClose(e.ViewportWidth, e.ExtentWidth));
}
}
#endregion
#region Protected Properties
/// <summary>
/// Gets an enumerator for the Ribbon's logical children.
/// </summary>
#if RIBBON_IN_FRAMEWORK
protected internal override IEnumerator LogicalChildren
#else
protected override IEnumerator LogicalChildren
#endif
{
get
{
return this.GetLogicalChildren();
}
}
private IEnumerator<object> GetLogicalChildren()
{
IEnumerator children = base.LogicalChildren;
while (children.MoveNext())
yield return children.Current;
if (this.ApplicationMenu != null)
yield return this.ApplicationMenu;
if (this.QuickAccessToolBar != null)
yield return this.QuickAccessToolBar;
}
#endregion
#region Protected Methods
/// <summary>
/// Called when the MouseWheel changes position while the mouse pointer is over the
/// Ribbon. In this case, the MouseWheelEvent is used to indicate that we should
/// iterate to the previous or next tab.
/// </summary>
/// <param name="e">The event data.</param>
protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
{
if (!this.IsMinimized && (SelectedIndex >= 0) && (Mouse.Captured == this || Mouse.Captured == null))
{
int selectedTabIndex = SelectedIndex;
_mouseWheelCumulativeDelta += e.Delta;
// Slow down the mouse wheel selection by waiting
// to change the selection until a cumulative delta
// is attained.
if (DoubleUtil.GreaterThan(Math.Abs(_mouseWheelCumulativeDelta), MouseWheelSelectionChangeThreshold))
{
if (_mouseWheelCumulativeDelta < 0)
{
// select the tab whose display index is 1 greater than current.
int displayIndex = GetTabDisplayIndexForIndex(selectedTabIndex);
if (displayIndex >= 0)
{
displayIndex++;
int newSelectedIndex = GetTabIndexForDisplayIndex(displayIndex);
if (newSelectedIndex >= 0)
{
SelectedIndex = newSelectedIndex;
if (_tabHeaderItemsControl != null)
{
_tabHeaderItemsControl.ScrollIntoView(SelectedIndex);
}
}
}
}
else
{
// select the tab whose display index is 1 less than current.
int displayIndex = GetTabDisplayIndexForIndex(selectedTabIndex);
if (displayIndex >= 0)
{
displayIndex--;
int newSelectedIndex = GetTabIndexForDisplayIndex(displayIndex);
if (newSelectedIndex >= 0)
{
SelectedIndex = newSelectedIndex;
if (_tabHeaderItemsControl != null)
{
_tabHeaderItemsControl.ScrollIntoView(SelectedIndex);
}
}
}
}
_mouseWheelCumulativeDelta = 0;
}
e.Handled = true;
}
base.OnPreviewMouseWheel(e);
}
/// <summary>
/// Generate RibbonTab as the item container.
/// </summary>
protected override DependencyObject GetContainerForItemOverride()
{
return new RibbonTab();
}
/// <summary>
/// An item is its own container if it is a RibbonTab
/// </summary>
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is RibbonTab);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
ItemsControl childItemsControl = element as ItemsControl;
if (childItemsControl != null)
{
// copy templates and styles from this ItemsControl
var itemTemplate = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateProperty);
var itemTemplateSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemTemplateSelectorProperty);
var itemStringFormat = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemStringFormatProperty);
var itemContainerStyle = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleProperty);
var itemContainerStyleSelector = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemContainerStyleSelectorProperty);
var alternationCount = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.AlternationCountProperty);
var itemBindingGroup = RibbonHelper.GetValueAndValueSource(childItemsControl, ItemsControl.ItemBindingGroupProperty);
HeaderedItemsControl headeredItemsControl = childItemsControl as HeaderedItemsControl;
var headerTemplate = RibbonHelper.GetValueAndValueSource(headeredItemsControl, HeaderedItemsControl.HeaderTemplateProperty);
var headerTemplateSelector = RibbonHelper.GetValueAndValueSource(headeredItemsControl, HeaderedItemsControl.HeaderTemplateSelectorProperty);
var headerStringFormat = RibbonHelper.GetValueAndValueSource(headeredItemsControl, HeaderedItemsControl.HeaderStringFormatProperty);
base.PrepareContainerForItemOverride(element, item);
// Call this function to work around a restriction of supporting hetrogenous
// ItemsCotnrol hierarchy. The method takes care of both ItemsControl and
// HeaderedItemsControl (in this case) and assign back the default properties
// whereever appropriate.
RibbonHelper.IgnoreDPInheritedFromParentItemsControl(
childItemsControl,
this,
itemTemplate,
itemTemplateSelector,
itemStringFormat,
itemContainerStyle,
itemContainerStyleSelector,
alternationCount,
itemBindingGroup,
headerTemplate,
headerTemplateSelector,
headerStringFormat);
}
else
{
base.PrepareContainerForItemOverride(element, item);
}
RibbonTab container = element as RibbonTab;
if (container != null)
{
container.PrepareRibbonTab();
}
}
/// <summary>
/// Gets called when items change on this itemscontrol.
/// Syncs header collection accordingly.
/// </summary>
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (e.Action == NotifyCollectionChangedAction.Remove ||
e.Action == NotifyCollectionChangedAction.Replace ||
e.Action == NotifyCollectionChangedAction.Reset)
{
InitializeSelection();
}
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated &&
e.Action == NotifyCollectionChangedAction.Move ||
e.Action == NotifyCollectionChangedAction.Remove)
{
// Only Move and Remove actions require us to explicitly refresh the header collection, since Add,
// Replace, and Reset actions already refresh the header collection through container generation.
RefreshHeaderCollection();
}
}
protected override AutomationPeer OnCreateAutomationPeer()
{
return new RibbonAutomationPeer(this);
}
/// <summary>
/// Gets called when selection changes.
/// </summary>
/// <param name="e"></param>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
// Selector.CanSelectMultiple is true by default and is internal.
// Force single selection by setting the selected item.
// This makes RibbonTab.IsSelected work appropriately.
SelectedItem = e.AddedItems[0];
if (IsDropDownOpen)
{
// Recapture and focus when selection changes.
RibbonHelper.AsyncSetFocusAndCapture(this,
delegate() { return IsDropDownOpen; },
this,
_itemsPresenterPopup.TryGetChild());
}
}
}
#endregion
#region Private Classes
/// <summary>
/// If no Header is set on RibbonTab class, a object
/// of this class gets created and used by default in
/// RibbonTabHeaderItemsCollection.
/// </summary>
private class SingleSpaceObject
{
public override string ToString()
{
return " ";
}
}
#endregion
#region Private Methods
/// <summary>
/// Called when the source in which the Ribbon is hosted changes.
/// </summary>
/// <param name="o">The Ribbon whose source has changed.</param>
/// <param name="e">Event args for the change.</param>
private static void OnSourceChangedHandler(object o, SourceChangedEventArgs e)
{
Ribbon rib = (Ribbon)o;
// Unhook handlers if the previous container was a Window.
if (e.OldSource is HwndSource &&
rib._window != null)
{
rib.UnhookWindowListeners(rib._window);
rib._window = null;
}
// Hook up new handlers if the new container is an Window.
if (e.NewSource != null &&
e.NewSource.RootVisual is Window)
{
rib._window = (Window)e.NewSource.RootVisual;
rib.HookWindowListeners(rib._window);
}
}
private void UnhookWindowListeners(Window win)
{
win.SizeChanged -= new SizeChangedEventHandler(this.OnWindowSizeChanged);
this.IsCollapsed = false;
if (CheckIfWindowIsRibbonWindow(win))
{
this.IsHostedInRibbonWindow = false;
RibbonWindow rw = (RibbonWindow)win;
rw.TitleChanged -= new EventHandler(this.OnRibbonWindowTitleChanged);
CoerceValue(TitleProperty);
}
}
private void HookWindowListeners(Window win)
{
// If the Window is loaded, run logic to set IsCollapsed=true if the window is not large enough to display the Ribbon.
if (win.IsLoaded)
{
OnWindowSizeChanged(win, null);
}
win.SizeChanged += new SizeChangedEventHandler(this.OnWindowSizeChanged);
if (CheckIfWindowIsRibbonWindow(win))
{
this.IsHostedInRibbonWindow = true;
RibbonWindow rw = (RibbonWindow)win;
rw.TitleChanged += new EventHandler(this.OnRibbonWindowTitleChanged);
CoerceValue(TitleProperty); // perform Title coercion immediately as well
rw.ChangeIconVisibility(this.WindowIconVisibility);
}
}
/// <summary>
/// Property Changed CallBack for IconVisibility. This call back handler
/// calls ChangeRibbonWindowIconVisibility which propogates the changes to the Ribbon window.
/// </summary>
/// <param name="d">The Sender</param>
/// <param name="e">DependencyPropertyChangedEventArgs For the changed event</param>
private static void OnWindowIconVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Ribbon rib = (Ribbon) d;
if (rib != null)
{
if (rib.IsHostedInRibbonWindow)
{
RibbonWindow rw = (RibbonWindow)rib._window;
rw.ChangeIconVisibility(rib.WindowIconVisibility);
}
}
}
/// <summary>
/// Called when the IsOpen property changes. This means that the one of the RibbonTab's
/// popups was either opened or closed.
/// </summary>
/// <param name="sender">The Ribbon whose tab opened or closed its popup.</param>
/// <param name="e">The event data.</param>
private static void OnIsDropDownOpenChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
if (ribbon.IsMinimized)
{
// If the drop down is closed due to
// an action of context menu or if the
// ContextMenu for a parent
// was opened by right clicking this
// instance then ContextMenuClosed
// event is never raised.
// Hence reset the flag.
ribbon._inContextMenu = false;
ribbon.ContextMenuOriginalSource = null;
}
RibbonHelper.HandleIsDropDownChanged(ribbon,
delegate() { return ribbon.IsDropDownOpen; },
ribbon,
ribbon._itemsPresenterPopup.TryGetChild());
if (ribbon.IsDropDownOpen)
{
ribbon._retainFocusOnEscape = RibbonHelper.IsKeyboardMostRecentInputDevice();
ribbon.OnRibbonTabPopupOpening();
}
// Raise UI Automation Events
RibbonTab selectedTab = ribbon.ItemContainerGenerator.ContainerFromItem(ribbon.SelectedItem) as RibbonTab;
if (selectedTab != null)
{
RibbonTabAutomationPeer peer = UIElementAutomationPeer.CreatePeerForElement(selectedTab) as RibbonTabAutomationPeer;
if (peer != null)
{
peer.RaiseTabExpandCollapseAutomationEvent((bool)e.OldValue, (bool)e.NewValue);
}
}
}
/// <summary>
/// Coerces the value of the IsOpen property. Always returns true if the Ribbon
/// is not minimized.
/// </summary>
/// <param name="sender">The Ribbon whose tab state is being coerced.</param>
/// <param name="value">The new value of the IsOpen property, prior to any coercion attempt.</param>
/// <returns>The coerced value of the IsOpen property.</returns>
private static object OnCoerceIsDropDownOpen(DependencyObject sender, object value)
{
Ribbon ribbon = (Ribbon)sender;
if (!ribbon.IsMinimized)
{
return false;
}
if (!ribbon.IsLoaded ||
(ribbon._itemsPresenterPopup != null &&
!((UIElement)(ribbon._itemsPresenterPopup.Parent)).IsArrangeValid))
{
ribbon.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(ribbon.RecoerceIsDropDownOpen), ribbon);
return false;
}
return value;
}
private object RecoerceIsDropDownOpen(object arg)
{
CoerceValue(IsDropDownOpenProperty);
return null;
}
/// <summary>
/// Called when the IsMinimized property changes.
/// </summary>
/// <param name="sender">The Ribbon being minimized or expanded.</param>
/// <param name="e">The event data.</param>
private static void OnIsMinimizedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
ribbon.CoerceValue(IsDropDownOpenProperty);
if (ribbon.IsMinimized &&
!ribbon.IsDropDownOpen)
{
// If the drop down is closed due to
// an action of context menu then ContextMenuClosed
// event is never raised. Hence reset the flag.
ribbon._inContextMenu = false;
ribbon.ContextMenuOriginalSource = null;
}
// Raise UI Automation Events
RibbonAutomationPeer peer = UIElementAutomationPeer.FromElement(ribbon) as RibbonAutomationPeer;
if (peer != null)
{
peer.RaiseExpandCollapseAutomationEvent(!(bool)e.OldValue, !(bool)e.NewValue);
}
}
/// <summary>
/// Called if the Ribbon's QuickAccessToolbar changes.
/// </summary>
/// <param name="sender">The Ribbon whose QuickAccessToolbar is changing.</param>
/// <param name="e">The event data.</param>
private static void OnQuickAccessToolBarChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
RibbonQuickAccessToolBar oldRibbonQuickAccessToolBar = e.OldValue as RibbonQuickAccessToolBar;
RibbonQuickAccessToolBar newRibbonQuickAccessToolBar = e.NewValue as RibbonQuickAccessToolBar;
// Remove Logical tree link
if (oldRibbonQuickAccessToolBar != null)
{
ribbon.RemoveLogicalChild(oldRibbonQuickAccessToolBar);
}
// Add Logical tree link
if (newRibbonQuickAccessToolBar != null)
{
ribbon.AddLogicalChild(newRibbonQuickAccessToolBar);
}
}
/// <summary>
/// Called when the Ribbon's ApplicationMenu changes.
/// </summary>
/// <param name="sender">The Ribbon whose ApplicationMenu has changed.</param>
/// <param name="e">The event data.</param>
private static void OnApplicationMenuChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
RibbonApplicationMenu oldRibbonApplicationMenu = e.OldValue as RibbonApplicationMenu;
RibbonApplicationMenu newRibbonApplicationMenu = e.NewValue as RibbonApplicationMenu;
// Remove Logical tree link
if (oldRibbonApplicationMenu != null)
{
ribbon.RemoveLogicalChild(oldRibbonApplicationMenu);
}
// Add Logical tree link
if (newRibbonApplicationMenu != null)
{
ribbon.AddLogicalChild(newRibbonApplicationMenu);
}
}
/// <summary>
/// Coerces the Title property. Return Window.Title if value is not set.
/// </summary>
/// <param name="sender">
/// The Ribbon that the Title property exists on. When the callback is invoked,
/// the property system will pass this value.
/// </param>
/// <param name="value">The new value of the Title property, prior to any coercion attempt.</param>
/// <returns>The coerced value of the Title property.</returns>
private static object OnCoerceTitle(DependencyObject sender, object value)
{
Ribbon ribbon = (Ribbon)sender;
return OnCoerceTitleImpl(ribbon, value);
}
private static object OnCoerceTitleImpl(Ribbon rib, object value)
{
if (rib.IsHostedInRibbonWindow)
{
RibbonWindow rw = (RibbonWindow)rib._window;
if (!(string.IsNullOrEmpty(rw.Title as string)))
{
return rw.Title;
}
}
return value;
}
/// <summary>
/// Called when the IsCollapsed property changes.
/// </summary>
/// <param name="sender">The Ribbon being collapsed or expanded.</param>
/// <param name="e">The event data.</param>
private static void OnIsCollapsedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
ribbon.RaiseEvent(new RoutedEventArgs(ribbon.IsCollapsed ? CollapsedEvent : ExpandedEvent));
}
private static object CoerceIsCollapsed(DependencyObject d, object baseValue)
{
Window window = ((Ribbon)d)._window;
if (window != null &&
(DoubleUtil.LessThan(window.ActualWidth, CollapseWidth) ||
DoubleUtil.LessThan(window.ActualHeight, CollapseHeight)))
{
return true;
}
return baseValue;
}
/// <summary>
/// Called when the Ribbon's selected tab is opening in popup mode.
/// </summary>
private void OnRibbonTabPopupOpening()
{
if (this.IsMinimized)
{
_itemsPresenterPopup.Width = this.CalculatePopupWidth();
}
}
/// <summary>
/// Called when the ContextualTabGroups collection changes.
/// </summary>
/// <param name="sender">The Ribbon whose ContextualTabGroups collection changed.</param>
/// <param name="e">The event data.</param>
private void OnContextualTabGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ContextualTabGroupsSource != null && ContextualTabGroups.Count > 0)
{
throw new InvalidOperationException(Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSourceInvalid);
}
}
private static void OnContextualTabGroupsSourceChanged(object sender, DependencyPropertyChangedEventArgs args)
{
Ribbon ribbon = (Ribbon)sender;
if (ribbon.ContextualTabGroupsSource != null && ribbon.ContextualTabGroups.Count > 0)
{
throw new InvalidOperationException(Microsoft.Windows.Controls.SR.Ribbon_ContextualTabHeadersSourceInvalid);
}
if (ribbon.ContextualTabGroupItemsControl != null)
{
ribbon.ContextualTabGroupItemsControl.ItemsSource = (IEnumerable)args.NewValue;
}
}
private static void OnNotifyContextualTabGroupPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)d;
if (ribbon.ContextualTabGroupItemsControl != null)
{
ribbon.ContextualTabGroupItemsControl.NotifyPropertyChanged(e);
}
}
private static void OnNotifyTabHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)d;
int itemCount = ribbon.Items.Count;
for (int i = 0; i < itemCount; i++)
{
RibbonTab ribbonTab = ribbon.ItemContainerGenerator.ContainerFromIndex(i) as RibbonTab;
if (ribbonTab != null)
{
ribbonTab.NotifyPropertyChanged(e);
}
}
}
/// <summary>
/// Called when the Ribbon is loaded. This creates its QuickAccessToolbar and
/// ApplicationMenu, and also selects the initial tab.
/// </summary>
/// <param name="sender">The Ribbon being loaded.</param>
/// <param name="e">The event data.</param>
private void OnLoaded(object sender, RoutedEventArgs e)
{
this.Loaded -= new RoutedEventHandler(this.OnLoaded);
// Create default RibbonApplicationMenu if ApplicationMenu is not set
if (this.ApplicationMenu == null)
{
this.ApplicationMenu = new RibbonApplicationMenu();
}
// Create default RibbonQuickAccessToolBar if QuickAccessToolBar is not set
if (this.QuickAccessToolBar == null)
{
this.QuickAccessToolBar = new RibbonQuickAccessToolBar();
}
}
private void InitializeSelection()
{
// Select the first Tab if nothing is selected
if (SelectedIndex < 0 && Items.Count > 0)
{
// Get index of first visible non-contextual tab
int selectedIndex = GetFirstVisibleTabIndex(true /*ignoreContextualTabs*/);
if (selectedIndex < 0)
{
// Get index of first visible contextual tab.
selectedIndex = GetFirstVisibleTabIndex(false /*ignoreContextualTabs*/);
}
if (selectedIndex >= 0)
{
SelectedIndex = selectedIndex;
}
}
}
internal void ResetSelection()
{
SelectedIndex = -1;
InitializeSelection();
}
private int GetFirstVisibleTabIndex(bool ignoreContextualTabs)
{
int itemCount = Items.Count;
for (int i = 0; i < itemCount; i++)
{
RibbonTab tab = ItemContainerGenerator.ContainerFromIndex(i) as RibbonTab;
if (tab != null &&
tab.IsVisible &&
(!tab.IsContextualTab || ignoreContextualTabs))
{
return i;
}
}
return -1;
}
/// <summary>
/// Returns a value indicating whether a Window is a RibbonWindow.
/// </summary>
/// <param name="win">The Window to check.</param>
/// <returns>Returns true if the win is a RibbonWindow.</returns>
private static bool CheckIfWindowIsRibbonWindow(Window win)
{
return win is RibbonWindow;
}
/// <summary>
/// Calculate the Width of the Ribbon's popup.
/// </summary>
/// <returns>The width of the popup.</returns>
private double CalculatePopupWidth()
{
// 1. Calculate _popupPlacementTarget bounding rect in screen coordinates
// 2. Get monitor for _popupPlacementTarget rect
// 3. Get monitor size
// 4. intersect monitor rect with _popupPlacementTarget rect
// 5. return the width of the intersection
FrameworkElement popupPlacementTarget = _itemsPresenterPopup.Parent as FrameworkElement;
Point startPoint = popupPlacementTarget.PointToScreen(new Point());
Point endPoint = popupPlacementTarget.PointToScreen(new Point(popupPlacementTarget.ActualWidth, popupPlacementTarget.ActualHeight));
NativeMethods.RECT popupPlacementTargetRect = new NativeMethods.RECT();
popupPlacementTargetRect.left = (int)startPoint.X;
popupPlacementTargetRect.right = (int)endPoint.X;
popupPlacementTargetRect.top = (int)startPoint.Y;
popupPlacementTargetRect.bottom = (int)endPoint.Y;
IntPtr monitorPtr = NativeMethods.MonitorFromRect(ref popupPlacementTargetRect, NativeMethods.MONITOR_DEFAULTTONEAREST);
if (monitorPtr != IntPtr.Zero)
{
NativeMethods.MONITORINFOEX monitorInfo = new NativeMethods.MONITORINFOEX();
NativeMethods.GetMonitorInfo(new HandleRef(null, monitorPtr), monitorInfo);
NativeMethods.RECT rect = monitorInfo.rcMonitor;
Rect screenRect = new Rect(new Point(rect.left, rect.top), new Point(rect.right, rect.bottom));
Rect popupRect = new Rect(startPoint, endPoint);
screenRect.Intersect(popupRect);
double screenWidth = Math.Abs(popupPlacementTarget.PointFromScreen(screenRect.BottomRight).X -
popupPlacementTarget.PointFromScreen(screenRect.TopLeft).X);
return screenWidth + (screenRect.Right == popupRect.Right ? 5 : 0); // Account for 5px popup shadow
}
return popupPlacementTarget.RenderSize.Width;
}
/// <summary>
/// Called when the Window hosting the Ribbon changes sizes. Here we decide
/// whether or nto to collapse the Ribbon.
/// </summary>
/// <param name="sender">The Window whose size has changed.</param>
/// <param name="e">The event data.</param>
private void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
CoerceValue(IsCollapsedProperty);
}
private void OnRibbonWindowTitleChanged(object sender, EventArgs e)
{
CoerceValue(TitleProperty);
}
/// <summary>
/// Returns collection index for a given display index of tab item
/// </summary>
internal int GetTabIndexForDisplayIndex(int displayIndex)
{
if (TabDisplayIndexToIndexMap.ContainsKey(displayIndex))
{
return TabDisplayIndexToIndexMap[displayIndex];
}
return -1;
}
/// <summary>
/// Returns collection index for a given display index of tab item
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
internal int GetTabDisplayIndexForIndex(int index)
{
if (TabIndexToDisplayIndexMap.ContainsKey(index))
{
return TabIndexToDisplayIndexMap[index];
}
return -1;
}
private void RefreshHeaderCollection()
{
Debug.Assert(ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated, "Expected: containers should be generated before calling this method.");
int itemCount = Items.Count;
for (int i = 0; i < itemCount; i++)
{
RibbonTab tab = ItemContainerGenerator.ContainerFromIndex(i) as RibbonTab;
object headerItem = null;
if (tab != null)
{
headerItem = tab.Header;
}
if (headerItem == null)
{
headerItem = CreateDefaultHeaderObject();
}
if (i >= _tabHeaderItemsSource.Count)
{
_tabHeaderItemsSource.Add(headerItem);
}
else
{
_tabHeaderItemsSource[i] = headerItem;
}
}
int headersCount = _tabHeaderItemsSource.Count;
for (int i = 0; i < (headersCount - itemCount); i++)
{
_tabHeaderItemsSource.RemoveAt(itemCount);
}
}
/// <summary>
/// Creates a default header object to host in header itemscontrol.
/// This is used when RibbonTab.Header property is not set.
/// </summary>
private static object CreateDefaultHeaderObject()
{
return new SingleSpaceObject();
}
private static void OnBorderBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Ribbon ribbon = (Ribbon)d;
if (ribbon._tabHeaderItemsControl != null)
{
RibbonTabHeadersPanel tabHeadersPanel = ribbon._tabHeaderItemsControl.InternalItemsHost as RibbonTabHeadersPanel;
if (tabHeadersPanel != null)
{
tabHeadersPanel.OnNotifyRibbonBorderBrushChanged();
}
}
RibbonContextualTabGroupItemsControl contextualItemsControl = ribbon.ContextualTabGroupItemsControl;
if (contextualItemsControl != null)
{
RibbonContextualTabGroupsPanel contextualTabHeadersPanel = contextualItemsControl.InternalItemsHost as RibbonContextualTabGroupsPanel;
if (contextualTabHeadersPanel != null)
{
contextualTabHeadersPanel.OnNotifyRibbonBorderBrushChanged();
}
}
}
private static object CoerceIsMinimized(DependencyObject d, object baseValue)
{
bool isMinimized = (bool)baseValue;
Ribbon ribbon = (Ribbon)d;
if (isMinimized && ribbon.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
return false;
}
return baseValue;
}
private void OnItemContainerGeneratorStatusChanged(object sender, EventArgs e)
{
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
CoerceValue(IsMinimizedProperty);
InitializeSelection();
RefreshHeaderCollection();
}
}
private void HandleIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (IsVisible)
{
// If the ribbon start invisible InitializeSelection would have
// failed. Hence call InitializeSelection again. Using a
// dispatcher operation because the tabs might not be visible yet.
// This is common scenario when hosted in WinForms.
Dispatcher.BeginInvoke(
(Action)delegate()
{
InitializeSelection();
},
DispatcherPriority.Normal,
null);
}
}
#endregion
#region Dismiss Popups
private static void OnLostMouseCaptureThunk(object sender, MouseEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
ribbon.OnLostMouseCaptureThunk(e);
}
private void OnLostMouseCaptureThunk(MouseEventArgs e)
{
RibbonHelper.HandleLostMouseCapture(this,
e,
delegate() { return (IsDropDownOpen && !_inContextMenu); },
delegate(bool value) { IsDropDownOpen = value; },
this,
_itemsPresenterPopup.TryGetChild());
}
private static void OnClickThroughThunk(object sender, MouseButtonEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
ribbon.OnClickThrough(e);
}
private void OnClickThrough(MouseButtonEventArgs e)
{
RibbonHelper.HandleClickThrough(this, e, _itemsPresenterPopup.TryGetChild());
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
_retainFocusOnEscape = false;
if (IsDropDownOpen)
{
// Close the drop down if the click happened any where outside the popup
// and tab header items control.
if (!RibbonHelper.IsAncestorOf(_itemsPresenterPopup.TryGetChild(), e.OriginalSource as DependencyObject) &&
!RibbonHelper.IsAncestorOf(_tabHeaderItemsControl, e.OriginalSource as DependencyObject))
{
IsDropDownOpen = false;
}
}
}
private static void OnDismissPopupThunk(object sender, RibbonDismissPopupEventArgs e)
{
Ribbon ribbon = (Ribbon)sender;
ribbon.OnDismissPopup(e);
}
private void OnDismissPopup(RibbonDismissPopupEventArgs e)
{
RibbonHelper.HandleDismissPopup(e,
DismissPopupAction,
delegate(DependencyObject d) { return false; },
_itemsPresenterPopup.TryGetChild(),
_tabHeaderItemsControl);
}
private void DismissPopupAction(bool value)
{
IsDropDownOpen = value;
if (!value && !IsMinimized)
{
RestoreFocusAndCapture(false);
}
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
// Restore the focus and capture if
// 1) The event is not handled
// 2) The Ribbon DropDown in not open
// 3) The button changed is not mouse right button
// 4) And the original source belongs to the visual tree of ribbon.
if (!e.Handled &&
!IsDropDownOpen &&
e.ChangedButton != MouseButton.Right &&
TreeHelper.IsVisualAncestorOf(this, e.OriginalSource as DependencyObject))
{
RestoreFocusAndCapture(false);
}
}
#endregion Dismiss Poups
#region Keyboard Navigation
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (!e.Handled)
{
RibbonHelper.HandleDropDownKeyDown(this, e,
delegate { return IsDropDownOpen; },
delegate(bool value) { IsDropDownOpen = value; },
_retainFocusOnEscape ? SelectedTabHeader : null,
_itemsPresenter);
}
if (!e.Handled)
{
if (e.Key == Key.Escape)
{
RestoreFocusAndCapture(false);
}
else if ((e.Key == Key.Left || e.Key == Key.Right) &&
((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control))
{
e.Handled = OnArrowControlKeyDown(e.Key);
}
}
}
private bool OnArrowControlKeyDown(Key key)
{
RibbonQuickAccessToolBar quickAccessToolBar = QuickAccessToolBar;
if (quickAccessToolBar != null && !quickAccessToolBar.IsLoaded)
{
quickAccessToolBar = null;
}
RibbonTabHeaderItemsControl tabHeaderItemsControl = RibbonTabHeaderItemsControl;
RibbonTab selectedTab = null;
int selectedIndex = SelectedIndex;
if (selectedIndex >= 0)
{
selectedTab = ItemContainerGenerator.ContainerFromIndex(selectedIndex) as RibbonTab;
}
if ((quickAccessToolBar != null && quickAccessToolBar.IsKeyboardFocusWithin) ||
(tabHeaderItemsControl != null && tabHeaderItemsControl.IsKeyboardFocusWithin) ||
(selectedTab != null && selectedTab.IsKeyboardFocusWithin))
{
ArrowKeyControlNavigationScope startingNavigationScope = ArrowKeyControlNavigationScope.Tab;
if (quickAccessToolBar != null && quickAccessToolBar.IsKeyboardFocusWithin)
{
startingNavigationScope = ArrowKeyControlNavigationScope.QuickAccessToolbar;
}
else if (tabHeaderItemsControl != null && tabHeaderItemsControl.IsKeyboardFocusWithin)
{
startingNavigationScope = ArrowKeyControlNavigationScope.TabHeaders;
}
return ArrowKeyControlNavigate((key == Key.Left)/* navigateLeft */,
quickAccessToolBar,
selectedTab,
startingNavigationScope);
}
return false;
}
private enum ArrowKeyControlNavigationScope
{
QuickAccessToolbar,
TabHeaders,
Tab
}
private RibbonTabHeader SelectedTabHeader
{
get
{
RibbonTabHeaderItemsControl tabHeaderItemsControl = RibbonTabHeaderItemsControl;
if (tabHeaderItemsControl == null ||
!tabHeaderItemsControl.IsVisible)
{
return null;
}
int selectedIndex = SelectedIndex;
if (selectedIndex >= 0)
{
return (tabHeaderItemsControl.ItemContainerGenerator.ContainerFromIndex(selectedIndex) as RibbonTabHeader);
}
return null;
}
}
/// <summary>
/// Helper method to focus the header of selected tab.
/// Returns success of the focus change.
/// </summary>
private bool FocusSelectedTabHeader()
{
RibbonTabHeader tabHeader = SelectedTabHeader;
if (tabHeader != null)
{
tabHeader.Focus();
return tabHeader.IsKeyboardFocusWithin;
}
return false;
}
/// <summary>
/// Helper method to navigate through groups of the selected
/// tab when left/right arrow keys are pressed along with CTRL.
/// Returns success of such navigation.
/// </summary>
private bool TabArrowKeyControlNavigate(RibbonTab tab,
bool leftToRight,
bool startFromCurrent,
bool cycle)
{
if (tab == null)
{
return false;
}
return ArrowKeyControlNavigate<RibbonTab>(tab,
leftToRight,
startFromCurrent,
cycle,
tab.Items.Count,
null,
GetFocusedRibbonGroupIndex,
TrySetFocusOnRibbonGroupAtIndex);
}
private int GetFocusedRibbonGroupIndex(RibbonTab tab)
{
RibbonGroup ribbonGroup = TreeHelper.FindVisualAncestor<RibbonGroup>(Keyboard.FocusedElement as DependencyObject);
if (ribbonGroup == null)
{
return -1;
}
return tab.ItemContainerGenerator.IndexFromContainer(ribbonGroup);
}
private bool TrySetFocusOnRibbonGroupAtIndex(RibbonTab tab,
int index)
{
RibbonGroup group = tab.ItemContainerGenerator.ContainerFromIndex(index) as RibbonGroup;
if (group != null &&
group.IsVisible)
{
group.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
if (group.IsKeyboardFocusWithin)
{
return true;
}
}
return false;
}
/// <summary>
/// Helper method to navigate through items of the qat (including
/// customize menu) when left/right arrow keys are pressed along with CTRL.
/// Returns success of such navigation.
/// </summary>
private bool QatArrowKeyControlNavigate(bool leftToRight,
bool startFromCurrent,
bool cycle)
{
RibbonQuickAccessToolBar quickAccessToolBar = QuickAccessToolBar;
if (quickAccessToolBar == null)
{
return false;
}
return ArrowKeyControlNavigate<RibbonQuickAccessToolBar>(quickAccessToolBar,
leftToRight,
startFromCurrent,
cycle,
quickAccessToolBar.Items.Count,
quickAccessToolBar.CustomizeMenuButton,
GetFocusedQatItemIndex,
TrySetFocusOnQatItemAtIndex);
}
/// <summary>
/// Helper method to set focus on the item at given index of qat.
/// </summary>
private bool TrySetFocusOnQatItemAtIndex(RibbonQuickAccessToolBar quickAccessToolBar,
int index)
{
RibbonControl ribbonControl = quickAccessToolBar.ItemContainerGenerator.ContainerFromIndex(index) as RibbonControl;
if (ribbonControl != null &&
ribbonControl.IsVisible &&
(index == 0 || ribbonControl.HostsRibbonGroup()))
{
ribbonControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
if (ribbonControl.IsKeyboardFocusWithin)
{
return true;
}
}
return false;
}
/// <summary>
/// Helper method to get the index of item which has focus within.
/// </summary>
private int GetFocusedQatItemIndex(RibbonQuickAccessToolBar quickAccessToolBar)
{
RibbonControl ribbonControl = TreeHelper.FindVisualAncestor<RibbonControl>(Keyboard.FocusedElement as DependencyObject);
if (ribbonControl == null)
{
return -1;
}
return quickAccessToolBar.ItemContainerGenerator.IndexFromContainer(ribbonControl);
}
/// <summary>
/// Helper method to do left/right arrow key control
/// navigation through items bases controls.
/// e.g, RibbonTab and RibbonQuickAccessToolbar.
/// Returns success of the operation.
/// </summary>
private static bool ArrowKeyControlNavigate<T>(T targetControl,
bool leftToRight,
bool startFromCurrent,
bool cycle,
int itemCount,
Control extraControl,
Func<T, int> getFocusedItemIndex,
Func<T, int, bool> trySetFocusAtItemIndex) where T : Control
{
if (targetControl == null ||
!targetControl.IsVisible)
{
return false;
}
// If focus belongs to one of the sub popups
// then do not navigate
if (targetControl.IsKeyboardFocusWithin &&
!TreeHelper.IsVisualAncestorOf(targetControl, Keyboard.FocusedElement as DependencyObject))
{
return false;
}
int attemptCount = 0;
int currentIndex = DeterminePreStartIndexForArrowControlNavigation<T>(targetControl,
startFromCurrent,
leftToRight,
cycle,
extraControl,
itemCount,
getFocusedItemIndex);
bool considerExtraControl = (extraControl != null && extraControl.IsVisible);
if (currentIndex == Int32.MinValue)
{
return false;
}
int incr = (leftToRight ? 1 : -1);
// iterate through the items in requested order and try to
// give focus to one of them. Cycle if needed.
while (attemptCount <= itemCount)
{
attemptCount++;
currentIndex += incr;
if (leftToRight && currentIndex == itemCount)
{
if (considerExtraControl && extraControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)))
{
return true;
}
if (cycle)
{
currentIndex = -1;
}
else
{
return false;
}
}
else if (!leftToRight && (currentIndex < 0 || currentIndex == itemCount))
{
if (currentIndex < 0 && !cycle)
{
return false;
}
if (considerExtraControl &&
extraControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)))
{
return true;
}
currentIndex = itemCount;
}
else
{
if (trySetFocusAtItemIndex(targetControl, currentIndex))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Helper method to determine the start item index of the
/// items based controls (like RibbonQuickAccessToolbar
/// and RibbonTab) to start left/right arrow control key
/// navigation. A return value of Int32.MinValue means
/// uninterested scenario.
/// </summary>
private static int DeterminePreStartIndexForArrowControlNavigation<T>(T targetControl,
bool startFromCurrent,
bool leftToRight,
bool cycle,
Control extraControl,
int itemCount,
Func<T, int> getFocusedItemIndex) where T : Control
{
int startIndex = 0;
bool considerExtraControl = (extraControl != null && extraControl.IsVisible);
if (startFromCurrent)
{
if (!targetControl.IsKeyboardFocusWithin)
{
// Cannot start from current if there is not focus within.
return Int32.MinValue;
}
if (considerExtraControl && extraControl.IsKeyboardFocusWithin)
{
if (leftToRight)
{
if (!cycle)
{
return Int32.MinValue;
}
startIndex = -1;
}
else
{
startIndex = itemCount;
}
}
else
{
startIndex = getFocusedItemIndex(targetControl);
if (startIndex < 0)
{
return Int32.MinValue;
}
}
}
else
{
if (leftToRight)
{
startIndex = -1;
}
else
{
startIndex = itemCount + 1;
}
}
return startIndex;
}
/// <summary>
/// Method to navigate through ribbon when left/right
/// arrow keys are pressed along with CTRL.
/// </summary>
private bool ArrowKeyControlNavigate(bool navigateLeft,
RibbonQuickAccessToolBar quickAccessToolBar,
RibbonTab selectedTab,
ArrowKeyControlNavigationScope startingNavigationScope)
{
DependencyObject focusedElement = Keyboard.FocusedElement as DependencyObject;
if (focusedElement != null &&
!TreeHelper.IsVisualAncestorOf(this, focusedElement) &&
_itemsPresenterPopup != null &&
_itemsPresenterPopup.Child != null &&
!TreeHelper.IsVisualAncestorOf(_itemsPresenterPopup.Child, focusedElement))
{
// If the focused element is in uninteresting popups,
// then fail.
return false;
}
bool isRTL = (FlowDirection == FlowDirection.RightToLeft);
ArrowKeyControlNavigationScope currentNavigationScope = startingNavigationScope;
int attemptCount = 0;
while (attemptCount < 3)
{
attemptCount++;
switch (currentNavigationScope)
{
case ArrowKeyControlNavigationScope.QuickAccessToolbar:
if (quickAccessToolBar != null && quickAccessToolBar.IsVisible && quickAccessToolBar.IsKeyboardFocusWithin)
{
// Try to navigate through remaining qat items if focus is already in qat.
if (QatArrowKeyControlNavigate((navigateLeft == isRTL) /* leftToRight */,
true,
IsMinimized && !IsDropDownOpen /* cycle */))
{
return true;
}
}
if (navigateLeft)
{
// Try to navigate into groups of the selected tab.
if (TabArrowKeyControlNavigate(selectedTab, isRTL /*leftToRight*/, false, IsDropDownOpen))
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.Tab;
}
else
{
// Navigate to the selected tab header
if (FocusSelectedTabHeader())
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.TabHeaders;
}
break;
case ArrowKeyControlNavigationScope.TabHeaders:
if (navigateLeft)
{
// Try to navigate into the items of qat.
if (QatArrowKeyControlNavigate(isRTL /* leftToRight */,
false,
IsMinimized && !IsDropDownOpen /* cycle */))
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.QuickAccessToolbar;
}
else
{
// Try to navigate to groups of selected tab.
if (TabArrowKeyControlNavigate(selectedTab,
!isRTL /* leftToRight */,
false,
IsDropDownOpen))
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.Tab;
}
break;
case ArrowKeyControlNavigationScope.Tab:
if (selectedTab != null && selectedTab.IsVisible && selectedTab.IsKeyboardFocusWithin)
{
// Try to navigate through the remaining groups if the focus is already in selected tab.
if (TabArrowKeyControlNavigate(selectedTab,
(navigateLeft == isRTL) /* leftToRight */,
true,
IsDropDownOpen))
{
return true;
}
}
if (navigateLeft)
{
// Navigate to the selected tab header
if (FocusSelectedTabHeader())
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.TabHeaders;
}
else
{
// Try to navigate in the items of qat.
if (QatArrowKeyControlNavigate(!isRTL /*leftToRight*/,
false,
(IsMinimized && !IsDropDownOpen)))
{
return true;
}
currentNavigationScope = ArrowKeyControlNavigationScope.QuickAccessToolbar;
}
break;
}
}
return false;
}
#endregion
#region KeyTips
private bool OnKeyTipEnterFocus(object sender, EventArgs e)
{
PresentationSource targetSource = sender as PresentationSource;
if (targetSource == RibbonHelper.GetPresentationSourceFromVisual(this))
{
// Focus the selected tab header if this Ribbon belongs
// to concerned presentation source.
return FocusSelectedTabHeader();
}
return false;
}
private bool OnKeyTipExitRestoreFocus(object sender, EventArgs e)
{
PresentationSource targetSource = sender as PresentationSource;
if (targetSource == RibbonHelper.GetPresentationSourceFromVisual(this))
{
// Restore the focus if the Ribbon belongs to
// the concerned presentation source.
RestoreFocusAndCapture(true);
}
return false;
}
internal void RestoreFocusAndCapture(bool force)
{
// Restore the focus only if not in keytip mode or if forced to.
if (KeyTipService.Current.State == KeyTipService.KeyTipState.None ||
force)
{
RibbonHelper.RestoreFocusAndCapture(this, this);
}
}
#endregion
#region Context Menu
private static void OnContextMenuOpeningThunk(object sender, ContextMenuEventArgs e)
{
((Ribbon)sender).OnContextMenuOpeningInternal(e);
}
private void OnContextMenuOpeningInternal(ContextMenuEventArgs e)
{
ContextMenuOriginalSource = e.OriginalSource as UIElement;
_inContextMenu = true;
}
internal UIElement ContextMenuOriginalSource
{
get;
private set;
}
private static void OnContextMenuClosingThunk(object sender, ContextMenuEventArgs e)
{
((Ribbon)sender).OnContextMenuClosingInternal();
}
private void OnContextMenuClosingInternal()
{
_inContextMenu = false;
ContextMenuOriginalSource = null;
if (IsDropDownOpen)
{
RibbonHelper.AsyncSetFocusAndCapture(this,
delegate() { return IsDropDownOpen; },
this,
_itemsPresenterPopup.TryGetChild());
}
}
internal void RestoreFocusOnContextMenuClose()
{
if (!IsDropDownOpen)
{
RestoreFocusAndCapture(false);
}
}
#endregion
#region QAT
private static void AddToQATCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject obj = args.OriginalSource as DependencyObject;
// Find nearest element that can be added to the QAT directly
obj = FindElementThatCanBeAddedToQAT(obj);
if (obj != null &&
RibbonControlService.GetQuickAccessToolBarId(obj) != null &&
!RibbonHelper.ExistsInQAT(obj))
{
args.CanExecute = true;
}
}
private static void AddToQATExecuted(object sender, ExecutedRoutedEventArgs args)
{
UIElement originalSource = args.OriginalSource as UIElement;
// Find nearest element that can be added to the QAT directly
originalSource = FindElementThatCanBeAddedToQAT(originalSource) as UIElement;
if (originalSource != null)
{
RibbonQuickAccessToolBarCloneEventArgs e = new RibbonQuickAccessToolBarCloneEventArgs(originalSource);
originalSource.RaiseEvent(e);
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
ribbon.QuickAccessToolBar != null &&
e.CloneInstance != null)
{
ribbon.QuickAccessToolBar.Items.Add(e.CloneInstance);
args.Handled = true;
}
}
}
private static DependencyObject FindElementThatCanBeAddedToQAT(DependencyObject obj)
{
while (obj != null && !RibbonControlService.GetCanAddToQuickAccessToolBarDirectly(obj))
{
obj = TreeHelper.GetParent(obj);
}
return obj;
}
private static void MaximizeRibbonCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
ribbon.IsMinimized)
{
args.CanExecute = true;
}
}
}
private static void MaximizeRibbonExecuted(object sender, ExecutedRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null)
{
ribbon.IsMinimized = false;
args.Handled = true;
}
}
}
private static void MinimizeRibbonCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
!ribbon.IsMinimized)
{
args.CanExecute = true;
}
}
}
private static void MinimizeRibbonExecuted(object sender, ExecutedRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null)
{
ribbon.IsMinimized = true;
args.Handled = true;
}
}
}
private static void RemoveFromQATCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject obj = args.OriginalSource as DependencyObject;
if (obj != null)
{
args.CanExecute = RibbonControlService.GetIsInQuickAccessToolBar(obj);
}
}
private static void RemoveFromQATExecuted(object sender, ExecutedRoutedEventArgs args)
{
UIElement originalSource = args.OriginalSource as UIElement;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
ribbon.QuickAccessToolBar != null)
{
RibbonQuickAccessToolBar qat = ribbon.QuickAccessToolBar;
if (qat.Items.Contains(originalSource))
{
qat.Items.Remove(originalSource);
args.Handled = true;
}
}
}
}
private static void ShowQATAboveCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
ribbon.QuickAccessToolBar != null &&
!ribbon.ShowQuickAccessToolBarOnTop)
{
args.CanExecute = true;
}
}
}
private static void ShowQATAboveExecuted(object sender, ExecutedRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null)
{
ribbon.ShowQuickAccessToolBarOnTop = true;
args.Handled = true;
}
}
}
private static void ShowQATBelowCanExecute(object sender, CanExecuteRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null &&
ribbon.QuickAccessToolBar != null &&
ribbon.ShowQuickAccessToolBarOnTop)
{
args.CanExecute = true;
}
}
}
private static void ShowQATBelowExecuted(object sender, ExecutedRoutedEventArgs args)
{
DependencyObject originalSource = args.OriginalSource as DependencyObject;
if (originalSource != null)
{
Ribbon ribbon = RibbonControlService.GetRibbon(originalSource);
if (ribbon != null)
{
ribbon.ShowQuickAccessToolBarOnTop = false;
args.Handled = true;
}
}
}
// Produce a duplicate UIElement. Cloning requires several steps:
// 1) Create an instance with special processing for RibbonMenuItem and RibbonSplitMenuItem.
// 2) Transfer all of the properties that are either template generated or locally set.
// 3) Create a wrapper around a Ribbongallery.
// 4) Transfer relevant properties to the wrapper instance.
private static void OnCloneThunk(object sender, RibbonQuickAccessToolBarCloneEventArgs e)
{
// If the cloning has not yet been performed (i.e. by a
// user-supplied handler), then perform the cloning ourselves.
if (e.CloneInstance == null)
{
RibbonHelper.PopulatePropertyLists();
bool allowTransformations = true;
e.CloneInstance = (UIElement)RibbonHelper.CreateClone(e.InstanceToBeCloned, allowTransformations);
e.Handled = true;
}
}
#endregion QAT
}
}
|