Search code examples
c#.netwinformspaintpicturebox

Paint Drawline Image into Picturebox Image


In my Form, I have 2 picturebox controls. I loaded a blue background image on pictureBox1 and left pictureBox2 control alone. With the code below I'm able to draw arrows on top of my picturebox1 image.

Goal: On my pictureBox1_MouseUp event I want to add all the arrows which I drew on pictureBox1 to pictureBox2.

Issue: The problem is on my pictureBox1_MouseUp event when I wrote pictureBox2.Image = pictureBox1.Imageit doesn't add the painted arrow which I drew on pictureBox1. It only adds the pictureBox1 image that I assigned in my form load event.

    private bool isMoving = false;
    private Point mouseDownPosition = Point.Empty;
    private Point mouseMovePosition = Point.Empty;
    private List<Tuple<Point, Point>> lines = new List<Tuple<Point, Point>>();
    Pen _Pen;

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {

        if (isMoving)
        {
            if (pictureBox1.Image == null) e.Graphics.Clear(Color.White);

            // Add this line for high quality drawing:
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

            AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
            _Pen = new Pen(Color.IndianRed, 3);
            _Pen.CustomEndCap = bigArrow;
            e.Graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
            _Pen.Dispose();
        }
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        isMoving = true;
        mouseDownPosition = e.Location;
    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (isMoving)
        {
            mouseMovePosition = e.Location;
            pictureBox1.Invalidate();
        }
    }

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {

        if (isMoving)
        {
            lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
        }
        isMoving = false;

        pictureBox2.Image = pictureBox1.Image;
    }

enter image description here

Test 1: (Changed pictureBox1_Paint code)

With this code, it draws the arrow on pictureBox2, but it looks like it is drawing multiple arrows.

        if (isMoving)
        {
            if (pictureBox1.Image == null) e.Graphics.Clear(Color.White);

            // Add this line for high quality drawing:
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

            AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
            _Pen = new Pen(Color.IndianRed, 3);
            Bitmap BitImg = (Bitmap)pictureBox1.Image;
            _Pen.CustomEndCap = bigArrow;
            using (var graphics = Graphics.FromImage(BitImg))
            {
                graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
            }
            pictureBox1.Image = BitImg;
            _Pen.Dispose();
        } 

enter image description here

Test 2: (I took the code from the paint event and pasted it MouseMove Event with some modifications. This uses too much memory and it doesn't draw on pictureBox1 but the arrow is now visible in pictureBox2)

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        if (isMoving)
        {
            mouseMovePosition = e.Location;

            if (isMoving)
            {

                AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
                _Pen = new Pen(Color.IndianRed, 3);
                BitImg = new Bitmap(pictureBox1.Image);                    
                _Pen.CustomEndCap = bigArrow;
                using (var graphics = Graphics.FromImage(BitImg))
                {
                    graphics.SmoothingMode = SmoothingMode.HighQuality;
                    graphics.DrawLine(_Pen, mouseDownPosition, mouseMovePosition);
                }

                _Pen.Dispose();
            }

            pictureBox1.Invalidate();

        }
    }

enter image description here


Solution

  • Draw all lines to pictureBox1: enter image description here

    Draw only last line to pictureBox1: enter image description here

    In pictureBox2 control, add Paint event to pictureBox2_Paint.

    I suggest you make pen and cap global varriable:

    // Make pen and cap global varriable to boost the perfomane.
    // Create and delete them each draw will cost alot of CPU
    Pen pen = new Pen(Color.IndianRed, 3);
    AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
    

    In Form1() event, add this line:

    pen.CustomEndCap = bigArrow;
    

    and do as following:

    public partial class Form1 : Form
    {
        private bool isMoving = false;
        private Point mouseDownPosition = Point.Empty;
        private Point mouseMovePosition = Point.Empty;
        private List<Tuple<Point, Point>> lines = new List<Tuple<Point, Point>>();
    
        public Form1()
        {
            InitializeComponent();
            pen.CustomEndCap = bigArrow;
        }
    
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            isMoving = true;
            mouseDownPosition = e.Location;
        }
    
        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (isMoving)
            {
                mouseMovePosition = e.Location;
                pictureBox1.Invalidate();
            }
        }
    
        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (isMoving)
            {
                lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
                pictureBox2.Invalidate();
            }
            isMoving = false;
        }
    
        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            if (isMoving)
            {
                if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
    
                // Add this line for high quality drawing:
                e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
    
                e.Graphics.DrawLine(pen, mouseDownPosition, mouseMovePosition);
    
                // If you want draw all previous lines here, add bellow code:
                //foreach (var line in lines)
                //{
                //    e.Graphics.DrawLine(pen, line.Item1, line.Item2);
                //}
            }
        }
    
        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
    
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            foreach (var line in lines)
            {
                e.Graphics.DrawLine(pen, line.Item1, line.Item2);
            }
        }
    }
    

    Above code draw lines to PictureBox control, not to image, this allow you remove some lines or clear all lines you draw to picturebox if you want later.

    If you want draw directly to image, things event much easier, you don't need pictureBox2_Paint at all:

    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        if (isMoving)
        {
            // You event don't need this line
            //lines.Add(Tuple.Create(mouseDownPosition, mouseMovePosition));
    
            if (pictureBox1.Image != null)
            {
                using (var g = Graphics.FromImage(pictureBox1.Image))
                {
                    g.SmoothingMode = SmoothingMode.HighQuality;
                    g.DrawLine(pen, mouseDownPosition, mouseMovePosition);
                }
                pictureBox2.Image = pictureBox1.Image;
            }
        }
        isMoving = false;
    }
    
    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (isMoving)
        {
            if ((sender as PictureBox).Image == null) e.Graphics.Clear(Color.White);
    
            e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
            e.Graphics.DrawLine(pen, mouseDownPosition, mouseMovePosition);
        }
    }
    
    private void pictureBox2_Paint(object sender, PaintEventArgs e)
    {
    }