-
Jul 10th, 2008, 09:03 PM
#1
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:
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim ctl As Control = Me.GetNextControl(Me, True)
Do Until ctl Is Nothing
AddHandler ctl.Paint, AddressOf Form1_Paint
ctl = Me.GetNextControl(ctl, True)
Loop
End Sub
Private Sub Form1_Paint(ByVal sender As Object, _
ByVal e As PaintEventArgs) Handles Me.Paint
'Get the points relative to the form.
Dim start As New Point(0, 0)
Dim [end] As New Point(Me.ClientSize)
Dim paintee As Control = DirectCast(sender, Control)
'Translate the points relative to the control being painted.
start = paintee.PointToClient(Me.PointToScreen(start))
[end] = paintee.PointToClient(Me.PointToScreen([end]))
e.Graphics.DrawLine(Pens.Black, start, [end])
End Sub
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.
Last edited by jmcilhinney; Mar 14th, 2023 at 01:23 AM.
-
Nov 27th, 2017, 02:16 AM
#2
New Member
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.
-
Nov 27th, 2017, 02:59 AM
#3
Re: Draw Common "Picture" on All Controls
Originally Posted by Loeli
What is the first private sub there for?
It's explained in the post.
Originally Posted by jmcilhinney
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.
Originally Posted by Loeli
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.
Last edited by jmcilhinney; Mar 14th, 2023 at 01:26 AM.
-
Nov 27th, 2017, 03:51 AM
#4
New Member
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?
-
Nov 27th, 2017, 04:42 AM
#5
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:
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'The following code allows the Buttons to be positioned precisely in the designer and then parented by the PictureBox at run time. For Each btn In {Button1, Button2, Button3, Button4} btn.Location = PictureBox1.PointToClient(btn.PointToScreen(Point.Empty)) btn.Parent = PictureBox1 Next AddHandler PictureBox1.Paint, AddressOf Controls_Paint AddHandler Button1.Paint, AddressOf Controls_Paint AddHandler Button2.Paint, AddressOf Controls_Paint AddHandler Button3.Paint, AddressOf Controls_Paint AddHandler Button4.Paint, AddressOf Controls_Paint End Sub Private Sub Controls_Paint(sender As Object, e As PaintEventArgs) 'Get the points relative to the form. Dim start As New Point(0, 0) Dim [end] As New Point(Me.ClientSize) 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(Pens.Black, start, [end]) End Sub End Class
vb.net Code:
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 'The following code allows the Buttons to be positioned precisely in the designer and then parented by the PictureBox at run time. For Each btn In {Button1, Button2, Button3, Button4} btn.Location = PictureBox1.PointToClient(btn.PointToScreen(Point.Empty)) btn.Parent = PictureBox1 Next End Sub Private Sub Controls_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint, Button1.Paint, Button2.Paint, Button3.Paint, Button4.Paint 'Get the points relative to the form. Dim start As New Point(0, 0) Dim [end] As New Point(Me.ClientSize) 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(Pens.Black, start, [end]) End Sub 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.
-
Nov 27th, 2017, 08:57 AM
#6
New Member
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.
-
Nov 27th, 2017, 09:13 PM
#7
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.
-
Jun 19th, 2019, 09:58 AM
#8
Member
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
Re-drawn
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|