Drydo's Blog

Teenager of the Internet

This blog hosted by:
http://blogs.vbcity.com
  Home :: Syndication  :: Login   Community Forums   :: vbCity.com   :: DevCity.NET  

I've seen three posts in the past week or so about this subject and thought I would post up my quick code sample for achieving the above.  The basic principal for hittesting on a non-rectangular shape, e.g. polygon, is to represent your shape within a graphicspath, then create a new Region based upon that GraphicsPath and invoke the 'IsVisible' property of the region, passing through the X and Y co-ordinates.

The following demonstrates this using the 'VBCity' logo (http://www.vbcity.com/images/vbcity.com.200x62.gif) and hittesting on the I and T of the logo.  The actual co-ordinates I took from placing the Logo into a graphics package and working out the pixel positions that way - as you will see I haven't used any curves, etc. but you get the point ;-)

The code is actually a custom control with a custom paint routine, mouseover handling and so forth.  Simply download the image to your C: drive, add the control to your project and run from there.

Code CopyHideScrollFull
Imports System.Drawing.Drawing2D

Public
Class RegionTest
Inherits System.Windows.Forms.Control

#Region " Windows Form Designer generated code "
'UserControl 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.

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

'

Me
.Name = "RegionTest"
Me
.Size = New System.Drawing.Size(40, 40)
End Sub

#End Region
Private _BGImage As Bitmap
Private
_Regions() As GraphicsPath
' Stores the currently hit graphicspath

Private
_RegionHit As GraphicsPath
Public Sub New()
' Base method
MyBase
.New()
'This call is required by the Windows Form Designer.

InitializeComponent()
' Set owner drawn parameters        
Me
.SetStyle( _
   ControlStyles.UserPaint Or _
   ControlStyles.AllPaintingInWmPaint Or _
   ControlStyles.SupportsTransparentBackColor, _
   True)
' Load background image
Me
._BGImage = Image.FromFile("C:\vbcity.com.200x62.gif")
' Set the size of this control to the dimensions of the image
Me
.Size = Me._BGImage.Size
' Generate array of points, derived from PaintShop Pro
Dim
Point_Letter_I() As Point = { _
New Point(72, 17), _
New
Point(85, 17), _
New
Point(85, 40), _
New
Point(77, 40), _
New
Point(77, 21), _
New
Point(72, 21), _
New
Point(72, 17) _
}
Dim Point_Letter_T() As Point = { _
New Point(110, 17), _
New
Point(118, 17), _
New
Point(124, 37), _
New
Point(129, 17), _
New
Point(137, 17), _
New
Point(126, 48), _
New
Point(120, 50), _
New
Point(109, 49), _
New
Point(111, 44), _
New
Point(118, 45), _
New
Point(123, 41), _
New
Point(119, 41), _
New
Point(110, 17) _
}
' Generate the appropriate graphics paths and add to our array
' Redim the array
ReDim
_Regions(1)
' Region for the letter 'I'
_Regions(0) = New GraphicsPath
' Add the appropriate lines and close

_Regions(0).AddLines(Point_Letter_I)
' Region for the letter 'T'
_Regions(1) = New GraphicsPath
' Add the appropriate lines and close

_Regions(1).AddLines(Point_Letter_T)
End Sub
' Used to paint the object
Protected
Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
' Call base method
MyBase
.OnPaint(e)
' Draw the BG
e.Graphics.DrawImage(Me._BGImage, New Point(0, 0))
' Has a region / graphics path been hit?  If so, color it...
If
Not Me._RegionHit Is Nothing Then
e.Graphics.FillPath(New SolidBrush(Color.FromArgb(255, 0, 0)), Me._RegionHit)
End If
End Sub
Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
' Call base method
MyBase
.OnMouseMove(e)
' Used to change the cursor
Dim
OverRegion As Boolean = False
' Are we over a region?

For
Each gp As GraphicsPath In Me._Regions
If New Region(gp).IsVisible(e.X, e.Y) Then
OverRegion = True
Exit
For
End If
Next
' Should we display the cursor?
If
OverRegion Then
Me.Cursor = Cursors.Hand
Else
Me.Cursor = Cursors.Default
End If
End Sub
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
' Call base method
MyBase
.OnMouseDown(e)
' Are we clicking over a graphics path?
For
Each gp As GraphicsPath In Me._Regions
If New Region(gp).IsVisible(e.X, e.Y) Then
' Set the hit region
Me
._RegionHit = gp
' Redraw the control

Me
.Invalidate()
Return
End If
Next
' Does the Region Hit still contain an object - if so, clear it and repaint
If
Not Me._RegionHit Is Nothing Then
Me._RegionHit = Nothing
Me
.Invalidate()
End If
End Sub

End
Class
. . .

Have fun and safe to say this isn't production code :-) M

posted on Friday, June 09, 2006 10:47 AM