Click here to Skip to main content
15,068,164 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
Hi,

I need to draw lines on the image which can be dragable, ie. by clicking on any line, I should be able to move that particular line along the image and finally save the image with lines.

I am using windows Form and picture box.

Please help
Posted
Updated 9-May-11 22:34pm
v2
Comments
Dalek Dave 10-May-11 4:34am
   
Edited for Grammar and Readability.

That is not a simple question, it has two parts:
Drawing on an Image, and saving it: Easy, just get a Graphics object for the image, draw your lines, and save the file:
using (Graphics g = Graphics.FromImage(myImage))
    {
    g.DrawLine(pen, new Point(0, 0), new Point(myImage.Width, myImage.Height));
    }
myImage.Save(@"F:\temp\myimage.jpg", ImageFormat.Jpeg);

The second part is more complex:
How do you draw moveable lines?
You will have to handle the PictureBox Paint event, and remember where your lines are.
You will have to handle the PictureBox mouse events, and detect if you are on an existing line, or a new line - it is probably easier to pick them up at the ends only, rather than trying to work out if you are on a line by picking it up in the middle.

I'm not going to try and give you instructions for that here: it is far to much for a quick answer.

Google for basic drawing on WinForms, and follow any tutorial or other instructions you can find.
   
Comments
Olivier Levrey 10-May-11 3:47am
   
Have my 5 for this answer. I gave a solution for the second part.
Dalek Dave 10-May-11 4:34am
   
Good Answer.
OriginalGriff answered the first part. I will give the idea for the second one.

I supposed the lines are vertical lines, and the x potisions for each line are stored in an xLines array.
Add also a member int selectedLineIndex; in your form to store the current line index.

In your form constructor, add the following handlers for your PictureBox:
C#
//paint handler
pictureBox1.Paint += (sender, e) =>
{
    foreach (int x in xLines)
    {
        //draw the line
        e.Graphics.DrawLine(Pens.Red, x, 0, x, pictureBox1.Height);
    }
};
//mouse down handler
//this function will update selectedLineIndex
pictureBox1.MouseDown += (sender, e) =>
{
    //find the closest line from the current mouse position
    int minDistance = int.MaxValue;
    selectedLineIndex = 0;
    for (int k = 0; k < xLines.Length; k++)
    {
        int dist = Math.Abs(e.X - xLines[k]);
        if (dist < minDistance)
        {
            minDistance = dist;
            selectedLineIndex = k;
        }
    }
};
//mouse move handler
pictureBox1.MouseMove += (sender, e) =>
{
    //if the left button is not clicked, exit
    if ((MouseButtons & MouseButtons.Left) != MouseButtons.Left)
        return;
    //update the selected line position
    xLines[selectedLineIndex] = e.X;
    //redraw the picture box
    pictureBox1.Invalidate();
};


------------------------------------------------

To handle both horizontal and vertical lines:

C#
public partial class Form1 : Form
{
   int[] xLines;
   int[] yLines;
   int selectedLineIndex;
   //true to move vertical lines, false to move horizontal lines
   bool xline = false;

   public Form1()
   {
       InitializeComponent();

       int xmid = pictureBox1.Width / 2;
       int ymid = pictureBox1.Height / 2;
       xLines = new int[] { xmid, xmid / 2 };
       yLines = new int[] { ymid, ymid / 2 };
   }

