Click here to Skip to main content
15,884,472 members
Articles / Programming Languages / C#

Moveable Resizable Objects

Rate me:
Please Sign up or sign in to vote.
4.98/5 (59 votes)
9 Oct 2009CPOL198 min read 125.1K   8.7K   178  
Here is a description of an extremely powerful mechanism that makes screen objects moveable and resizable.
using System;
using System .Collections .Generic;
using System .Drawing;
using System .Drawing .Drawing2D;
using System .Windows .Forms;

using MoveGraphLibrary;

namespace Test_MoveGraphLibrary
{
    public partial class Form_NNodeContours : Form
    {
        Mover mover;
        bool bShowContours = false;
        MRCircle mrcircle;
        MRRectangle mrrect;
        PolyRectangle polyrect;
        NRing ring;

        int cyShiftComments, cxShiftComments_R;
        string [] Cmnts = new string [] { "L_Press anywhere inside ", "- move",    
                                          "L_Press at any border ",   "- resize",
                                          "NO rotation in this form!", " " };
        // -------------------------------------------------
        public Form_NNodeContours ()
        {
            InitializeComponent ();

            Auxi_Common .TwoColumnPanelComments (this, panelInfo, Font, Cmnts, out cxShiftComments_R, out cyShiftComments);

            mover = new Mover (this);
            int nW = ClientSize .Width;
            int nH = ClientSize .Height;

            mrcircle = new MRCircle (new Point (nW / 6, nH / 4), Convert .ToInt32 (Math .Min (nW / 6, nH / 4) * 0.8), Color .Yellow);
            mover .Add (mrcircle);
            mrrect = new MRRectangle (new Rectangle (nW / 9, nH / 2, nW / 4, nH * 5 / 16), Color .Cyan);
            mover .Insert (0, mrrect);
            polyrect = new PolyRectangle (new Rectangle (nW * 7 / 12, nH * 7 / 10, nW / 4, nH / 4));
            polyrect .Color = Color .LightGreen;
            mover .Insert (0, polyrect);
            ring = new NRing (new Point (nW * 2 / 3, nH * 3 / 10), nH * 2 / 15, nH / 3, new double [] { 1, 5, 3, 10, 7 });
            mover .Insert (0, ring);

            mover .Insert (0, buttonContours);
            mover .Insert (0, panelInfo);
        }
        // -------------------------------------------------        Click_btnContours
        private void Click_btnContours (object sender, EventArgs e)
        {
            bShowContours = !bShowContours;
            Invalidate ();
        }
        // -------------------------------------------------        OnPaint
        private void OnPaint (object sender, PaintEventArgs e)
        {
            Graphics grfx = e .Graphics;

            mrcircle .Draw (grfx);
            mrrect .Draw (grfx);
            polyrect .Draw (grfx);
            ring .Draw (grfx);
            if (bShowContours)
            {
                for (int i = mover .Count - 1; i >= 0; i--)
                {
                    mover [i] .DrawCover (grfx);
                }
            }
        }
        // -------------------------------------------------        OnMouseDown
        private void OnMouseDown (object sender, MouseEventArgs mea)
        {
            if (mea .Button == MouseButtons .Left)
            {
                mover .Catch (mea .Location);
            }
        }
        // -------------------------------------------------        OnMouseUp
        private void OnMouseUp (object sender, MouseEventArgs mea)
        {
            if (mover .Release ())
            {
                if (mover [mover .WasCaughtObject] .Source is MRRectangle)
                {
                    (mover [mover .WasCaughtObject] .Source as MRRectangle) .RedefineCover ();
                    Invalidate ();
                }
                else if (mover [mover .WasCaughtObject] .Source is NRing)
                {
                    (mover [mover .WasCaughtObject] .Source as NRing) .RedefineCover ();
                    Invalidate ();
                }
            }
        }
        // -------------------------------------------------        OnMouseMove
        private void OnMouseMove (object sender, MouseEventArgs mea)
        {
            if (mover .Move (mea .Location))
            {
                Invalidate ();
            }
        }
        // -------------------------------------------------        OnPaint_panelComments
        private void OnPaint_panelComments (object sender, PaintEventArgs e)
        {
            Graphics grfx = e .Graphics;
            SolidBrush brush = new SolidBrush (Color .Black);
            int nLines = Cmnts .Length / 2;
            for (int i = 0; i < nLines; i++)
            {
                grfx .DrawString (Cmnts [i * 2], Font, brush, 0, i * cyShiftComments);
                grfx .DrawString (Cmnts [i * 2 + 1], Font, brush, cxShiftComments_R, i * cyShiftComments);
            }
        }
    }
    
