File: System.Activities.Presentation\System\Activities\Presentation\Annotations\AnnotationAdorner.cs
Project: ndp\cdf\src\NetFx40\Tools\System.Activities.Presentation.csproj (System.Activities.Presentation)
//----------------------------------------------------------------
// <copyright company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//----------------------------------------------------------------
 
namespace System.Activities.Presentation.Annotations
{
    using System.Activities.Presentation.View;
    using System.Runtime;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Media;
 
    internal class AnnotationAdorner : Adorner
    {
        public static readonly DependencyProperty AnchorProperty = DependencyProperty.RegisterAttached("Anchor", typeof(AdornerLocation), typeof(AnnotationAdorner), new FrameworkPropertyMetadata(AdornerLocation.None));
        private UIElement content;
 
        public AnnotationAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
        }
 
        internal ScrollViewer ScrollViewer
        {
            get;
            set;
        }
 
        internal UIElement Content
        {
            get
            {
                return this.content;
            }
 
            set
            {
                if (this.content != value)
                {
                    if (this.content != null)
                    {
                        this.RemoveVisualChild(this.content);
                    }
 
                    this.content = value;
                    if (this.content != null)
                    {
                        this.AddVisualChild(this.content);
                    }
                }
            }
        }
 
        protected override int VisualChildrenCount
        {
            get
            {
                return 1;
            }
        }
 
        public static AdornerLocation GetAnchor(DependencyObject obj)
        {
            return (AdornerLocation)obj.GetValue(AnnotationAdorner.AnchorProperty);
        }
 
        public static void SetAnchor(DependencyObject obj, AdornerLocation anchor)
        {
            obj.SetValue(AnnotationAdorner.AnchorProperty, anchor);
        }
 
        // (3)   |   (2)
        // ______|______
        // (4)   |   (1)
        //       |
        // canvas is divided into four areas by anchorPoint
        // try to figure out which area could fit desiredSize from area1 to area4, if there's no fit, default to area4.
        internal static AdornerLocation FindAnchor(Point anchorPoint, Size desiredSize, Rect canvas)
        {
            Fx.Assert(anchorPoint.X >= canvas.X && anchorPoint.X <= canvas.X + canvas.Width, "X axis of anchorPoint not within canvas");
            Fx.Assert(anchorPoint.Y >= canvas.Y && anchorPoint.Y <= canvas.Y + canvas.Height, "Y axis of anchorPoint not within canvas");
 
            AdornerLocation anchor = AdornerLocation.None;
 
            // try area 1 and 2
            if (anchorPoint.X + desiredSize.Width <= canvas.X + canvas.Width)
            {
                // area 1
                if (anchorPoint.Y + desiredSize.Height <= canvas.Y + canvas.Height)
                {
                    anchor = AdornerLocation.BottomRight;
                }
 
                // area 2
                if (anchor == AdornerLocation.None && anchorPoint.Y - desiredSize.Height >= canvas.Y)
                {
                    anchor = AdornerLocation.TopRight;
                }
            }
 
            // area 3
            if (anchor == AdornerLocation.None && anchorPoint.X - desiredSize.Width >= canvas.X && anchorPoint.Y - desiredSize.Height >= canvas.Y)
            {
                anchor = AdornerLocation.TopLeft;
            }
 
            // default to area 4
            if (anchor == AdornerLocation.None)
            {
                anchor = AdornerLocation.BottomLeft;
            }
 
            return anchor;
        }
 
        protected override Visual GetVisualChild(int index)
        {
            return this.Content;
        }
 
        protected override Size MeasureOverride(Size constraint)
        {
            this.content.Measure(constraint);
            return this.content.DesiredSize;
        }
 
        protected override Size ArrangeOverride(Size finalSize)
        {
            Point anchorPoint = this.AdornedElement.TranslatePoint(new Point(this.AdornedElement.RenderSize.Width, 0), this.ScrollViewer);
 
            AdornerLocation anchor = AnnotationAdorner.GetAnchor(this);
            if (anchor == AdornerLocation.None)
            {
                // Calculate based on the real size of the adorner, depending on current zoom level
                DesignerView designerView = ((WorkflowViewElement)AdornedElement).Context.Services.GetService<DesignerView>();
                double zoomLevel = designerView.ZoomFactor;
                Size adornerSize = new Size(this.content.DesiredSize.Width * zoomLevel, this.content.DesiredSize.Height * zoomLevel);
                anchor = FindAnchor(anchorPoint, adornerSize, new Rect(0, 0, this.ScrollViewer.ViewportWidth, this.ScrollViewer.ViewportHeight));
                AnnotationAdorner.SetAnchor(this, anchor);
            }
 
            Point location = CalculateLocation(anchor, this.AdornedElement.RenderSize, this.content.DesiredSize);
 
            this.content.Arrange(new Rect(location, finalSize));
 
            return finalSize;
        }
 
        private static Point CalculateLocation(AdornerLocation anchor, Size adorneeSize, Size adornerSize)
        {
            Point location = new Point();
 
            switch (anchor)
            {
                case AdornerLocation.BottomRight:
                    location = new Point(adorneeSize.Width, 0);
                    break;
                case AdornerLocation.TopRight:
                    location = new Point(adorneeSize.Width, -adornerSize.Height);
                    break;
                case AdornerLocation.TopLeft:
                    location = new Point(adorneeSize.Width - adornerSize.Width, -adornerSize.Height);
                    break;
                case AdornerLocation.BottomLeft:
                    location = new Point(adorneeSize.Width - adornerSize.Width, 20);
                    break;
            }
 
            return location;
        }
    }
}