   private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
   {
       //find the closest vertical line from the current mouse position
       int xminDistance = int.MaxValue;
       int xSelectedLineIndex = 0;
       for (int i = 0; i < xLines.Length; i++)
       {
           int dist = Math.Abs(e.X - xLines[i]);
           if (dist < xminDistance)
           {
               xminDistance = dist;
               xSelectedLineIndex = i;
           }
       }
       //find the closest horizontal line from the current mouse position
       int yminDistance = int.MaxValue;
       int ySelectedLineIndex = 0;
       for (int i = 0; i < yLines.Length; i++)
       {
           int dist = Math.Abs(e.Y - yLines[i]);
           if (dist < yminDistance)
           {
               yminDistance = dist;
               ySelectedLineIndex = i;
           }
       }
       //keep the closest line
       if (xminDistance < yminDistance)
       {
           selectedLineIndex = xSelectedLineIndex;
           xline = true;
       }
       else
       {
           selectedLineIndex = ySelectedLineIndex;
           xline = false;
       }
   }
   private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
   {
       //if the left button is not clicked, exit
       if ((MouseButtons & MouseButtons.Left) != MouseButtons.Left)
           return;
       //update the selected line position
       if (xline)
           xLines[selectedLineIndex] = e.X;
       else
           yLines[selectedLineIndex] = e.Y;
       //redraw the picture box
       pictureBox1.Invalidate();
   }
}
   
v5
Comments
Deepurs 10-May-11 4:09am
   
Am getting error at this line --> int dist = Math.Abs(e.X - xLines[k].X);

Error 1 'int' does not contain a definition for 'X' and no extension method 'X' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?)
Olivier Levrey 10-May-11 4:15am
   
Sorry remove ".X".
In this code the closest line will be chosen automatically. If you want to choose it manually, you will need to add more code.
Do you want to ask the user to choose it with a combo box for example or something?
Deepurs 10-May-11 4:19am
   
Yes. I need manual selection of lines. User should be able to select lines just by a mouse click on that particular line. Please help. And thank you so much
Deepurs 10-May-11 4:20am
   
To be more specific user should be able to drag lines on the image and save.
Olivier Levrey 10-May-11 4:31am
   
I updated my answer. You need to add a selectedLineIndex member, the MouseDown handler and change the MouseMove handler.
Deepurs 10-May-11 4:45am
   
am not able to drag lines :(
Olivier Levrey 10-May-11 4:50am
   
I used this code in my own applications, I know this works. There should be something you did wrong. Use the debugger to see if you go into your handlers and check the x values. Check that the correct point is selected, ...
Deepurs 10-May-11 5:01am
   
Attaching my code...am Still not able to drag.
Olivier Levrey 10-May-11 5:15am
   
1- you shouldn't initialize xLines in the Paint handler but in the constructor. Otherwise you will reset it for each repaint to its default value.

2- I changed my MouseMove handler but you didn't: remove the "find closest lin" part.
Deepurs 10-May-11 5:22am
   
Thank you so much. It works perfectly :) Sorry for my mistakes. Thanks again
Deepurs 10-May-11 5:25am
   
One last question, When i do pictureBox1.Image = null; for clearing the pictureBox...only image is getting cleared, Not the lines :(
Olivier Levrey 10-May-11 5:30am
   
After setting the image to null, you should remove your paint handler and then repaint again:


pictureBox1.Paint -= pictureBox1_Paint;
pictureBox1.Invalidate();


If everything is working, then you can click on "accept answer".
Thanks.
Deepurs 10-May-11 5:33am
   
Thanks a ton :)
Olivier Levrey 10-May-11 5:34am
   
You are welcome.
Deepurs 10-May-11 6:29am
   
Its working fine if i have only vertical lines. I included horizontal lines also in the same manner. But when i drag any line, both horizontal and vertical lines are moved.
Olivier Levrey 10-May-11 6:42am
   
Yes it is a little bit harder with both vertical and horizontal lines:
- you need 2 arrays (one for horizontal lines, one for vertical ones)
- you need a bool member to know it you are moving a horizontal line, or a vertical line (this bool member will be updated in the "find closest line" part)
- you have to change the "find closest line" part: find the closest vertical line, find the closest horizontal line, then compare both and keep the closest.
Deepurs 10-May-11 6:58am
   