    // ******************************************
    //
    public class MRCircle : GraphicalObject
    {
        Point ptCenter;
        int radius;
        Color clr = Color .Blue;
        int nrSmall = 5;
        int distanceNeighbours = 9;
        int nMinRadius = 25;   // min size is set to avoid disappearence while resizing

        bool bShowRadius;
        Color clrText;
        Font font;
        double angle = 0.0;

        // -------------------------------------------------
        public MRCircle (Point pt, int rad, Color color)
        {
            ptCenter = pt;
            radius = rad;
            clr = color;

            bShowRadius = false;
            clrText = Color .Black;
            font = new Font ("Microsoft Sans Serif", (float) 7.8);
        }
        // -------------------------------------------------        Center
        public Point Center
        {
            get { return (ptCenter); }
        }
        // -------------------------------------------------        Radius
        public int Radius
        {
            get { return (radius); }
        }
        // -------------------------------------------------        Color
        public Color Color
        {
            get { return (clr); }
            set { clr = value; }
        }
        // -------------------------------------------------        ShowRadius
        public bool ShowRadius
        {
            get { return (bShowRadius); }
            set { bShowRadius = value; }
        }
        // -------------------------------------------------        TextColor
        public Color TextColor
        {
            get { return (clrText); }
            set { clrText = value; }
        }
        // -------------------------------------------------        Font
        public Font Font
        {
            get { return (font); }
            set
            {
                if (font != null)
                {
                    font = value;
                }
            }
        }
        // -------------------------------------------------        Angle
        public double Angle
        {
            get { return (angle); }
            set { angle = value; }
        }
        // -------------------------------------------------        IsInside
        public bool IsInside (Point pt)
        {
            return (Auxi_Geometry .Distance (ptCenter, pt) <= radius);
        }
        // -------------------------------------------------        Draw
        public void Draw (Graphics grfx)
        {
            Auxi_Drawing .FillEllipse (grfx, clr, ptCenter, radius);
            Auxi_Drawing .DrawEllipse (grfx, Color .DarkGray, ptCenter, radius);
            if (bShowRadius)
            {
                Auxi_Drawing .RotatedText (grfx, radius .ToString (), font, angle, TextColor, ptCenter, TextBasicPoint .M);
            }
        }
        // -------------------------------------------------        DefineCover
        public override void DefineCover ()
        {
            int nOnPerimeter = Convert .ToInt32 ((2 * Math .PI * radius) / distanceNeighbours);
            CoverNode [] shreds = new CoverNode [nOnPerimeter + 1];
            shreds [0] = new CoverNode (0, ptCenter, radius - nrSmall + 1, Cursors .SizeAll);
            for (int i = 1; i <= nOnPerimeter; i++)
            {
                shreds [i] = new CoverNode (i, Auxi_Geometry .PointToPoint (ptCenter, 2 * Math .PI * (i - 1) / nOnPerimeter, radius), nrSmall);
                shreds [i] .Clearance = false;
            }
            cover = new Cover (shreds);
        }
        // -------------------------------------------------
        public override void Move (int cx, int cy)
        {
            ptCenter += new Size (cx, cy);
        }
        // -------------------------------------------------        MoveNode
        public override bool MoveNode (int i, int cx, int cy, Point ptM, MouseButtons catcher)
        {
            bool bRet = false;

            if (catcher == MouseButtons .Left)
            {
                if (i == 0)
                {
                    Move (cx, cy);
                }
                else
                {
                    int nRadNew = Convert .ToInt32 (Auxi_Geometry .Distance (ptCenter, ptM));
                    if (nRadNew != radius && nRadNew >= nMinRadius)
                    {
                        radius = nRadNew;
                        bRet = true;
                    }
                }
            }
            return (bRet);
        }
    }
    
