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.
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
|