am getting array out of bounds expection at lines
xLines[selectedLineIndex] = e.X;
yLines[selectedLineIndex] = e.Y;
Deepurs 10-May-11 6:58am
   
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestLine
{
public partial class Form1 : Form
{
bool imageOpened = false;
int imght, imgwd, xmid, ymid;
int[] xLines;
int[] yLines;
int selectedLineIndex;
bool xline = false;
bool yline = false;
int xdist, ydist;
int x,y;

public Form1()
{
InitializeComponent();
imght = pictureBox1.Height;
imgwd = pictureBox1.Width;
xmid = (imgwd) / 2;
ymid = (imght) / 2;

xLines = new int[] { xmid, xmid / 2 };
yLines = new int[] { ymid, ymid / 2 };
}

private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog1 = new OpenFileDialog();
if (openFileDialog1.ShowDialog() != DialogResult.Cancel)
{
// Display the image in the PictureBox.
pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
// Display the Resolution of a loaded image in a status bar
//imgSizeStatusLabel.Text = "" + pictureBox1.Width + "x" + pictureBox1.Height + "";
}
imageOpened = true;
}

private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
if (imageOpened == true)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "JPEG Files(*.jpg)|*.jpg";
Bitmap orgImg = new Bitmap(pictureBox1.Width, pictureBox1.Height);
pictureBox1.DrawToBitmap(orgImg, pictureBox1.Bounds);
if (sfd.ShowDialog() == DialogResult.OK)
orgImg.Save(sfd.FileName);
}
else
MessageBox.Show("No image to save");
}


private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
//find the closest line from the current mouse position
int xminDistance = int.MaxValue;
int yminDistance = int.MaxValue;

selectedLineIndex = 0;


for (x = 0; x < xLines.Length; x++)
{
xdist = Math.Abs(e.X - xLines[x]);
if (xdist < xminDistance)
{
xminDistance = xdist;
//selectedLineIndex = x;

}
}
for (y = 0; y < yLines.Length; y++)
{
ydist = Math.Abs(e.Y - xLines[y]);
if (ydist < yminDistance)
{
yminDistance = ydist;
//selectedLineIndex = y;
}
}

if (xminDistance < yminDistance)
{
xline = true;
}
else if (yminDistance < xminDistance)
{
yline = true;
}

