Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

GraphicsPath.IsVisible

188 views
Skip to first unread message

Matt

unread,
Sep 19, 2003, 9:59:18 AM9/19/03
to
I am having a problem the IsVisible method. I've created a
simple project that draws a rectangle to the screen inside
of a picture box. The graphics.PageUnit has been changed
to inches. I want to detect when the mouse is over the
rectangle. I'm using the mouse over event to check whether
or not the graphicspath isvisible. I followed Bob Powell's
GDI+ FAQ (http://www.bobpowell.net/gdiplus_faq.htm) on
converting mouse coordinates to inches. However the
IsVisible method is only returning true when it was over a
small portion of the rectangle. When I call the IsVisible
method and pass one of the points (other than 0,0) it
returns false. I don't know what I'm doing wrong.

The code is posted below:

Private Sub Draw(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button3.Click
Dim g As Graphics
Dim sngScaleFactor As Single
g = PictureBox1.CreateGraphics()
g.Clear(PictureBox1.BackColor)

g.PageUnit = GraphicsUnit.Inch

Dim Points() As PointF = {New PointF(0, 0), New
PointF(CSng(TextBox3.Text), 0), New PointF(CSng
(TextBox3.Text), CSng(TextBox4.Text)), New PointF(0, CSng
(TextBox4.Text))}
path.Reset()
path.AddPolygon(Points)
g.DrawPath(New Pen(Color.Red, 1 / g.DpiX), path)

End Sub

Private Sub PictureBox1_MouseMove(ByVal sender As Object,
ByVal e As System.Windows.Forms.MouseEventArgs) Handles
PictureBox1.MouseMove
If path.IsVisible(e.X / PictureBox1.CreateGraphics
().DpiX, e.Y / PictureBox1.CreateGraphics.DpiY) Then

MsgBox("X - " & e.X.ToString & " Y - " &
e.Y.ToString)
End If
End Sub

Bob Powell [MVP]

unread,
Sep 19, 2003, 10:52:12 AM9/19/03
to
Ugh thats horrible!!!!

Please stop grabbing the graphics out of the PictureBox I know that old
style VB encouraged you to do such awful things but that was wrong and this
is wronger.

One tip I'll give you is that you don't need PictureBox at-all because
you're not even using a picture. Use a panel instead and then use proper
event-driven code to do all the drawing and the mouse handling for you.

I prepared the following application to demonstrate the right way to do it
but I find to my horror that IsVisible only works with integer numbers and
rounds floating point input!!

If I put in a rectangle height with some fraction of an inch larger than 0.5
the hit test is valid to the next nearest rounded inch. So much for
resolution independance eh?? This is either a definite bug or my ignorance
of VB which constantly springs idiotic surprises like this on me.

Code after my sig...

--
Bob Powell [MVP]
C#, System.Drawing

September's edition of Well Formed is now available.
http://www.bobpowell.net/currentissue.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm

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

Imports System.Drawing.Drawing2D

Public Class Form1

Inherits System.Windows.Forms.Form

Private pth As GraphicsPath

Private insquare As Boolean

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

Friend WithEvents TextBox1 As System.Windows.Forms.TextBox

Friend WithEvents TextBox2 As System.Windows.Forms.TextBox

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents Panel1 As System.Windows.Forms.Panel

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.TextBox1 = New System.Windows.Forms.TextBox

Me.TextBox2 = New System.Windows.Forms.TextBox

Me.Label1 = New System.Windows.Forms.Label

Me.Label2 = New System.Windows.Forms.Label

Me.Panel1 = New System.Windows.Forms.Panel

Me.SuspendLayout()

'

'TextBox1

'

Me.TextBox1.Location = New System.Drawing.Point(56, 24)

Me.TextBox1.Name = "TextBox1"

Me.TextBox1.Size = New System.Drawing.Size(64, 20)

Me.TextBox1.TabIndex = 0

Me.TextBox1.Text = "0.5"

'

'TextBox2

'

Me.TextBox2.Location = New System.Drawing.Point(56, 48)

Me.TextBox2.Name = "TextBox2"

Me.TextBox2.Size = New System.Drawing.Size(64, 20)

Me.TextBox2.TabIndex = 1

Me.TextBox2.Text = "1.2"

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(8, 24)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(48, 23)

Me.Label1.TabIndex = 2

Me.Label1.Text = "Width"

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(8, 48)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(48, 23)

Me.Label2.TabIndex = 2

Me.Label2.Text = "Height"

'

'Panel1

'

Me.Panel1.Location = New System.Drawing.Point(144, 8)

Me.Panel1.Name = "Panel1"

Me.Panel1.Size = New System.Drawing.Size(432, 248)

Me.Panel1.TabIndex = 3

'

'Form1

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(592, 266)

Me.Controls.Add(Me.Panel1)

Me.Controls.Add(Me.Label1)

Me.Controls.Add(Me.TextBox2)

Me.Controls.Add(Me.TextBox1)

Me.Controls.Add(Me.Label2)

Me.Name = "Form1"

Me.Text = "Form1"

Me.ResumeLayout(False)

End Sub

#End Region

Private Sub TextBox2_TextChanged(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles TextBox2.TextChanged

If Not Me.pth Is Nothing Then

Me.pth.Dispose()

Me.pth = Nothing

End If

Try

Me.pth = New GraphicsPath

pth.AddRectangle(New RectangleF(0.5F, 0.5F, Single.Parse(Me.TextBox1.Text),
Single.Parse(Me.TextBox2.Text)))

Me.Panel1.Invalidate()

Catch

Finally

Me.Panel1.Invalidate()

End Try

End Sub

Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles TextBox1.TextChanged

If Not Me.pth Is Nothing Then

Me.pth.Dispose()

Me.pth = Nothing

End If

Try

Me.pth = New GraphicsPath

pth.AddRectangle(New RectangleF(0.5F, 0.5F, CSng(Me.TextBox1.Text),
CSng(Me.TextBox2.Text)))

Me.Panel1.Invalidate()

Catch

Finally

Me.Panel1.Invalidate()

End Try

End Sub

Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As
System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint

If pth Is Nothing Then

Return

End If

e.Graphics.PageUnit = GraphicsUnit.Inch

If insquare Then

e.Graphics.FillPath(Brushes.Red, pth)

Else

e.Graphics.FillPath(Brushes.Green, pth)

End If

End Sub

Private Sub Panel1_MouseMove(ByVal sender As Object, ByVal e As
System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove

If pth Is Nothing Then

Return

End If

'the graphics for a panel is the same dpi as the form it sits on

Dim g As Graphics = CreateGraphics()

Dim dpix As Single = CSng(g.DpiX)

Dim dpiy As Single = CSng(g.DpiY)

g.Dispose() 'do that properly

Dim pf As New PointF(CSng(e.X) / dpix, CSng(e.Y) / dpiy)

System.Diagnostics.Trace.WriteLine(pf.ToString)

Dim oldone = Me.insquare

Me.insquare = Me.pth.IsVisible(pf)

If Me.insquare <> oldone Then

Me.Panel1.Invalidate()

End If

End Sub

End Class

"Matt" <matt_batcha@N0$PAMfenetech.com> wrote in message
news:143b01c37eb6$382fa4e0$a101...@phx.gbl...

Matt

unread,
Sep 19, 2003, 1:17:52 PM9/19/03
to
Bob,

First, thanks for you reply.

Could please explain why getting the graphics object is
not the correct way to do things or point me to an article
that I can read that explains it? I'm new to working with
GDI+. What I'm currently working is building a custom
user control that requires drawing to the screen.

Thanks Again....Matt

>.
>

Justin Weinberg

unread,
Sep 19, 2003, 1:49:42 PM9/19/03
to
You can get a Graphics object 3 primary ways in .NET.

1) Override OnPaint for your control/form/usercontrol (e.Graphics)
2) Handle the Paint event for a control such as a Panel per Bob's example
3) Use CreateGraphics() per Bob's example.

Different people will tell you different things about when to use what, and
you will find competing examples on MSDN. For example, a compact framework
game example I recently saw used CreateGraphics a lot, but most people
recommend you avoid CreateGraphics and use the OnPaint override and/or
handle paint events unless you have a good reason not to.

As you saw in Bob's example, make sure if you use CreateGraphics you dispose
the graphics object. If you override OnPaint, call the base class paint
unless you have a good reason not to. The rule of thumb is, if you find a
Dispose method in intellisense, and you created the object (it wasn't given
to you by an event handler, etc), you need to dispose it. You should also
dispose your GraphicsPath you are creating.

Given the same goal, people will have slight variations in their
implementations.

"Matt" <matt_batcha@N0$PAMfenetech.com> wrote in message

news:01c501c37ed1$f51255b0$a401...@phx.gbl...

Matt

unread,
Sep 19, 2003, 2:20:48 PM9/19/03
to
Justin,

Thanks for your reply.

I understand how to get the graphics object. The problem I
had earlier was getting the IsVisible method to work
correctly (ignoring the floating point rounding Bob
pointed out). In Bob's reply and example he said not to
use the picturebox's graphics object instead recommended
using the panel box. The code I displayed earlier was a
small test app that I had created to play around with
changing the page unit to inches. I'm modifying a custom
user control which draws to the screen. Currently it uses
pixels but I will be adding complexity and would prefer to
do everything inches. When I draw a 1x2 inch rectangle I
only "get a hit" when I'm over a small portion of the user
control.

Anyway I don't know if I'll even be able to stay with
using the page units as inches since the isvisible does
not correctly work with floating point numbers.

>.
>

Bob Powell [MVP]

unread,
Sep 19, 2003, 4:07:59 PM9/19/03
to
> Could please explain why getting the graphics object is
> not the correct way to do things or point me to an article

Matt,

Windows is an event driven system in which things happen in a strict order
in response to the event stimulus. For an object to draw itself outside of
that synchronized loop is bad enough but when you go in through a back-door,
steal the graphics of and rudely paint on some other control outside of the
WM_PAINT event then you open yourself up for problems. Specifically, the
bits you paint are only good until the next Paint event comes along. You
have no way of determinig in advance when or why the paint event will be
kicked off so you'll never reliably be able to display a graphic and have it
remain in place for a known period (ie as long as you need it). The biggest
complaint of programmers who use this dubious technoque is "I drew a bunch
of stuff on my PictureBox and every time a window moves in front of it it
all disappears". PictureBox bye the way is the most misused control in the
known universe and is almost totally unecessary.

The *only* time you should paint on the screen is when WM_PAINT fires and
the events kick off from that. You can override the OnPaint protected method
if you want to do it properly or you can handle the Paint event. In both
cases, the Graphics to paint on is handed to you in an event argument and
you should *never* need to use CreateGraphics inside a drawing loop.

In my example, I got the Graphics of the control, not to paint upon the
screen but to get some information that I knew would be provided. No
painting was done outside of the Paint event and the graphics was disposed
of immediately.

Following the event driven architecture strictly is perhaps a little more
difficult and requires more discipline than just banging away in what seems
to be a logical manner, after all when you click the mouse you want to draw
something.. yes?.. Well actually no! The answer really is that you set up
conditions within your program so that when the Paint event comes along, you
draw what happened during the click.

The first edition of Well Formed has a complete drawing package that
illustrates how to perform mouse event management and drawing in accordance
with the Paint event. http://www.bobpowell.net/August2003.htm

Sorry to bang on about this but so many people cause themselves pain that
they don't need and then come here and ask questions about it.

Bob.

--
Bob Powell [MVP]
C#, System.Drawing

September's edition of Well Formed is now available.
http://www.bobpowell.net/currentissue.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm

"Matt" <matt_batcha@N0$PAMfenetech.com> wrote in message
news:01c501c37ed1$f51255b0$a401...@phx.gbl...

Bob Powell [MVP]

unread,
Sep 19, 2003, 4:23:28 PM9/19/03
to
In my example, if you keep the values to exact inches, ie 1*2 it'll hit it
every time.

I need to investigate that rounding thing. It'll give me sleepless nights
now.

--
Bob Powell [MVP]
C#, System.Drawing

September's edition of Well Formed is now available.
http://www.bobpowell.net/currentissue.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm

"Matt" <matt_batcha@N0$PAMfenetech.com> wrote in message
news:0f5201c37eda$bfbe0ea0$a001...@phx.gbl...

Matt

unread,
Sep 19, 2003, 4:39:24 PM9/19/03
to
Bob,

Thanks for the explanation. I will be subscribing to Well
Formed.

Have a good weekend.

Matt

>.
>

Paul Hetherington

unread,
Sep 19, 2003, 7:17:24 PM9/19/03
to
Hi Bob,
I have also found the same behaviour with the .IsVisible rounding to a
integer.
I am using Pixels as my pageunit so the behavior is only noticable when I
zoom in to a significant amount.
If you ever figure out away around this I wouldn't mind hearing about it :)
I have also found that if I use the Path.Widen method The .IsVisble only
seems to work on the Boundary of the path. It no longer seems to detect the
interior. Annoying but I can work with it.

Cheers,
Paul


"Bob Powell [MVP]" <bob@_spamkiller_bobpowell.net> wrote in message
news:OTQpU2rf...@TK2MSFTNGP12.phx.gbl...

Bob Powell [MVP]

unread,
Sep 20, 2003, 4:28:56 AM9/20/03
to
> I have also found that if I use the Path.Widen method The .IsVisble only
> seems to work on the Boundary of the path.

Widen takes a boundary and effectively strokes it with a pen making a region
with voids where the interior used to be and a band around the outside the
same width as the pen including any end-caps or miters the pen has.

If you want to widen a path and detect the middle of it you need to create a
region from the original, perform a widen, greate a region from the widened
path then a union to merge the two regions before finally hit-testing the
result with Region.IsVisible.

--
Bob Powell [MVP]
C#, System.Drawing

September's edition of Well Formed is now available.
http://www.bobpowell.net/currentissue.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/gdiplus_faq.htm

"Paul Hetherington" <phethe...@visualstatement.com> wrote in message
news:OYyvsQw...@tk2msftngp13.phx.gbl...

Paul Hetherington

unread,
Sep 22, 2003, 7:01:11 PM9/22/03
to
Yeah,
I am essentially doing the same thing already.
I am hit testing the original path. If that fails then I hit test the
widened path.
This allows me to hit test small paths with a tolerance around them :)

It is still annoying that the .IsVisible takes two singles but only seems to
work to the nearest integer regardless.
Cheers,
Paul

"Bob Powell [MVP]" <bob@_spamkiller_bobpowell.net> wrote in message

news:%23PpLrE1...@TK2MSFTNGP10.phx.gbl...

0 new messages