2

My program can draw lines using canvas.Drawline(). How to click line and change this color (select line)?

private List<Point> coordFirst = new List<Point>();
private List<Point> coordLast = new List<Point>();
public Graphics canvas;

        private void Form1_Load(object sender, EventArgs e)
        {
            canvas=panel1.CreateGraphics();
        }

Coordinate line stored in coordFirs & coodLast.

TaW
  • 53,122
  • 8
  • 69
  • 111
Kancil
  • 35
  • 3
  • 10
  • 1
    What are you targetting: Winforms? WPF? ASP? ...?? __Always__ tag your question accordingly! - If it is Winforrms here are two hints: 1) __NEVER__ try to cache a Graphics object! __NEVER__ !! - All drawing must be done with the `e.Graphics` object of a `Paint` event's parameters! - 2) There is no such thing as a 'Line' in winforms, only pixels of various colors. So to _select a line_ you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking. This could be done with math or by looking at the pixels. For better advice we need more info about your goals! – TaW Oct 03 '15 at 07:55
  • 1
    For the necessary Math see [here](https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points)! - Instead of using two dumb lists you should define a Line class with all info a line should hve, like color, width, selectionstate and also methods to draw itself using a Graphics obejct and a method to see if a Point is on it! – TaW Oct 03 '15 at 08:26

2 Answers2

6

Here is a suitable Line class:

class Line
{
    public Color LineColor { get; set; }
    public float Linewidth { get; set; }
    public bool Selected { get; set; }
    public Point Start { get; set; }
    public Point End { get; set; }

    public Line(Color c, float w, Point s, Point e)
    { LineColor = c; Linewidth = w; Start = s; End = e;    }

    public void Draw(Graphics G)
    { using (Pen pen = new Pen(LineColor, Linewidth)) G.DrawLine(pen, Start, End); }

    public bool HitTest(Point Pt)
    {
        // test if we fall outside of the bounding box:
        if ((Pt.X < Start.X && Pt.X < End.X) || (Pt.X > Start.X && Pt.X > End.X) ||
            (Pt.Y < Start.Y && Pt.Y < End.Y) || (Pt.Y > Start.Y && Pt.Y > End.Y)) 
            return false;
        // now we calculate the distance:
        float dy = End.Y - Start.Y;
        float dx = End.X - Start.X;
        float Z = dy * Pt.X - dx * Pt.Y + Start.Y * End.X - Start.X * End.Y;
        float N = dy * dy + dx * dx;
        float dist = (float)( Math.Abs(Z) / Math.Sqrt(N));
        // done:
        return dist < Linewidth / 2f;
    }

}

Define a List for the lines, probably at class level:

    List<Line> lines = new List<Line>();

Here is how you can initialize it with a few lines:

for (int i = 0; i < 20; i++) lines.Add(new Line(Color.Black, 4f, 
    new Point(R.Next(panel1.Width), R.Next(panel1.Height)), 
    new Point(R.Next(panel1.Width), R.Next(panel1.Height))));

Here is the result of clicking on a crossing:

enter image description here

Whenever you add, change or remove a line you need to make the Panel reflect the news by triggering the Paint event:

panel1.Invalidate();

Here is the Paint event of the Panel:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    foreach (Line L in lines) L.Draw(e.Graphics);
}

In the MouseClick event you do the test:

private void panel1_MouseClick(object sender, MouseEventArgs e)
{
    foreach(Line L in lines) 
            L.LineColor = L.HitTest(e.Location) ?  Color.Red : Color.Black;
    panel1.Invalidate();
}

To avoid flicker don't use the basic Panel class as it isn't doublebuffered. Instead use either a PictureBox or a Label (with AutoSize=false) or a doublebuffered Panel subclass:

class DrawPanel : Panel 
{     public DrawPanel ()   { DoubleBuffered = true; }   }

Notes:

  • There is no such thing as a 'Line' in WinForms, only pixels of various colors. So to select a line you need to store it's two endpoints' coordinates and then find out if you have hit it when clicking.

  • The above example shows how to do it in math.

  • Instead one could test each line by drawing it onto a bitmap and test the pixel the mouse has clicked. But drawing those bitmaps would have to do math behind the scenes as well and also allocate space for the bitmaps, so the math will be more efficient..

  • Yes the Line class looks a little long for such a simple thing a s a line but look how short all the event codes now are! That's because the responsiblities are where they belong!

  • Also note the the first rule of doing any drawing in WinForms is: Never cache or store a Grahics object. In fact you shouldn't ever use CreateGraphics in the first place, as the Graphics object will never stay in scope and the graphics it produces will not persist (i.e. survive a Minimize-maximize sequence)..

  • Also note how I pass out the e.Graphics object of the Paint event's parameters to the Line instances so they can draw themselves with a current Graphics object!

  • To select thinner lines it may help to modify the distance check a little..

  • The Math was taken directly form Wikipedia.

TaW
  • 53,122
  • 8
  • 69
  • 111
-1

You can change the color of everything on click. By using click event of particular object.

I give you an example for button. If you click on button then panal’s color will be change. You can modify the code as per your requirement.

private List<Point> coordFirst = new List<Point>();
    private List<Point> coordLast = new List<Point>();
    public Graphics canvas;



    private void Form1_Load(object sender, EventArgs e)
    {
        canvas = panel1.CreateGraphics();
    }





    private void panel1_Click(object sender, EventArgs e)
    {
        panel1.BackColor = Color.Blue;
    }

    private void nonSelectableButton3_Click(object sender, EventArgs e)
    {
        panel1.BackColor = Color.BurlyWood;
    }
Muhammed Naqi
  • 47
  • 1
  • 9