Search code examples
c#wpfadornerlayer

Resize more than one different shape at same time using .net adorner


I am developing a desktop application in WPF, that contains different types of shapes (like circle, radius circle, diameter circle). Now I need to resize shapes on demand so I used .Net adorner which provides the flexibility to drag and resize the shapes. The exact issue is that I want to resize two elements at same time (i.e *When I resize the circle, the radius Line should also resize with respect to the radius start and end points).

Note I haven't tried anything (I have not done any development yet, so I have not code).

Updated Trial of your code. This is a Diameter Circle So when I am drag it it will drag only the ellipse

enter image description here

public class SimpleCircleAdorner : Adorner
    {
        // Be sure to call the base class constructor.
        public SimpleCircleAdorner(UIElement adornedElement, Panel ownerPanel)
            : base(adornedElement)
        {
            _ownerPanel = ownerPanel;
        }

        protected override void OnMouseEnter(MouseEventArgs e)
        {
            Point point = Mouse.GetPosition(AdornedElement);
            _currentPosition = getMousePosition(point);
            switch (_currentPosition)
            {
                case MousePosition.BR:
                case MousePosition.TL:
                    Cursor = Cursors.SizeNWSE;
                    break;
                case MousePosition.BL:
                case MousePosition.TR:
                    Cursor = Cursors.SizeNESW;
                    break;
            }
        }

        protected override void OnMouseLeave(MouseEventArgs e)
        {
            AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement);
            if (adornerLayer != null)
            {
                Adorner[] adorners = adornerLayer.GetAdorners(AdornedElement);
                if (adorners != null)
                {
                    foreach (Adorner adorner in adorners)
                    {
                        adornerLayer.Remove(adorner);
                    }
                }
            }
        }

        MousePosition _currentPosition;
        Panel _ownerPanel;
        bool _isDraging = false;
        Point _startPosition;

        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (Mouse.Capture(this))
            {
                _isDraging = true;
                _startPosition = Mouse.GetPosition(_ownerPanel);
            }
        }

        protected override void OnPreviewMouseMove(MouseEventArgs e)
        {
            if (_isDraging)
            {
                Point newPosition = Mouse.GetPosition(_ownerPanel);
                double diffX = (newPosition.X - _startPosition.X);
                double diffY = (newPosition.Y - _startPosition.Y);

                // we should decide whether to change Width and Height or to change Canvas.Left and Canvas.Right
                if (Math.Abs(diffX) >= 1 || Math.Abs(diffY) >= 1)
                {
                    switch (_currentPosition)
                    {
                        case MousePosition.TL:
                        case MousePosition.BL:
                            foreach (FrameworkElement ui in _ownerPanel.Children)
                            {
                                if (ui.GetType() == typeof(Ellipse) || ui.GetType() == typeof(Line))
                                {
                                    Canvas.SetLeft(ui, Math.Max(0, Canvas.GetLeft(ui) + diffX));
                                    ui.Width = Math.Max(0, ui.Width - diffX);
                                }
                            }
                            _ownerPanel.InvalidateArrange();

                            break;
                        case MousePosition.BR:
                        case MousePosition.TR:

                            foreach (FrameworkElement ui in _ownerPanel.Children)
                            {
                                if (ui.GetType() == typeof(Ellipse) || ui.GetType() == typeof(Line))
                                {
                                    ui.Width = Math.Max(0, ui.Width + diffX);
                                }
                            }
                            break;
                    }


                    switch (_currentPosition)
                    {
                        case MousePosition.TL:
                        case MousePosition.TR:
                            foreach (FrameworkElement ui in _ownerPanel.Children)
                            {
                                if (ui.GetType() == typeof(Ellipse) || ui.GetType() == typeof(Line))
                                {
                                    Canvas.SetTop(ui, Math.Max(0, Canvas.GetTop(ui) + diffY));
                                }
                            }
                            foreach (FrameworkElement ui in _ownerPanel.Children)
                            {
                                if (ui.GetType() == typeof(Ellipse) || ui.GetType() == typeof(Line))
                                {
                                    ui.Height = Math.Max(0, ui.Height - diffY);
                                }
                            }
                            break;
                        case MousePosition.BL:
                        case MousePosition.BR:
                            foreach (FrameworkElement ui in _ownerPanel.Children)
                            {
                                if (ui.GetType() == typeof(Ellipse) || ui.GetType() == typeof(Line))
                                {
                                    ui.Height = Math.Max(0, ui.Height + diffY);
                                }
                            }
                            break;

                    }
                }
                _startPosition = newPosition;
            }
        }

        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {

        }

        protected override void OnPreviewMouseRightButtonUp(MouseButtonEventArgs e)
        {
            if (_isDraging)
            {
                Mouse.Capture(null);
                _isDraging = false;
            }
        }

        MousePosition getMousePosition(Point point) // point relative to element
        {
            double h2 = ActualHeight / 2;
            double w2 = ActualWidth / 2;
            if (point.X < w2 && point.Y < h2)
                return MousePosition.TL;
            else if (point.X > w2 && point.Y > h2)
                return MousePosition.BR;
            else if (point.X > w2 && point.Y < h2)
                return MousePosition.TR;
            else
                return MousePosition.BL;



        }

        enum MousePosition
        {
            TL,
            TR,
            BL,
            BR
        }

        double _renderRadius = 5.0;
        protected override void OnRender(DrawingContext drawingContext)
        {
            Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);
            SolidColorBrush renderBrush = new SolidColorBrush(Colors.Black);
            renderBrush.Opacity = 0.3;
            Pen renderPen = new Pen(new SolidColorBrush(Colors.Black), 1.5);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, _renderRadius, _renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, _renderRadius, _renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, _renderRadius, _renderRadius);
            drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, _renderRadius, _renderRadius);
        }
    }

