File: Microsoft\Windows\Controls\KeyTipAdorner.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\System.Windows.Controls.Ribbon\System.Windows.Controls.Ribbon_smvy2x3f_wpftmp.csproj (System.Windows.Controls.Ribbon)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
        
 
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
#if RIBBON_IN_FRAMEWORK
using System.Windows.Controls.Ribbon;
#else
using Microsoft.Windows.Controls.Ribbon;
#endif
using MS.Internal;
#if RIBBON_IN_FRAMEWORK
namespace System.Windows.Controls
#else
namespace Microsoft.Windows.Controls
#endif
{
    /// <summary>
    ///     The adorner control which is used to render KeyTip
    /// </summary>
    internal class KeyTipAdorner : Adorner
    {
        #region Constructor
 
        public KeyTipAdorner(UIElement adornedElement,
            UIElement placementTarget,
            KeyTipHorizontalPlacement horizontalPlacement,
            KeyTipVerticalPlacement verticalPlacement,
            double horizontalOffset,
            double verticalOffset,
            RibbonGroup ownerRibbonGroup)
            : base(adornedElement)
        {
            PlacementTarget = (placementTarget == null ? adornedElement : placementTarget);
            HorizontalPlacement = horizontalPlacement;
            VerticalPlacement = verticalPlacement;
            HorizontalOffset = horizontalOffset;
            VerticalOffset = verticalOffset;
            OwnerRibbonGroup = ownerRibbonGroup;
        }
 
        #endregion
 
        #region Basic Adorner
 
        protected override Visual GetVisualChild(int index)
        {
            if (index != 0 ||
                _keyTipControl == null)
            {
                throw new ArgumentOutOfRangeException("index");
            }
            return _keyTipControl;
        }
 
        protected override int VisualChildrenCount
        {
            get
            {
                return (_keyTipControl == null ? 0 : 1);
            }
        }
 
        protected override Size MeasureOverride(Size constraint)
        {
            if (_keyTipControl != null)
            {
                Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
                _keyTipControl.Measure(childConstraint);
            }
            return new Size(0, 0);
        }
 
        protected override Size ArrangeOverride(Size finalSize)
        {
            if (_keyTipControl != null)
            {
                _keyTipControl.Arrange(new Rect(_keyTipControl.DesiredSize));
            }
            return finalSize;
        }
 
        #endregion
 
        #region KeyTipControl Management
 
        /// <summary>
        ///     Links the given KeyTipControl as the visual child of self.
        ///     In the process sets various properties of the control.
        /// </summary>
        public void LinkKeyTipControl(DependencyObject keyTipElement, KeyTipControl keyTipControl)
        {
            Debug.Assert(_keyTipControl == null && keyTipControl.KeyTipAdorner == null);
            _keyTipControl = keyTipControl;
            _keyTipControl.KeyTipAdorner = this;
            _keyTipControl.Text = KeyTipService.GetKeyTip(keyTipElement).ToUpper(KeyTipService.GetCultureForElement(keyTipElement));
            _keyTipControl.IsEnabled = (bool)keyTipElement.GetValue(UIElement.IsEnabledProperty);
            Style keyTipStyle = KeyTipService.GetKeyTipStyle(keyTipElement);
            _keyTipControl.Style = keyTipStyle;
            _keyTipControl.RenderTransform = _keyTipTransform;
            bool clearCustomProperties = true;
            if (keyTipStyle == null)
            {
                Ribbon.Ribbon ribbon = RibbonControlService.GetRibbon(PlacementTarget);
                if (ribbon != null)
                {
                    // Use Ribbon properties if the owner element belongs to a Ribbon.
                    keyTipStyle = KeyTipService.GetKeyTipStyle(ribbon);
                    if (keyTipStyle != null)
                    {
                        _keyTipControl.Style = keyTipStyle;
                    }
                    else
                    {
                        clearCustomProperties = false;
                        _keyTipControl.Background = ribbon.Background;
                        _keyTipControl.BorderBrush = ribbon.BorderBrush;
                        _keyTipControl.Foreground = ribbon.Foreground;
                    }
                }
            }
            if (clearCustomProperties)
            {
                _keyTipControl.ClearValue(Control.BackgroundProperty);
                _keyTipControl.ClearValue(Control.BorderBrushProperty);
                _keyTipControl.ClearValue(Control.ForegroundProperty);
            }
            AddVisualChild(_keyTipControl);
            EnsureTransform();
        }
 
        /// <summary>
        ///     Unlinks the earlier linked KeyTipControl from visual tree.
        /// </summary>
        public void UnlinkKeyTipControl()
        {
            if (_keyTipControl != null)
            {
                _keyTipControl.KeyTipAdorner = null;
                RemoveVisualChild(_keyTipControl);
                _keyTipControl = null;
            }
        }
 
        public KeyTipControl KeyTipControl
        {
            get
            {
                return _keyTipControl;
            }
        }
 
        #endregion
 
        #region KeyTip Placement
 
        private KeyTipHorizontalPlacement HorizontalPlacement { get; set; }
        private KeyTipVerticalPlacement VerticalPlacement { get; set; }
        private double HorizontalOffset { get; set; }
        private double VerticalOffset { get; set; }
        private UIElement PlacementTarget { get; set; }
        private RibbonGroup OwnerRibbonGroup { get; set; }
 
        /// <summary>
        ///     Invalidate X/Y properties of keytip transform
        ///     when size of KeyTipControl changes accordingly.
        /// </summary>
        /// <param name="e"></param>
        internal void OnKeyTipControlSizeChanged(SizeChangedEventArgs e)
        {
            if (e.WidthChanged)
            {
                EnsureTransformX();
            }
            if (e.HeightChanged)
            {
                EnsureTransformY();
            }
        }
 
        private void EnsureTransform()
        {
            EnsureTransformX();
            EnsureTransformY();
        }
 
        /// <summary>
        ///     Updates X of keytip transform.
        /// </summary>
        private void EnsureTransformX()
        {
            UIElement placementTarget = PlacementTarget;
            if (placementTarget != null)
            {
                int horizontalPlacementValue = (int)HorizontalPlacement;
                double horizontalPosition = 0;
                if (horizontalPlacementValue >= 0 && horizontalPlacementValue < 9)
                {
                    switch (horizontalPlacementValue % 3)
                    {
                        case 1:
                            // compensate horizontal position for center of target
                            horizontalPosition += (placementTarget.RenderSize.Width / 2);
                            break;
                        case 2:
                            // compensate horizontal position for right of target
                            horizontalPosition += placementTarget.RenderSize.Width;
                            break;
                    }
 
                    if (_keyTipControl != null)
                    {
                        if (horizontalPlacementValue >= 6)
                        {
                            // compensate horizontal position for right of keytip
                            horizontalPosition -= _keyTipControl.ActualWidth;
                        }
                        else if (horizontalPlacementValue >= 3)
                        {
                            // compensate horizontal position for center of keytip
                            horizontalPosition -= (_keyTipControl.ActualWidth / 2);
                        }
                    }
                }
 
                horizontalPosition += HorizontalOffset;
                _keyTipTransform.X = horizontalPosition;
            }
            else
            {
                _keyTipTransform.X = 0;
            }
        }
 
        /// <summary>
        ///     Updates Y of keytip transform.
        /// </summary>
        private void EnsureTransformY()
        {
            UIElement placementTarget = PlacementTarget;
            if (placementTarget == null)
            {
                placementTarget = AdornedElement;
            }
 
            if (placementTarget != null)
            {
                int verticalPlacementValue = (int)VerticalPlacement;
                double verticalPosition = 0;
                if (verticalPlacementValue >= 0 && verticalPlacementValue < 9)
                {
                    switch (verticalPlacementValue % 3)
                    {
                        case 1:
                            // compensate vertical position for center of target
                            verticalPosition += (placementTarget.RenderSize.Height / 2);
                            break;
                        case 2:
                            // compensate vertical position for bottom of target
                            verticalPosition += placementTarget.RenderSize.Height;
                            break;
                    }
 
                    if (_keyTipControl != null)
                    {
                        if (verticalPlacementValue >= 6)
                        {
                            // compensate vertical position for bottom of keytip
                            verticalPosition -= _keyTipControl.ActualHeight;
                        }
                        else if (verticalPlacementValue >= 3)
                        {
                            // compensate vertical position for center of keytip
                            verticalPosition -= (_keyTipControl.ActualHeight / 2);
                        }
                    }
                }
                verticalPosition += VerticalOffset;
                verticalPosition = NudgeToRibbonGroupAxis(placementTarget, verticalPosition);
                _keyTipTransform.Y = verticalPosition;
            }
            else
            {
                _keyTipTransform.Y = 0;
            }
        }
 
        /// <summary>
        ///     Helper method to nudge the vertical postion of keytip,
        ///     to RibbonGroup's top/bottom axis if applicable.
        /// </summary>
        private double NudgeToRibbonGroupAxis(UIElement placementTarget, double verticalPosition)
        {
            if (OwnerRibbonGroup != null)
            {
                ItemsPresenter itemsPresenter = OwnerRibbonGroup.ItemsPresenter;
                if (itemsPresenter != null)
                {
                    GeneralTransform transform = placementTarget.TransformToAncestor(itemsPresenter);
                    Point targetOrigin = transform.Transform(new Point());
                    double keyTipTopY = verticalPosition + targetOrigin.Y;
                    double keyTipCenterY = keyTipTopY;
                    double keyTipBottomY = keyTipTopY;
                    if (_keyTipControl != null)
                    {
                        keyTipBottomY += _keyTipControl.ActualHeight;
                        keyTipCenterY += _keyTipControl.ActualHeight / 2;
                    }
 
                    if (DoubleUtil.LessThan(Math.Abs(keyTipTopY), RibbonGroupKeyTipAxisNudgeSpace))
                    {
                        // Nudge to top axis
                        verticalPosition -= (keyTipCenterY - RibbonGroupKeyTipAxisOffset);
                    }
                    else if (DoubleUtil.LessThan(Math.Abs(itemsPresenter.ActualHeight - keyTipBottomY), RibbonGroupKeyTipAxisNudgeSpace))
                    {
                        // Nudge to bottom axis
                        double centerOffsetFromGroupBottom = keyTipCenterY - itemsPresenter.ActualHeight;
                        verticalPosition -= (centerOffsetFromGroupBottom + RibbonGroupKeyTipAxisOffset);
                    }
                }
            }
            return verticalPosition;
        }
 
        /// <summary>
        ///     Helper method to nudge the keytip into the
        ///     boundary of the adorner layer.
        /// </summary>
        internal void NudgeIntoAdornerLayerBoundary(AdornerLayer adornerLayer)
        {
            if (_keyTipControl != null && _keyTipControl.IsLoaded)
            {
                Point adornerOrigin = this.TranslatePoint(new Point(), adornerLayer);
                Rect adornerLayerRect = new Rect(0, 0, adornerLayer.ActualWidth, adornerLayer.ActualHeight);
                Rect keyTipControlRect = new Rect(adornerOrigin.X + _keyTipTransform.X,
                    adornerOrigin.Y + _keyTipTransform.Y,
                    _keyTipControl.ActualWidth,
                    _keyTipControl.ActualHeight);
                if (adornerLayerRect.IntersectsWith(keyTipControlRect) &&
                    !adornerLayerRect.Contains(keyTipControlRect))
                {
                    double deltaX = 0;
                    double deltaY = 0;
 
                    // Nudge the keytip control horizontally if its left or right
                    // edge falls outside the adornerlayer.
                    if (DoubleUtil.LessThan(keyTipControlRect.Left, adornerLayerRect.Left))
                    {
                        deltaX = adornerLayerRect.Left - keyTipControlRect.Left;
                    }
                    else if (DoubleUtil.GreaterThan(keyTipControlRect.Right, adornerLayerRect.Right))
                    {
                        deltaX = adornerLayerRect.Right - keyTipControlRect.Right;
                    }
 
                    // Nudge the keytip control vertically if its top or bottom
                    // edge falls outside the adornerlayer.
                    if (DoubleUtil.LessThan(keyTipControlRect.Top, adornerLayerRect.Top))
                    {
                        deltaY = adornerLayerRect.Top - keyTipControlRect.Top;
                    }
                    else if (DoubleUtil.GreaterThan(keyTipControlRect.Bottom, adornerLayerRect.Bottom))
                    {
                        deltaY = adornerLayerRect.Bottom - keyTipControlRect.Bottom;
                    }
 
                    _keyTipTransform.X += deltaX;
                    _keyTipTransform.Y += deltaY;
                }
            }
        }
 
        #endregion
 
        #region Private Data
 
        KeyTipControl _keyTipControl;
        private TranslateTransform _keyTipTransform = new TranslateTransform(0, 0);
 
        private const double RibbonGroupKeyTipAxisNudgeSpace = 15;
        private const double RibbonGroupKeyTipAxisOffset = 5;
 
        #endregion
    }
}