Results 1 to 19 of 19

Thread: [RESOLVED] Trouble with DrawLine

  1. #1

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Resolved [RESOLVED] Trouble with DrawLine

    I'm trying to draw a line in a picturebox on my form. Using System.Drawing, I would think this to be really easy but for the life of me I can't get the line to actually appear. When I run the code below, absolutely nothing happens. The form opens, I click button1, and get nothing.

    There are no compile errors, and no runtime errors. Why won't the line show up on the form?

    The code I'm using to do this is:

    Code:
    using System;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    namespace WindowsApplication1
    {
        public partial class Form1 : Form
        {
            private Point start = Point.Empty;
            private Point end = Point.Empty;
            private Random rand = new Random();
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void Form1_Load(object sender, EventArgs e)
            {
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Graphics g;
                g = this.pictureBox1.CreateGraphics();
                Pen aPen = new Pen(Brushes.Black, 2);
                g.DrawLine(aPen, 0, 0, 10, 10);
    
                this.pictureBox1.Refresh();
                this.Refresh();
            }
        }
    }
    Last edited by Fedge; Apr 11th, 2007 at 10:33 PM.

  2. #2
    Hyperactive Member
    Join Date
    Mar 2006
    Location
    Madrid
    Posts
    325

    Re: Trouble with DrawLine

    Not sure whether this will work,...

    Try,........

    this.Invalidate

    Instead of

    this.refresh.

  3. #3
    G&G Moderator chemicalNova's Avatar
    Join Date
    Jun 2002
    Location
    Victoria, Australia
    Posts
    4,246

    Re: Trouble with DrawLine

    Its been a while since I've done anything similar, but I'm fairly certain refreshing the control erases the drawn lines.

    Try it without refresh.

    chem

    Visual Studio 6, Visual Studio.NET 2005, MASM

  4. #4

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    Thanks guys. You're both correct. I removed
    Code:
    this.pictureBox1.Refresh();
    this.Refresh();
    and everything works perfect. It works with or without this.Invalidate(), for what that's worth. I had a feeling it was something really dumb on my part. Thanks for the help.

  5. #5
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine [RESOLVED]

    That code is actually no good. Drawing anything on a control with GDI+ anywhere but in that control's Paint event handler is wrong. The reason that your original code wasn't working was that by calling Refresh you were forcing the control to repaint, which was erasing your drawing. Now that you've removed that call your drawing will stay, but only until the control is repainted.

    Try minimising and restoring your form, or dragging another window over it, and you'll see that your drawing just disappears. That's because the form is repainted and your drawing isn't done again so it's lost. ALL drawing on controls with GDI+ MUST be done in that control's Paint event handler. What you do is set the values of control variables that describe what to draw, then call the control's Refresh method or its Invalidate and Update methods. That will force the control to repaint, which raises its Paint event. You handle that event, retrieve the values of your control variables and perfrom the drawing that they describe.

    Check out the #1 FAQ question here.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  6. #6
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: Trouble with DrawLine [RESOLVED]

    The best plan is to setup a bitmap and draw everything to that when required, then just draw that bitmap to the control in the paint event. Easy and persistent.
    I don't live here any more.

  7. #7

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    jmcilhinney, thanks for your reply.

    I hadn't noticed that until you pointed it out, but the repainting is an issue now. I checked out the FAQ question, and while the code does seem to work, I can't quite get it to suit my purpose.

    I have a TableLayoutPanel that contains a whole bunch of picturboxes, each containing an image. On a button click, I need to draw lines on top of on one or more of the picturesboxes. The coordinates for the lines vary and are somewhat dynamic, so I (think) I need someway to pass the paint event variable coordinates. The picturboxes are added to the table at runtime.

    After reading the FAQ, it seems I need a paint event for each box?? Do I have to have 100 different paint event functions?

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine

    You need a Paint event handler for every control you want to draw on. If you have 100 PictureBoxes that you want to draw on then you need a Paint event handler for each one. That doesn't necessarily mean that you need 100 different event handlers though. If the process is the same for all of them then you can simply create one event handler and have it handle the Paint event for all the controls. The easiest way to distinguish what you need to draw is with a Dictionary. You create a Dictionary keyed on the control where the values are the drawing control data. In the Paint event handler you use the sender to get the value from the Dictionary for that control. Here's a very simple example of drawing a line on three PictureBoxes. Create a new project and add three PictureBoxes to the form. Now replace the form's code with the following:
    vb Code:
    1. Public Class Form1
    2.  
    3.     'A structure that represents a line with a start point and an end point.
    4.     Private Structure Line
    5.         Public Start As Point
    6.         Public [End] As Point
    7.  
    8.         Public Sub New(ByVal start As Point, ByVal [end] As Point)
    9.             Me.Start = start
    10.             Me.End = [end]
    11.         End Sub
    12.     End Structure
    13.  
    14.     'Line objects keyed by the PictureBox they will be drawn on.
    15.     Private linesByPictureBox As New Dictionary(Of PictureBox, Line)
    16.  
    17.     Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    18.         'Add three lines to the Dictionary keyed by the PictureBox they will be drawn on.
    19.         Me.linesByPictureBox.Add(Me.PictureBox1, _
    20.                                  New Line(New Point(10, 5), _
    21.                                           New Point(15, 30)))
    22.         Me.linesByPictureBox.Add(Me.PictureBox2, _
    23.                                  New Line(New Point(20, 10), _
    24.                                           New Point(5, 25)))
    25.         Me.linesByPictureBox.Add(Me.PictureBox3, _
    26.                                  New Line(New Point(0, 15), _
    27.                                           New Point(35, 10)))
    28.     End Sub
    29.  
    30.     Private Sub PictureBox_Paint(ByVal sender As System.Object, _
    31.                                  ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox3.Paint, _
    32.                                                                                          PictureBox2.Paint, _
    33.                                                                                          PictureBox1.Paint
    34.         'Get the PictureBox that raised this Paint event.
    35.         Dim picture As PictureBox = TryCast(sender, PictureBox)
    36.  
    37.         If picture IsNot Nothing AndAlso Me.linesByPictureBox.ContainsKey(picture) Then
    38.             'Get the line for this PictureBox.
    39.             Dim drawing As Line = Me.linesByPictureBox(picture)
    40.  
    41.             'Draw the line on the PictureBox.
    42.             e.Graphics.DrawLine(Pens.Black, drawing.Start, drawing.End)
    43.         End If
    44.     End Sub
    45.  
    46. End Class
    Now run the project and you'll see that the appropriate line gets drawn on each PictureBox. You can change the values of the lines at run time if you want and then when you refresh the controls the old lines will disappear and the new ones will be drawn.

    Check out my simple drawing program http://www.vbforums.com/showthread.php?t=426684 too.

    Edit: Ah b*gger. I got to this thread via my UserCP and I'd forgotten it was in the C# forum. Anyway, hopefully you get the idea and you can do basically the same thing in C#:

    1. Create a Dictionary keyed on your controls.
    2. Store the drawing data, possibly in a custom type, in the Dictionary values.
    3. In the common Paint event handler use the sender to get the drawing data for that control.
    4. Draw the shapes represented by that data.
    Last edited by jmcilhinney; Apr 8th, 2007 at 07:16 PM.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  9. #9

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    jmcilhinney, I adapted your code sample to my needs as best I could, and have it working... sorta.

    The problem I'm having is adding the lines to the dictionary. The pictureboxes are created in a big 3D array at runtime and then added to the form like this:

    Code:
    PictureBox[,,] picArray = new PictureBox[2, 9, 9];
    ...
    for (i,j,k, etc..)
    { { {
          picArray[i, j, k] = new PictureBox();
          picArray[i, j, k].Image = imgSB0;
          picArray[i, j, k].SizeMode = PictureBoxSizeMode.AutoSize;
          picArray[i, j, k].Name = "picBox" + i + j + k;
    
          picArray[i, j, k].Paint += new PaintEventHandler(PictureBox_Paint);
    
          table1.Controls.Add(picArray[i, j,k], i + 1, j);
    } } }
    Then I add the picBox to to the dictionary like this:
    Code:
    Dictionary<PictureBox, Line> dicPictureLines = new Dictionary<PictureBox, Line>();
    ...
    this.dicPictureLines.Add(picArray[x, y, z], new Line(new Point(10, 5), new Point(15, 30)));
    And your PaintEvent handler (translated to C#) looks like this:
    Code:
            private void PictureBox_Paint(object sender, PaintEventArgs e)
            {
                PictureBox picture = sender as PictureBox;
                if (picture != null && dicPictureLines.ContainsKey(picture))
                {
                    Line drawing = this.dicPictureLines[picture];
                    e.Graphics.DrawLine(new Pen(Brushes.Black, 2), drawing.Start, drawing.End);
                }
            }
    So, the problem is that when this.dicPictureLines.Add(picArray[x, y, z],new Line(...) ); is called, nothing is drawn. I think this has something to do with the way the pic boxes in the array are actually stored in the dictionary, but I'm still trying to figure that out.

    The above code works on picboxes that I created at design time, and are not in an array, so the functionality is there. I just need to figure out how to make it work with this picbox array. Perhaps you could shed some light on this for me, and thanks for all your help so far.

  10. #10

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    I think I just solved my own problem. I started from scratch on a fresh project to make this code work, and I managed to do so. Trying to write in this paint/dictionary code in my giant program before attempting small scale was no doubt the source of my problems.

    Anyway, here's the small-scale version that works. I kept the array 2D for now, but you get the idea. Three picboxes are created at design time and one of them is created at runtime (picArray[0,3]).
    Code:
        public partial class Form1 : Form
        {
            Dictionary<PictureBox,Line> dicPictureLines = new Dictionary<PictureBox,Line>();
            PictureBox[,] picArray = new PictureBox[2,4];
    
            private struct Line
            {
                public Point Start;
                public Point End;
    
                public Line(Point start, Point end)
                {
                    this.Start = start;
                    this.End = end;
                }
            }
    
            public Form1()
            {
                InitializeComponent();
                picArray[0,0] = pictureBox1;
                picArray[0,1] = pictureBox2;
                picArray[0,2] = pictureBox3;
    
                picArray[0, 3] = new PictureBox();
                picArray[0, 3].BorderStyle = BorderStyle.FixedSingle;
                this.Controls.Add(picArray[0, 3]);
    
                picArray[0, 0].Paint += new PaintEventHandler(PictureBox_Paint);
                picArray[0, 1].Paint += new PaintEventHandler(PictureBox_Paint);
                picArray[0, 2].Paint += new PaintEventHandler(PictureBox_Paint);
                picArray[0, 3].Paint += new PaintEventHandler(PictureBox_Paint);
            }
    
            private void PictureBox_Paint(object sender, PaintEventArgs e)
            {
                PictureBox picture = sender as PictureBox;
                if (picture != null && dicPictureLines.ContainsKey(picture))
                {
                    Line drawing = this.dicPictureLines[picture];
                    e.Graphics.DrawLine(new Pen(Brushes.Black, 2), drawing.Start, drawing.End);
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                this.dicPictureLines.Add(picArray[0, 0], new Line(new Point(10, 5), new Point(15, 30)));
                this.dicPictureLines.Add(picArray[0, 1], new Line(new Point(20, 10), new Point(5, 25)));
                this.dicPictureLines.Add(picArray[0, 2], new Line(new Point(0, 15), new Point(35, 10)));
                this.dicPictureLines.Add(picArray[0, 3], new Line(new Point(0, 15), new Point(35, 10)));
                this.Refresh();
            }
        }
    Thanks again, jmcilhinney, for your dictionary code. It worked like a charm, once I stopped being a moron

  11. #11

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    I just realized this code only works for drawing one line per picbox. I need to be able to draw multiple graphics to each picbox. Like... 2 lines and a piece of text or 4 lines and 3 pieces of text. The above code doesn't work that way.

    Calling:
    Code:
    this.dicPictureLines.Add(picArray[0, 3], new Line(new Point(0, 15), new Point(35, 10)))
    this.dicPictureLines.Add(picArray[0, 3], new Line(new Point(0, 0), new Point(10, 10)))
    throws and error because the dictionary already contains a value for that particular picturebox.

    I tried:
    Code:
    this.dicPictureLines.Add(picArray[0, 3], new Line(new Point(0, 15), new Point(35, 10)))
    this.dicPictureLines.Remove(picArray[0, 3]);
    this.dicPictureLines.Add(picArray[0, 3], new Line(new Point(0, 0), new Point(10, 10)))
    but it removes the first line before adding the second line.

    Is there an easy fix for adding multiple graphics?

  12. #12
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine

    In my example I was drawing one line per PictureBox so the values in my Dictionary were Line objects. If you wanted to draw multiple lines per PictureBox then the values in your Dictionary could be List<Line> objects. If you want to be able to draw multiple lines and multiple text strings then you should declare your own type that has properties containing a List<Line> and a List<string> and the then the values in the Dictionary should be Lists of that type.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  13. #13

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    jmcilhinney, could you elaborate on declaring a type for List<line> and List<string>? I modified the dictionary to accept multiple lines using List<Line> but I'm a little unawares as to how to create another type.

    I presume some sort of class like GraphicObject that has a list of Lines and Strings, but I think I need some direction.

  14. #14
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine

    If all you want are lines and string:
    C# Code:
    1. public class Line
    2. {
    3.     private Point _start;
    4.     private Point _end;
    5.  
    6.     public Point Start
    7.     {
    8.         get { return _start; }
    9.         set { _start = value; }
    10.     }
    11.  
    12.     public Point End
    13.     {
    14.         get { return _end; }
    15.         set { _end = value; }
    16.     }
    17.  
    18.     public Line(Point start, Point end)
    19.     {
    20.         this._start = start;
    21.         this._end = end;
    22.     }
    23. }
    24.  
    25.  
    26. public class DrawingObjectCollection
    27. {
    28.     private List<Line> _lines;
    29.  
    30.     public List<Line> Lines
    31.     {
    32.         get
    33.         {
    34.             if (this._lines == null)
    35.             {
    36.                 this._lines = new List<Line>();
    37.             }
    38.  
    39.             return _lines;
    40.         }
    41.         set { _lines = value; }
    42.     }
    43.  
    44.     private List<string> _strings;
    45.  
    46.     public List<string> Strings
    47.     {
    48.         get
    49.         {
    50.             if (this._strings == null)
    51.             {
    52.                 this._strings = new List<string>();
    53.             }
    54.  
    55.             return _strings;
    56.         }
    57.         set { _strings = value; }
    58.     }  
    59. }
    Now you Dictionary would be declared:
    C# Code:
    1. private Dictionary<PictureBox, DrawingObjectCollection> drawingObjectsByPictureBox = new Dictionary<PictureBox, DrawingObjectCollection>();
    In the Paint event handler you cast the sender as a PictureBox and get the corresponding DrawingObjectCollection from the Dictionary. You'd then loop through its Lines and Strings collections and draw each one. If you want to be able to draw other types of objects too then you'd need to add more properties to the DrawingObjectCollection class. For instance, if you wanted to draw rectangles too you'd add another property of type List<Rectangle>. The Framework already contains a Rectangle type so you don't need to declare that. If you wanted to draw circles then you'd have to declare a Circle type and add a List<Circle> property to the DrawingObjectCollection class, etc., etc.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  15. #15

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    Thanks for the class, I really do appreciate it. I was able to make your code work in my application.

    While it does allow me to draw multiple lines/strings, etc, I can only draw them at the same time. What I mean to say is, on a button click, x number of graphics are added to the picbox all at the same time.

    Is it possible to add more graphics at a later time? For example, on one button click, 2 lines and some text is added. Then, on another button click, 1 more line and more text is added.

    I'm not sure if this is possible. It seems like I'm starting to push the limits of using a dictionary to draw graphics.

  16. #16
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine

    There are no limits. The whole point of collections is that you can add and remove items at will. If you have a DrawingObjectCollection that corresponds to a particular PictureBox you can add a new Line to its Lines collection or a new string to its Strings collection at any time. Then when you repaint that PictureBox the new Line(s) and/or new string(s) will be drawn along with all the others. You can make this all as complex as you like.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  17. #17

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    Well, jmcilhinney, I can't thank you enough for your help. I'd offer to buy you a beer sometime, but I just realized you're located in Australia. If you ever make it to the states, let me know and I'll get you a brew.

  18. #18
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,221

    Re: Trouble with DrawLine

    Note that the example I provided is still relatively simple. You still have to provide the Pens, Brushes, Fonts and text locations elsewhere. Instead of your DrawObjectCollection class containing collections of simple Line and string objects you could make it contain collections of objects that contain all the required data to draw something, like the Pen, Font and location for a string or the Bruch for a Line. Like I said, you can extend this to whatever level of complexity you like.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  19. #19

    Thread Starter
    New Member
    Join Date
    Apr 2007
    Posts
    13

    Re: Trouble with DrawLine

    Yeah, the other code was already in place (pens, brushes, etc.) so DrawingObjectCollection dropped in pretty easy.

    You suggestion for extending the class to contain everything sounds like a good idea. If you hadn't guessed by now, the specifics on the text location, pen, font, and whatnot is extremely complex, and very dynamic, so I've been struggling with allowing the application to remain so flexible.

    At the moment, I have everything working great, so I'll save re-working the class for another time (maybe when I feel like a little programming pain ).

    Thanks again, JM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width