Solution

  • I did following for Diameter circle and Radius circle to solve my issue.

    public class ResizingAdorner : Adorner
        {
            // Resizing adorner uses Thumbs for visual elements.  
            // The Thumbs have built-in mouse input handling.
    
            System.Windows.Controls.Primitives.Thumb topLeft, topRight, bottomLeft, bottomRight, Left, Right, RightCenter, Center;
    
            string m_strelement_prefix = string.Empty;
            List<UIElement> multiObject = new List<UIElement>();
            List<Point> contextData = new List<Point>();
            Panel _ownerPanel;
    
            // To store and manage the adorner's visual children.
            VisualCollection visualChildren;
    
            public ResizingAdorner(UIElement adornedElement, Panel ownerPanel)
                : base(adornedElement)
            {
                _ownerPanel = ownerPanel;
                visualChildren = new VisualCollection(this);
    
                m_strelement_prefix = (adornedElement.Uid != "") ? adornedElement.Uid.Substring(0, 2) : string.Empty;
                if (m_strelement_prefix == string.Empty) { return; }
    
                switch (m_strelement_prefix)
                {
                    case "DC":
                        if (adornedElement.GetType() == typeof(Line) || adornedElement.GetType() == typeof(Ellipse))
                        {
                            BuildAdornerCorner(ref Left, Cursors.Hand);
                            BuildAdornerCorner(ref Right, Cursors.Hand);
                            Left.DragDelta += new DragDeltaEventHandler(onDragDeltaLeft);
                            Right.DragDelta += new DragDeltaEventHandler(onDragDeltaRight);
    
                            foreach (UIElement ui in _ownerPanel.Children)
                                if (ui.Uid.Contains(m_strelement_prefix)) { multiObject.Add(ui); }
                        }
                        break;
                    case "RC":
                        if (adornedElement.GetType() == typeof(Line) || adornedElement.GetType() == typeof(Ellipse))
                        {
                            BuildAdornerCorner(ref Right, Cursors.Hand);
                            Right.DragDelta += new DragDeltaEventHandler(onDragDeltaRight);
                            foreach (UIElement ui in _ownerPanel.Children)
                                if (ui.Uid.Contains(m_strelement_prefix)) { multiObject.Add(ui); }
                        }
                        break;
                    case "TC":
                        break;
    
                }
            }
    
            void onDragDeltaLeft(object sender, DragDeltaEventArgs args)
            {
                FrameworkElement adornedElement = AdornedElement as FrameworkElement;
                System.Windows.Controls.Primitives.Thumb hitThumb = sender as System.Windows.Controls.Primitives.Thumb;
                if (adornedElement == null || hitThumb == null) return;
                Point position = Mouse.GetPosition(this);
    
                double distance = 0;
                Point _startPoint = new Point();
    
                switch (m_strelement_prefix)
                {
                    #region Diameter Circle
                    case "DC":  
                    foreach (UIElement ui in multiObject)
                    {
                        if (ui.GetType() != AdornedElement.GetType())
                        {
                            switch (ui.GetType().ToString())
                            {
                                //Selected Element is Ellipse
                                case "System.Windows.Shapes.Line":
                                    _startPoint = new Point();
                                    distance = getMidPoint(new Point(((Line)ui).X2, ((Line)ui).Y2), position, out _startPoint, true);
    
                                    ((Line)ui).X2 = position.X;
                                    ((Line)ui).Y2 = position.Y;
    
                                    ((Ellipse)adornedElement).Height = ((Ellipse)adornedElement).Width = distance * 2;
                                    Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                    Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
    
                                    break;
                                //Selected Element is Line
                                case "System.Windows.Shapes.Ellipse":
                                    _startPoint = new Point();
                                    distance = getMidPoint(new Point(((Line)adornedElement).X2, ((Line)adornedElement).Y2), position, out _startPoint, true);
    
                                    ((Line)adornedElement).X2 = position.X;
                                    ((Line)adornedElement).Y2 = position.Y;
    
                                    ((Ellipse)ui).Height = ((Ellipse)ui).Width = distance * 2;
                                    Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                    Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
                                    break;
                            }
                        }
                    }
                    break;
                    #endregion 
                }
            }
    
            void onDragDeltaRight(object sender, DragDeltaEventArgs args)
            {
                FrameworkElement adornedElement = AdornedElement as FrameworkElement;
                System.Windows.Controls.Primitives.Thumb hitThumb = sender as System.Windows.Controls.Primitives.Thumb;
                if (adornedElement == null || hitThumb == null) return;
    
                Point position = Mouse.GetPosition(this);
                double distance = 0;
                Point _startPoint = new Point();
    
                switch (m_strelement_prefix)
                {
                    #region Diameter Circle
                    case "DC":
                        foreach (UIElement ui in multiObject)
                        {
                            if (ui.GetType() != AdornedElement.GetType())
                            {
                                switch (ui.GetType().ToString())
                                {
                                    //Selected Element is Ellipse
                                    case "System.Windows.Shapes.Line":
                                        _startPoint = new Point();
                                        distance = getMidPoint(new Point(((Line)ui).X1, ((Line)ui).Y1), position, out _startPoint, true);
    
                                        ((Line)ui).X2 = position.X;
                                        ((Line)ui).Y2 = position.Y;
    
                                        ((Ellipse)adornedElement).Height = ((Ellipse)adornedElement).Width = distance * 2;
                                        Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                        Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
    
                                        break;
                                    //Selected Element is Line
                                    case "System.Windows.Shapes.Ellipse":
                                        _startPoint = new Point();
                                        distance = getMidPoint(new Point(((Line)adornedElement).X1, ((Line)adornedElement).Y1), position, out _startPoint, true);
    
                                        ((Line)adornedElement).X2 = position.X;
                                        ((Line)adornedElement).Y2 = position.Y;
    
                                        ((Ellipse)ui).Height = ((Ellipse)ui).Width = distance * 2;
                                        Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                        Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
                                        break;
                                }
                            }
                        }
                        break;
                    #endregion
    
                    #region Radius Circle
                    case "RC":
                        foreach (UIElement ui in multiObject)
                        {
                            if (ui.GetType() != AdornedElement.GetType())
                            {
                                switch (ui.GetType().ToString())
                                {
                                    //Selected Element is Ellipse
                                    case "System.Windows.Shapes.Line":
                                        _startPoint = new Point();
    
                                        distance = getMidPoint(new Point(((Line)ui).X1, ((Line)ui).Y1), position, out _startPoint, false);
    
                                        ((Line)ui).X2 = position.X;
                                        ((Line)ui).Y2 = position.Y;
    
                                        ((Ellipse)adornedElement).Height = ((Ellipse)adornedElement).Width = distance * 2;
                                        Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                        Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
    
                                        break;
                                    //Selected Element is Line
                                    case "System.Windows.Shapes.Ellipse":
                                        _startPoint = new Point();
                                        distance = getMidPoint(new Point(((Line)adornedElement).X1, ((Line)adornedElement).Y1), position, out _startPoint, false);
    
                                        ((Line)adornedElement).X2 = position.X;
                                        ((Line)adornedElement).Y2 = position.Y;
    
                                        ((Ellipse)ui).Height = ((Ellipse)ui).Width = distance * 2;
                                        Canvas.SetLeft(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Left);
                                        Canvas.SetTop(ui, (new Thickness((_startPoint.X - ((distance * 2) / 2)), (_startPoint.Y - ((distance * 2) / 2)), 0, 0)).Top);
                                        break;
                                }
                            }
                        }
                        break;
                    #endregion 
                }
            }
    
            private static double getMidPoint(Point _start,Point _end,out Point _middle , bool findMidPoint)
            {
                _middle = (findMidPoint) ? new Point(((_start.X + _end.X) / 2), ((_start.Y + _end.Y) / 2)) : _start;
                return getDistanceBetweenTwoPoints(_middle, _end);
            }
    
            private static double getDistanceBetweenTwoPoints(Point StartPoint, Point EndPoint)
            {
                return Math.Round(Math.Sqrt(Math.Pow((EndPoint.X - StartPoint.X), 2) + Math.Pow((EndPoint.Y - StartPoint.Y), 2)), 2);
            }
            // Arrange the Adorners.
            protected override Size ArrangeOverride(Size finalSize)
            {
    
                // desiredWidth and desiredHeight are the width and height of the element that's being adorned.  
                // These will be used to place the ResizingAdorner at the corners of the adorned element.  
                double desiredWidth = AdornedElement.DesiredSize.Width;
                double desiredHeight = AdornedElement.DesiredSize.Height;
    
                // adornerWidth & adornerHeight are used for placement as well.
                double adornerWidth = this.DesiredSize.Width;
                double adornerHeight = this.DesiredSize.Height;
    
                switch (m_strelement_prefix)
                {
                    case "DC":
                        if (AdornedElement.GetType() == typeof(Line) || AdornedElement.GetType() == typeof(Ellipse))
                        {
                            if (AdornedElement.GetType() == typeof(Ellipse)) { contextData = ((List<Point>)((AdornedElement as FrameworkElement).DataContext)); }
    
                            Line selectedLine = ((AdornedElement.GetType() == typeof(Ellipse)) && contextData.Count != 0)
                                ? new Line() { X1 = contextData[0].X, Y1 = contextData[0].Y, X2 = contextData[1].X, Y2 = contextData[1].Y }
                                : (AdornedElement as Line);
    
                            double left = Math.Min(selectedLine.X1, selectedLine.X2);
                            double top = Math.Min(selectedLine.Y1, selectedLine.Y2);
                            var startRect = new Rect(selectedLine.X1 - (Left.Width / 2), selectedLine.Y1 - (Left.Width / 2), Left.Width, Left.Height);
                            var endRect = new Rect(selectedLine.X2 - (Right.Width / 2), selectedLine.Y2 - (Right.Height / 2), Right.Width, Right.Height);
                            Left.Arrange(startRect);
                            Right.Arrange(endRect);
                        }
                        break;
                    case "RC":
                        if (AdornedElement.GetType() == typeof(Line) || AdornedElement.GetType() == typeof(Ellipse))
                        {
                            if (AdornedElement.GetType() == typeof(Ellipse)) { contextData = ((List<Point>)((AdornedElement as FrameworkElement).DataContext)); }
    
                            Line selectedLine = ((AdornedElement.GetType() == typeof(Ellipse)) && contextData.Count != 0)
                                ? new Line() { X1 = contextData[0].X, Y1 = contextData[0].Y, X2 = contextData[1].X, Y2 = contextData[1].Y }
                                : (AdornedElement as Line);
    
                            double top = Math.Min(selectedLine.Y1, selectedLine.Y2);
                            var endRect = new Rect(selectedLine.X2 - (Right.Width / 2), selectedLine.Y2 - (Right.Height / 2), Right.Width, Right.Height);
                            Right.Arrange(endRect);
                        }
                        break;
                }
                return finalSize;
            }
    
            // Helper method to instantiate the corner Thumbs, set the Cursor property, 
            // set some appearance properties, and add the elements to the visual tree.
            void BuildAdornerCorner(ref System.Windows.Controls.Primitives.Thumb cornerThumb, Cursor customizedCursor)
            {
                if (cornerThumb != null) return;
    
                cornerThumb = new System.Windows.Controls.Primitives.Thumb();
    
                // Set some arbitrary visual characteristics.
                cornerThumb.Cursor = customizedCursor;
                cornerThumb.Height = cornerThumb.Width = 10;
                cornerThumb.Background = new SolidColorBrush(Colors.Black);
    
                visualChildren.Add(cornerThumb);
            }
    
            // This method ensures that the Widths and Heights are initialized.  Sizing to content produces
            // Width and Height values of Double.NaN.  Because this Adorner explicitly resizes, the Width and Height
            // need to be set first.  It also sets the maximum size of the adorned element.
            void EnforceSize(FrameworkElement adornedElement)
            {
                if (adornedElement.Width.Equals(Double.NaN))
                    adornedElement.Width = adornedElement.DesiredSize.Width;
                if (adornedElement.Height.Equals(Double.NaN))
                    adornedElement.Height = adornedElement.DesiredSize.Height;
    
                FrameworkElement parent = adornedElement.Parent as FrameworkElement;
                if (parent != null)
                {
                    adornedElement.MaxHeight = parent.ActualHeight;
                    adornedElement.MaxWidth = parent.ActualWidth;
                }
            }
    
            // Override the VisualChildrenCount and GetVisualChild properties to interface with 
            // the adorner's visual collection.
            protected override int VisualChildrenCount { get { return visualChildren.Count; } }
            protected override Visual GetVisualChild(int index) { return visualChildren[index]; }
    
        }    
    

    enter image description here

    enter image description here