Creating a resizable grid to overlay on a image using c#

后端 未结 1 1574
半阙折子戏
半阙折子戏 2020-12-18 15:21

I am trying to figure out a method in which the user can drag a grid over a image and then resize the columns and rows to fit the image. How would i go about creating someth

相关标签:
1条回答
  • 2020-12-18 15:43

    Here is a grid class that can be overlaid over any Control and will draw an N x M grid of lines.

    You can move the lines with the mouse and move the grid with the right mouse button. You can access the current x- and y- values in the two List<int> Xs and Ys.

    It is a Panel subclass and you should make sure it has the correct size and number of rows and columns.

    Let's see it in action:

    To set it up use the Init function..

    Here is the code:

    public partial class Grid : Panel
    {
        public Grid()
        {
            InitializeComponent();
            GridColor = Color.DarkMagenta;
            HandleSize = 4;
            BackColor = Color.Transparent;
            DoubleBuffered = true;
        }
    
        int RowCount { get; set; }
        int ColCount { get; set; }
        Color GridColor { get; set; }
        int HandleSize { get; set; }
    
        List<int> Xs { get; set; }
        List<int> Ys { get; set; }
    
        public void Init(int cols, int rows)
        {
            RowCount = rows;
            ColCount = cols;
            Xs = new List<int>();
            Ys = new  List<int>();
            float w = 1f * Width / cols;
            float h = 1f * Height / rows;
    
            for (int i = 0; i <= cols; i++) Xs.Add((int)(i * w));
            for (int i = 0; i <= rows; i++) Ys.Add((int)(i * h));
            // draw inside the panel only
            if (Xs[cols] == Width) Xs[cols]--;
            if (Ys[rows] == Height) Ys[cols]--;
        }
    
        public void Init(int cols, int rows, Size sz)
        {
            Size = sz;
            Init(cols, rows);
        }
    
        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
    
            using (Pen pen = new Pen(GridColor))
            {
                foreach (int x in Xs) pe.Graphics.DrawLine(pen, x, 0, x, Height);
                foreach (int y in Ys) pe.Graphics.DrawLine(pen, 0, y, Width, y);
            }
        }
    
        private Point mDown = Point.Empty;
    
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (Cursor != Cursors.Default) mDown = e.Location;
        }
    
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
    
            // distances
            var dx = Xs.Select(x => Math.Abs(x - e.X));
            var dy = Ys.Select(y => Math.Abs(y - e.Y));
            // smallest distance
            int mx = dx.Min();
            int my = dy.Min();
            // grid index
            int ix = dx.ToList().IndexOf(mx);
            int iy = dy.ToList().IndexOf(my);
    
            if (e.Button.HasFlag(MouseButtons.Right))
            {   // move the grid with the right mouse button
                Location = new Point(Left + e.X - mDown.X, Top + e.Y - mDown.Y);
            }
            else if (!e.Button.HasFlag(MouseButtons.Left))
            {   // if we are close enough set cursor
                Cursor = Cursors.Default;
                if (mx < HandleSize) Cursor = Cursors.SizeWE;
                if (my < HandleSize) Cursor = Cursors.SizeNS;
                if (mx < HandleSize && my < HandleSize) Cursor = Cursors.SizeAll;
            }
            else
            {   // else move grid line(s)
                if (Cursor == Cursors.SizeWE  || Cursor == Cursors.SizeAll)
                   Xs[ix] += e.X - mDown.X;
                if (Cursor == Cursors.SizeNS  || Cursor == Cursors.SizeAll) 
                   Ys[iy] +=  e.Y - mDown.Y;
                Invalidate();
                mDown = e.Location;
                // restore order in case we overshot
                Xs = Xs.OrderBy(x => x).ToList();
                Ys = Ys.OrderBy(x => x).ToList();
            }
        }
    }
    

    It was just a quick shot, so many things can and probably should be improved, like adding and removing columns and rows, validating etc..

    I set it up to overlay a Panel panel1 like this:

    Grid grid1 = new Grid();
    panel1.Controls.Add(grid1);
    //grid1.Size = panel1.ClientSize;    // overlay full area..or..
    grid1.Init(4, 3, new Size(99, 44));  // .. use the overload with size
    grid1.Invalidate();
    

    To let the user place and size it where he wants it you can use the usual mouse events instead..

    Update: On re-reading I saw that you wanted to let the user resize the grid as well. Here is an example of how to expand the code to allow resizing from the left or right edge..:

            {   // else move gridline or size grid
                if (Cursor == Cursors.SizeWE  || Cursor == Cursors.SizeAll)
                {
                    int delta = mDown.X - e.X;
                    if (ix == 0)  // left edge: resize
                    {
                        Width += delta;
                        Left -= delta;
                        Xs[Xs.Count - 1] = Width - 1;
                    }
                    else if (ix == Xs.Count - 1)  // right edge resize
                    {
                        Width -= delta;
                        Xs[Xs.Count - 1] = Width - 1;
                    }
                    else Xs[ix] -= delta;  // move gridline
                }
    

    The top & bottom edges will work the in the very same way. Like with the line crossings resizing will also work from the corners..


    Update: Instead of a Panel, which is a Container control and not really meant to draw onto you can use a Picturebox or a Label (with Autosize=false); both have the DoubleBuffered property turned on out of the box and support drawing better than Panels do.

    0 讨论(0)
提交回复
热议问题