    // ******************************************
    //
    public class MRRectangle : GraphicalObject
    {
        Rectangle rect;
        Color color = Color .Blue;
        Brush brush;
        int halfNode = 3;
        int sideNode;
        int nMinRectSide = 25;   // min size is set to avoid disappearence while resizing
        int nNodesOnSide;
        int nNodesOnTop;
        int iFirstOnLeft;
        int iFirstOnRight;
        int iFirstOnTop;
        int iFirstOnBottom;

        // -------------------------------------------------
        public MRRectangle (Rectangle rc, Color clr)
        {
            if (rc .IsEmpty)
            {
                rect = new Rectangle (100, 100, 200, 120);
            }
            else
            {
                rc .Width = Math .Max (nMinRectSide, rc .Width);
                rc .Height = Math .Max (nMinRectSide, rc .Height);
                rect = rc;
            }
            sideNode = 2 * halfNode;
            NodesOnSides ();
            color = clr;
            brush = new SolidBrush (Color);
        }
        // -------------------------------------------------        NodesOnSides
        private void NodesOnSides ()
        {
            nNodesOnSide = (rect .Height - sideNode) / sideNode;
            if (rect .Height % sideNode != 0)
            {
                nNodesOnSide++;
            }
            nNodesOnTop = (rect .Width - sideNode) / sideNode;
            if (rect .Width % sideNode != 0)
            {
                nNodesOnTop++;
            }
            iFirstOnLeft = 6;
            iFirstOnRight = iFirstOnLeft + nNodesOnSide;
            iFirstOnTop = iFirstOnRight + nNodesOnSide;
            iFirstOnBottom = iFirstOnTop + nNodesOnTop;
        }
        // -------------------------------------------------        Rectangle
        public Rectangle Rectangle
        {
            get { return (rect); }
        }
        // -------------------------------------------------        Color
        public Color Color
        {
            get { return (color); }
            set
            {
                color = value;
                brush = new SolidBrush (Color);
            }
        }
        // -------------------------------------------------        Draw
        public void Draw (Graphics grfx)
        {
            grfx .FillRectangle (brush, rect);
        }
        // -------------------------------------------------        DefineCover
        public void RedefineCover ()
        {
            NodesOnSides ();
            DefineCover ();
        }
        // -------------------------------------------------        DefineCover
        // order of nodes:  2 big inside,
        //                  4 on corners (LT, RT, RB, LB),
        //                  2 * nNodesOnSide on sides (left, right)
        //                  2 * nNodesOnTop on sides (top, bottom)
        // nodes  0, 1 - cover the ends of the inside area; the only nodes that are connected
        //
        public override void DefineCover ()
        {
            Rectangle rcInner = new Rectangle (rect .Left + halfNode, rect .Top + halfNode, rect .Width - sideNode, rect .Height - sideNode);
            int nBigNodeSide;
            Point [] ptM = Auxi_Geometry .TwoSquaresOnRectangle (rcInner, out nBigNodeSide);
            CoverNode [] nodes = new CoverNode [2 + 4 + 2 * nNodesOnSide + 2 * nNodesOnTop];
            int halfBig = nBigNodeSide / 2; ;
            nodes [0] = new CoverNode (0, new PointF [] {new PointF (ptM [0] .X - halfBig, ptM [0] .Y - halfBig), 
                                                         new PointF (ptM [0] .X + halfBig, ptM [0] .Y - halfBig), 
                                                         new PointF (ptM [0] .X + halfBig, ptM [0] .Y + halfBig), 
                                                         new PointF (ptM [0] .X - halfBig, ptM [0] .Y + halfBig) });
            nodes [1] = new CoverNode (1, new PointF [] {new PointF (ptM [1] .X - halfBig, ptM [1] .Y - halfBig), 
                                                         new PointF (ptM [1] .X + halfBig, ptM [1] .Y - halfBig), 
                                                         new PointF (ptM [1] .X + halfBig, ptM [1] .Y + halfBig), 
                                                         new PointF (ptM [1] .X - halfBig, ptM [1] .Y + halfBig) });
            nodes [2] = new CoverNode (2, new PointF (rect .Left, rect .Top), 5, Cursors .SizeNWSE);
            nodes [3] = new CoverNode (3, new PointF (rect .Right, rect .Top), 5, Cursors .SizeNESW);
            nodes [4] = new CoverNode (4, new PointF (rect .Right, rect .Bottom), 5, Cursors .SizeNWSE);
            nodes [5] = new CoverNode (5, new PointF (rect .Left, rect .Bottom), 5, Cursors .SizeNESW);

            int cyMax = rect .Bottom - (halfNode + 1);
            for (int i = 0; i < nNodesOnSide; i++)
            {
                nodes [i + iFirstOnLeft] = new CoverNode (i + iFirstOnLeft,
                                                          new PointF (rect .Left, Math .Min (rect .Top + sideNode * (i + 1), cyMax)),
                                                          MovementFreedom .WE, Cursors .SizeWE);
            }
            for (int i = 0; i < nNodesOnSide; i++)
            {
                nodes [i + iFirstOnRight] = new CoverNode (i + iFirstOnRight,
                                                           new PointF (rect .Right, Math .Min (rect .Top + sideNode * (i + 1), cyMax)),
                                                           MovementFreedom .WE, Cursors .SizeWE);
            }
            int cxMax = rect .Right - (halfNode + 1);
            for (int i = 0; i < nNodesOnTop; i++)
            {
                nodes [i + iFirstOnTop] = new CoverNode (i + iFirstOnTop,
                                                         new PointF (Math .Min (rect .Left + sideNode * (i + 1), cxMax), rect .Top),
                                                         MovementFreedom .NS, Cursors .SizeNS);
            }
            for (int i = 0; i < nNodesOnTop; i++)
            {
                nodes [i + iFirstOnBottom] = new CoverNode (i + iFirstOnBottom,
                                                            new PointF (Math .Min (rect .Left + sideNode * (i + 1), cxMax), rect .Bottom),
                                                            MovementFreedom .NS, Cursors .SizeNS);
            }
            for (int i = 0; i < nodes .Length; i++)
            {
                nodes [i] .Clearance = false;
            }
            cover = new Cover (nodes);
        }
        // -------------------------------------------------
        public override void Move (int cx, int cy)
        {
            rect .X += cx;
            rect .Y += cy;
        }
        // -------------------------------------------------        MoveNode
        public override bool MoveNode (int i, int cx, int cy, Point ptM, MouseButtons catcher)
        {
            bool bRet = false;

            if (catcher == MouseButtons .Left)
            {
                if (i == 0 || i == 1)
                {
                    Move (cx, cy);
                }
                else if (i == 2)        //LT corner
                {
                    if (rect .Height - cy >= nMinRectSide)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                    if (rect .Width - cx >= nMinRectSide)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
                else if (i == 3)        // RT corner
                {
                    if (rect .Height - cy >= nMinRectSide)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                    if (rect .Width + cx >= nMinRectSide)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                }
                else if (i == 4)        // RB corner
                {
                    if (rect .Width + cx >= nMinRectSide)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                    if (rect .Height + cy >= nMinRectSide)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                }
                else if (i == 5)        // LB corner
                {
                    if (rect .Height + cy >= nMinRectSide)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                    if (rect .Width - cx >= nMinRectSide)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
                else if (i >= iFirstOnBottom)   // on bottom
                {
                    if (rect .Height + cy >= nMinRectSide)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                }
                else if (i >= iFirstOnTop)      // on top
                {
                    if (rect .Height - cy >= nMinRectSide)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                }
                else if (i >= iFirstOnRight)   // on right side
                {
                    if (rect .Width + cx >= nMinRectSide)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                }
                else if (i >= iFirstOnLeft)     // on left side
                {
                    if (rect .Width - cx >= nMinRectSide)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
            }
            return (bRet);
        }
        // -------------------------------------------------        MoveBorder_Top
        private void MoveBorder_Top (int cy)
        {
            rect .Y += cy;
            rect .Height -= cy;
        }
        // -------------------------------------------------        MoveBorder_Bottom
        private void MoveBorder_Bottom (int cy)
        {
            rect .Height += cy;
        }
        // -------------------------------------------------        MoveBorder_Left
        private void MoveBorder_Left (int cx)
        {
            rect .X += cx;
            rect .Width -= cx;
        }
        // -------------------------------------------------        MoveBorder_Right
        private void MoveBorder_Right (int cx)
        {
            rect .Width += cx;
        }
    }

    // ******************************************
    //
    public class PolyRectangle : GraphicalObject
    {
        Rectangle rect;
        Color color = Color .LightGreen;
        Brush brush;
        int minwidth = 35;      // to avoid disappearence while resizing
        int minheight = 25;     // to avoid disappearence while resizing

        // -------------------------------------------------
        public PolyRectangle (Rectangle rc)
        {
            if (rc .IsEmpty)
            {
                rect = new Rectangle (200, 200, 160, 110);
            }
            else
            {
                rc .Width = Math .Max (minwidth, rc .Width);
                rc .Height = Math .Max (minheight, rc .Height);
                rect = rc;
            }
            brush = new SolidBrush (Color);
        }
        // -------------------------------------------------        Rectangle
        public Rectangle Rectangle
        {
            get { return (rect); }
        }
        // -------------------------------------------------        Color
        public Color Color
        {
            get { return (color); }
            set
            {
                color = value;
                brush = new SolidBrush (Color);
            }
        }
        // -------------------------------------------------        Draw
        public void Draw (Graphics grfx)
        {
            grfx .FillRectangle (brush, rect);
        }
        // -------------------------------------------------        DefineCover
        // order of nodes:  4 on corners (LT, RT, RB, LB),
        //                  4 polygon nodes on sides (left, top, right, bottom)
        //                  1 polygon (rectangular) node inside,
        //                  
        public override void DefineCover ()
        {
            int smallhalf = 3;
            int cornerradius = 6;
            CoverNode [] nodes = new CoverNode [4 + 4 + 1];
            nodes [0] = new CoverNode (0, new PointF (rect .Left, rect .Top), cornerradius, Cursors .SizeNWSE);
            nodes [1] = new CoverNode (1, new PointF (rect .Right, rect .Top), cornerradius, Cursors .SizeNESW);
            nodes [2] = new CoverNode (2, new PointF (rect .Right, rect .Bottom), cornerradius, Cursors .SizeNWSE);
            nodes [3] = new CoverNode (3, new PointF (rect .Left, rect .Bottom), cornerradius, Cursors .SizeNESW);

            nodes [4] = new CoverNode (4, new PointF [] {new PointF (rect .Left - smallhalf, rect .Top), 
                                                         new PointF (rect .Left + smallhalf, rect .Top),
                                                         new PointF (rect .Left + smallhalf, rect .Bottom),
                                                         new PointF (rect .Left - smallhalf, rect .Bottom)},
                                       MovementFreedom .WE, Cursors .SizeWE);
            nodes [5] = new CoverNode (5, new PointF [] {new PointF (rect .Left, rect .Top - smallhalf), 
                                                         new PointF (rect .Right, rect .Top - smallhalf),
                                                         new PointF (rect .Right, rect .Top + smallhalf),
                                                         new PointF (rect .Left, rect .Top + smallhalf)},
                                       MovementFreedom .NS, Cursors .SizeNS);
            nodes [6] = new CoverNode (6, new PointF [] { new PointF (rect .Right - smallhalf, rect .Top), 
                                                         new PointF (rect .Right + smallhalf, rect .Top),
                                                         new PointF (rect .Right + smallhalf, rect .Bottom),
                                                         new PointF (rect .Right - smallhalf, rect .Bottom)},
                                       MovementFreedom .WE, Cursors .SizeWE);
            nodes [7] = new CoverNode (7, new PointF [] { new PointF (rect .Left, rect .Bottom - smallhalf), 
                                                         new PointF (rect .Right, rect .Bottom - smallhalf),
                                                         new PointF (rect .Right, rect .Bottom + smallhalf),
                                                         new PointF (rect .Left, rect .Bottom + smallhalf)},
                                       MovementFreedom .NS, Cursors .SizeNS);
            nodes [8] = new CoverNode (8, new PointF [] { new PointF (rect .Left, rect .Top), 
                                                         new PointF (rect .Right, rect .Top),
                                                         new PointF (rect .Right, rect .Bottom), 
                                                         new PointF (rect .Left, rect .Bottom)});
            cover = new Cover (nodes);
        }
        // -------------------------------------------------
        public override void Move (int cx, int cy)
        {
            rect .X += cx;
            rect .Y += cy;
        }
        // -------------------------------------------------        MoveNode
        public override bool MoveNode (int i, int cx, int cy, Point ptM, MouseButtons catcher)
        {
            bool bRet = false;

            if (catcher == MouseButtons .Left)
            {
                if (i == 8)
                {
                    Move (cx, cy);
                }
                else if (i == 0)        //LT corner
                {
                    if (rect .Height - cy >= minheight)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                    if (rect .Width - cx >= minwidth)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
                else if (i == 1)        // RT corner
                {
                    if (rect .Height - cy >= minheight)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                    if (rect .Width + cx >= minwidth)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                }
                else if (i == 2)        // RB corner
                {
                    if (rect .Width + cx >= minwidth)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                    if (rect .Height + cy >= minheight)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                }
                else if (i == 3)        // LB corner
                {
                    if (rect .Height + cy >= minheight)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                    if (rect .Width - cx >= minwidth)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
                else if (i == 4)     // on left side
                {
                    if (rect .Width - cx >= minwidth)
                    {
                        MoveBorder_Left (cx);
                        bRet = true;
                    }
                }
                else if (i == 5)      // on top
                {
                    if (rect .Height - cy >= minheight)
                    {
                        MoveBorder_Top (cy);
                        bRet = true;
                    }
                }
                else if (i == 6)   // on right side
                {
                    if (rect .Width + cx >= minwidth)
                    {
                        MoveBorder_Right (cx);
                        bRet = true;
                    }
                }
                else if (i == 7)   // on bottom
                {
                    if (rect .Height + cy >= minheight)
                    {
                        MoveBorder_Bottom (cy);
                        bRet = true;
                    }
                }
            }
            return (bRet);
        }
        // -------------------------------------------------        MoveBorder_Top
        private void MoveBorder_Top (int cy)
        {
            rect .Y += cy;
            rect .Height -= cy;
        }
        // -------------------------------------------------        MoveBorder_Bottom
        private void MoveBorder_Bottom (int cy)
        {
            rect .Height += cy;
        }
        // -------------------------------------------------        MoveBorder_Left
        private void MoveBorder_Left (int cx)
        {
            rect .X += cx;
            rect .Width -= cx;
        }
        // -------------------------------------------------        MoveBorder_Right
        private void MoveBorder_Right (int cx)
        {
            rect .Width += cx;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions