Results 1 to 8 of 8

Thread: Draw Common "Picture" on All Controls

  1. #1

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,301

    Draw Common "Picture" on All Controls

    C# version here.

    The following code uses a single Paint event handler to draw the same "picture" on every control on a form. Specifically it draws a line from the top, left corner of the form to the bottom, right corner and "over" any controls that happen to be on that diagonal.
    VB.NET Code:
    1. Public Class Form1
    2.  
    3.     Private Sub Form1_Load(ByVal sender As System.Object, _
    4.                            ByVal e As System.EventArgs) Handles MyBase.Load
    5.         Dim ctl As Control = Me.GetNextControl(Me, True)
    6.  
    7.         Do Until ctl Is Nothing
    8.             AddHandler ctl.Paint, AddressOf Form1_Paint
    9.             ctl = Me.GetNextControl(ctl, True)
    10.         Loop
    11.     End Sub
    12.  
    13.     Private Sub Form1_Paint(ByVal sender As Object, _
    14.                             ByVal e As PaintEventArgs) Handles Me.Paint
    15.         'Get the points relative to the form.
    16.         Dim start As New Point(0, 0)
    17.         Dim [end] As New Point(Me.ClientSize)
    18.  
    19.         Dim paintee As Control = DirectCast(sender, Control)
    20.  
    21.         'Translate the points relative to the control being painted.
    22.         start = paintee.PointToClient(Me.PointToScreen(start))
    23.         [end] = paintee.PointToClient(Me.PointToScreen([end]))
    24.  
    25.         e.Graphics.DrawLine(Pens.Black, start, [end])
    26.     End Sub
    27.  
    28. End Class
    You can use a similar pattern to draw any "picture" you like on your form. The important part is the use of PointToScreen and PointToClient to convert coordinates from the form's frame of reference to that of the control currently being painted.

    Note also that you can attach the event handler to your controls any way you like. The code I've included in the Load event handler there means that any controls you add in the designer will be included. Alternatively you could include the other controls in the method's Handles clause along with the form. You can also use the AddHandler statement to include controls added at run time.

  2. #2
    New Member
    Join Date
    Nov 2017
    Posts
    3

    Re: Draw Common "Picture" on All Controls

    Hi jmcilhinney,
    could you please explain the code? I want to have a picturebox and then on button click I want the programm to draw a contour. When the users clicks on a different button now the programm should dynamically create buttons over the whole picturebox with the contour still being visible. When I use your second private sub I can see the contour. What is the first private sub there for? When I just use 4 buttons and let the programm do single steps I can see that the second private sub is being repeated exactly 4 times but the sender is always the picturebox. I really don't get it why the sender is the picturebox but the line is drawn on the buttons. Thanks in advance.

  3. #3

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,301

    Re: Draw Common "Picture" on All Controls

    Quote Originally Posted by Loeli View Post
    What is the first private sub there for?
    It's explained in the post.
    Quote Originally Posted by jmcilhinney View Post
    The code I've included in the Load event handler there means that any controls you add in the designer will be included. Alternatively you could include the other controls in the method's Handles clause along with the form. You can also use the AddHandler statement to include controls added at run time.
    The AddHandler statement adds a handler for an event. That code loops through all the controls on the form so it adds a handler for the Paint event of every control on the form.
    Quote Originally Posted by Loeli View Post
    When I just use 4 buttons and let the programm do single steps I can see that the second private sub is being repeated exactly 4 times but the sender is always the picturebox. I really don't get it why the sender is the picturebox but the line is drawn on the buttons.
    The point of the code is to handle the Paint event of every control and, in the event handler, draw on the control that raised the event. If you're only handling the Paint event of the PictureBox then you're only drawing on the PictureBox. If you're drawing on the Buttons then you're handling the Paint event of the Buttons. If you're not seeing that then there's something wrong with the way you're debugging. Try adding a line like this to your event handler:
    Code:
    Debug.WriteLine(String.Format("In event handler for {0}.Paint", paintee.Name))
    Watch the Output window and you should see that you're handling the event for each of the Buttons as well as the PictureBox.

  4. #4
    New Member
    Join Date
    Nov 2017
    Posts
    3

    Re: Draw Common "Picture" on All Controls

    So I changed the code a little bit because I want the lines to be drawn on the buttons and the buttons are childs of the picturebox.

    Code:
    Private Sub Pciturebox1_Paint(ByVal sender As Object, _
    
    14.                            ByVal e As PaintEventArgs) Handles PictureBox1.Paint
    
    15.        'Get the points relative to the form.
    
    16.        Dim start As New Point(0, 0)
    
    17.        Dim [end] As New Point(PictureBox1.ClientSize)
    
    18. 
    
    19.        Dim paintee As Control = DirectCast(sender, Control)
    
    20. 
    
    21.        'Translate the points relative to the control being painted.
    
    22.        start = paintee.PointToClient(PictureBox1.PointToScreen(start))
    
    23.        [end] = paintee.PointToClient(PictureBox1.PointToScreen([end]))
    
    24. 
    
    25.        e.Graphics.DrawLine(Pens.Black, start, [end])
    
    26.        Debug.WriteLine(String.Format("In event handler for {0}.Paint", paintee.Name))
    
    27.    End Sub
    The AddHandler statement adds a handler for an event. That code loops through all the controls on the form so it adds a handler for the Paint event of every control on the form.
    So every control has this Paint event? The Paint event means that for example when the window size is changed everything in the second private sub is going to be loaded, right? But why is it needed that every single control has this Pain event? If I would change the window size wouldn't it be enough that only the second private sub is called and all the single controls on the form don't have a handler?



    The point of the code is to handle the Paint event of every control and, in the event handler, draw on the control that raised the event. If you're only handling the Paint event of the PictureBox then you're only drawing on the PictureBox. If you're drawing on the Buttons then you handling the Paint event of the Buttons.
    So when I start the code and do the single steps with button F8 I'm always getting the output "In event handler for PictureBox1.Paint". I'm getting this 4 times while the seconde private sub is repeated 4 times. And this is when I have 4 buttons which are child of the picturebox1.

    Is there a possibility to show the drawn line only when a button is clicked and not immediately when the Form1 is loaded?

  5. #5

    Thread Starter
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    110,301

    Re: Draw Common "Picture" on All Controls

    Paint is an event and you handle it the same way you do any other event. In this particular case I added a Handles clause to the method and handled the Paint event of the form that way, then used a loop and AddHandler to handle the Paint event of every control on the form. That's not really relevant to the point. You can write an AddHandler statement for each control explicitly if you want or you can add all the controls to the Handles clause or any combination thereof. It doesn't matter how you register the event handler, only that you do it for each control that you want to draw on I just tested both the examples below and they both worked exactly as expected:
    vb.net Code:
    1. Public Class Form1
    2.  
    3.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4.         'The following code allows the Buttons to be positioned precisely in the designer and then parented by the PictureBox at run time.
    5.         For Each btn In {Button1, Button2, Button3, Button4}
    6.             btn.Location = PictureBox1.PointToClient(btn.PointToScreen(Point.Empty))
    7.             btn.Parent = PictureBox1
    8.         Next
    9.  
    10.         AddHandler PictureBox1.Paint, AddressOf Controls_Paint
    11.         AddHandler Button1.Paint, AddressOf Controls_Paint
    12.         AddHandler Button2.Paint, AddressOf Controls_Paint
    13.         AddHandler Button3.Paint, AddressOf Controls_Paint
    14.         AddHandler Button4.Paint, AddressOf Controls_Paint
    15.     End Sub
    16.  
    17.     Private Sub Controls_Paint(sender As Object, e As PaintEventArgs)
    18.         'Get the points relative to the form.
    19.         Dim start As New Point(0, 0)
    20.         Dim [end] As New Point(Me.ClientSize)
    21.  
    22.         Dim paintee As Control = DirectCast(sender, Control)
    23.         Debug.WriteLine(String.Format("In event handler for {0}.Paint", paintee.Name))
    24.  
    25.         'Translate the points relative to the control being painted.
    26.         start = paintee.PointToClient(Me.PointToScreen(start))
    27.         [end] = paintee.PointToClient(Me.PointToScreen([end]))
    28.  
    29.         e.Graphics.DrawLine(Pens.Black, start, [end])
    30.     End Sub
    31.  
    32. End Class
    vb.net Code:
    1. Public Class Form1
    2.  
    3.     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4.         'The following code allows the Buttons to be positioned precisely in the designer and then parented by the PictureBox at run time.
    5.         For Each btn In {Button1, Button2, Button3, Button4}
    6.             btn.Location = PictureBox1.PointToClient(btn.PointToScreen(Point.Empty))
    7.             btn.Parent = PictureBox1
    8.         Next
    9.     End Sub
    10.  
    11.     Private Sub Controls_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint,
    12.                                                                               Button1.Paint,
    13.                                                                               Button2.Paint,
    14.                                                                               Button3.Paint,
    15.                                                                               Button4.Paint
    16.         'Get the points relative to the form.
    17.         Dim start As New Point(0, 0)
    18.         Dim [end] As New Point(Me.ClientSize)
    19.  
    20.         Dim paintee As Control = DirectCast(sender, Control)
    21.         Debug.WriteLine(String.Format("In event handler for {0}.Paint", paintee.Name))
    22.  
    23.         'Translate the points relative to the control being painted.
    24.         start = paintee.PointToClient(Me.PointToScreen(start))
    25.         [end] = paintee.PointToClient(Me.PointToScreen([end]))
    26.  
    27.         e.Graphics.DrawLine(Pens.Black, start, [end])
    28.     End Sub
    29.  
    30. End Class
    The Paint event is a member of the Control class. That means that every control, which includes forms, has a Paint event. You don't have to handle the event for every control but the event handler for a control is where you draw on that control. If you handle the event for the form but none of the control on the form then you can only draw on the form. Given that each control is drawn over the form, it will hide any drawing you do on the form. That's the whole point of this thread: to be able to draw over every control on the form. If you could draw on the Buttons without handling the Paint event of the Buttons then why would I have told you to handle the Paint event of the Buttons?

    There are basically two ways to render something on screen. You can display something and have it stay there until it gets changed or you can display everything you want each time there's a change. Windows Forms, by design, uses the second option. That means that, if you want to do custom drawing on a control, you have to handle that control's Paint event and provide code to draw what you want drawn every time the control is painted. Generally speaking, you declare one or more variables to store the data that represents what you want drawn and then get the data from those variables in the Paint event handler. If you want to change the drawing, you change the data stored in those variables and then force a repaint, which you can do by calling Invalidate on the control you want repainted. When a control is repainted, all its children will be repainted too. If you only want to draw something after a Button is clicked then you would declare a Boolean variable that tells you whether to draw or not. In the Click event of the Button, you'd toggle that variable and then force a repaint. In the Paint event handler, you'd test that variable and only draw if it was set appropriately.

  6. #6
    New Member
    Join Date
    Nov 2017
    Posts
    3

    Re: Draw Common "Picture" on All Controls

    But why does it work then with only this code:

    Code:
    Private Sub Picturebox1_Paint(ByVal sender As Object, _
    
                                ByVal e As PaintEventArgs) Handles PictureBox1.Paint
    
           'Get the points relative to the form.
    
            Dim start As New Point(0, 0)
    
            Dim [end] As New Point(PictureBox1.ClientSize)
    
    
           e.Graphics.DrawLine(Pens.Black, start, [end])
    
           End Sub
    Where is the code which is drawing on all my buttons? There is nowhere a sign of the buttons.

    Btw: Thanks for all the explanations. I'm a bit new to all this programming.

  7. #7
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Draw Common "Picture" on All Controls

    That same Paint event handler was specified for all the controls, so it is called for all the controls, not just Picturebox1.
    The "sender" is different for each of the calls you're seeing. And the object "e.Graphics" draws on sender, so when the sender is the Picturebox it draws on the picturebox, and when the sender is one of the buttons, it is drawing on the button.

  8. #8
    Member
    Join Date
    May 2009
    Posts
    54

    Re: Draw Common "Picture" on All Controls

    Hi,
    Thanks for this code it does just what I wanted ....... well nearly, this draws lines across some labels inside a panel fine but I then re-scale the positions of the labels inside the panel and move the line I drew, which indicates "Today" but when I redraw the line it re-draws fine on the form background and the labels inside the panel that move but it stays in the same place inside the panel, it doesn't re-draw like everything else.

    Basically the line doesn't seem to want to be re-drawn on the Panel control as it is on everything else.

    Any ideas why?
    Code
    Code:
            Dim RedPen As New Pen(Color.Red, 5.0F)
            Dim start As New Point(kwNow - 13, Panel1.Bottom + 6)
            Dim [end] As New Point(kwNow - 13, Panel1.Top - 10)
            Dim paintee As Control = DirectCast(sender, Control)
            'Debug.WriteLine(String.Format("In event handler for {0}.Paint", paintee.Name))
            'Translate the points relative to the control being painted.
            start = paintee.PointToClient(Me.PointToScreen(start))
            [end] = paintee.PointToClient(Me.PointToScreen([end]))
            e.Graphics.DrawLine(RedPen, start, [end])
            e.Graphics.ResetTransform()
    When the panel scale is changed this code will be re run to re-draw the line

    First drawn
    Name:  FirstRun.jpg
Views: 1261
Size:  15.2 KB
    Re-drawn
    Name:  re-ran.jpg
Views: 1209
Size:  13.8 KB

    Thanks,
    Phil
    Last edited by gadjet; Jun 19th, 2019 at 09:58 AM. Reason: added name

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