if (xline)
{
selectedLineIndex = x;
}
else if (yline)
{
selectedLineIndex = y;
}

}

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{

foreach (int x in xLines)
{
//draw the line
e.Graphics.DrawLine(Pens.Red, x, 0, x, pictureBox1.Height);
}
foreach (int y in yLines)
{
//draw the line
e.Graphics.DrawLine(Pens.Red, 0, y, pictureBox1.Width, y);
}
}

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if ((MouseB
Olivier Levrey 10-May-11 7:57am
   
I updated my answer. Tell me if it works (I didn't test it).
Deepurs 10-May-11 8:05am
   
its working perfectly... thanks :)
Olivier Levrey 10-May-11 8:06am
   
Ok good :)
Deepurs 11-May-11 0:50am
   
Am trying to draw a small circle on the image with a mouse click. But the circle is drawn away from the mouse clicked point. here is my code.
private void pictureBox1_MouseClick(object sender, MouseEventArgs e1)
{
string str = "O";
if ((e1.Button & MouseButtons.Left) == MouseButtons.Left)
{
Bitmap b = (Bitmap)pictureBox1.Image;
pictureBox1.Image = b;
Graphics g = Graphics.FromImage(b);
p = new Point(e1.X, e1.Y);
Pen p3 = new Pen(Color.Red, 1);
this.Text = p.ToString();
SizeF sizef = g.MeasureString(str, Font);
f = new Font(new FontFamily("Times New Roman"), 10);
g.DrawString(str, f, Brushes.Red, ((p.X)), ((p.Y)));
}
pictureBox1.Invalidate();
}
Dalek Dave 10-May-11 4:35am
   
Nice.
Olivier Levrey 10-May-11 4:43am
   
Thank you Dalek.
Manfred Rudolf Bihy 10-May-11 8:17am
   
Good work! 5+
There are various ways to achieve this, I would go for a solution where you store a list of Line objects and render them after the base render pass of the PictureBox.
This would make it easy to move and change the style of all lines.

To save a image is fairly simple, there's a Save method on Image.

You could try extending PictureBox like this, this is a working example;



C#
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace LinesTest
{
    public class LinedPictureBox : PictureBox
    {
        private IList<Line> lines = new List<Line>();
        private Line currentLine = null;
        private bool holdsA = false;
        public LinedPictureBox()
        {
            DoubleBuffered = true;
            MouseDown += HandleMouseDown;
            MouseMove += HandleMouseMove;
            MouseUp += (s, e) => currentLine = null;
        }
        private static int GetDistance(Point a, Point b)
        {
            int dx = a.X - b.X;
            int dy = a.Y - b.Y;
            return (int)Math.Sqrt(dx * dx + dy * dy);
        }
        private void HandleMouseMove(object sender, MouseEventArgs e)
        {
            if (currentLine != null)
            {
                if (holdsA)
                    currentLine.A = e.Location;
                else
                    currentLine.B = e.Location;
                Invalidate();
            }
        }
        private void HandleMouseDown(object sender, MouseEventArgs e)
        {
            Line aLine = (from line in lines orderby GetDistance(line.A, e.Location) select line).FirstOrDefault();
            Line bLine = (from line in lines orderby GetDistance(line.B, e.Location) select line).FirstOrDefault();
            if (aLine != null && bLine != null)
            {
                int aDistance = GetDistance(aLine.A, e.Location);
                int bDistance = GetDistance(bLine.B, e.Location);
                if (Math.Min(aDistance, bDistance) < 8)
                {
                    if (aDistance < bDistance)
                    {
                        currentLine = aLine;
                        holdsA = true;
                    }
                    else
                    {
                        currentLine = bLine;
                        holdsA = false;
                    }
                    return;
                }
            }
            currentLine = new Line { A = e.Location, B = e.Location, Pen = Pens.Red };
            holdsA = false;
            lines.Add(currentLine);
            Invalidate();
        }
        private void PaintLines(Graphics graphics)
        {
            foreach (Line line in lines)
                graphics.DrawLine(line.Pen, line.A, line.B);
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            PaintLines(e.Graphics);
        }
        public void Save(string filename)
        {
            Image image = new Bitmap(Width, Height);
            Graphics graphics = Graphics.FromImage(image);
            switch (SizeMode)
            {
                case PictureBoxSizeMode.AutoSize:
                case PictureBoxSizeMode.Normal:
                    graphics.DrawImage(Image, new Point());
                    break;
                case PictureBoxSizeMode.StretchImage:
                    graphics.DrawImage(Image, new Rectangle(0, 0, Width, Height), new Rectangle(0, 0, Image.Width, Image.Height), GraphicsUnit.Pixel);
                    break;
                case PictureBoxSizeMode.CenterImage:
                    {
                        int x = Math.Max(0, (Image.Width - Width) / 2);
                        int y = Math.Max(0, (Image.Height - Height) / 2);
                        graphics.DrawImage(Image, new Rectangle(0, 0, Width, Height), new Rectangle(x, y, Width, Height), GraphicsUnit.Pixel);
                    }
                    break;
            }
            PaintLines(graphics);
            image.Save(filename);
        }
    }
    public class Line
    {
        public Point A { get; set; }
        public Point B { get; set; }
        public Pen Pen { get; set; }
    }
}


Hope this helps,
Fredrik
   
Comments
Dalek Dave 10-May-11 4:35am
   
You worked hard on that!
Fredrik Bornander 10-May-11 4:55am
   
Don't tell anyone, I'm supposed to be working.
Deepurs 10-May-11 5:03am
   
How to use the extended pictureBox in my code? Sorry am new to C#
Fredrik Bornander 10-May-11 5:20am
   
Just added the class to your solution and rebuild it.
The LinedPictureBox should show up in the ToolBox so you can just drag and drop it onto your form.
Manfred Rudolf Bihy 10-May-11 8:17am
   
Great effort! 5+
Fredrik Bornander 10-May-11 10:03am
   
Thanks.

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




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900