none
add crosshairs or popup of x,y coordinates on mouse hover for this graph control?

    Question

  • This control takes in an array and plots a bar chart type 'count' along the Y axis for a corresponding X location. A user has requested to be able to mouse hover over any location on the graph and have it display the X and Y coordinates.  How would I do that? Thank you!

    Option Strict On
    Option Explicit On
    
    Imports System.Windows.Forms
    Imports System.ComponentModel
    Imports System.Globalization
    
    <AttributeUsageAttribute(AttributeTargets.All)>
    Public Class AlarmCountGraph
      Private mAlarmCountData(215) As Integer 'X Values Limit of Graph
      Private mYMin As Integer = 0
      Private mYMax As Integer = 100      'Y Values Limit of Graph
    
      Private TextBrush As New SolidBrush(Color.Black)
      Private PanelMain As New PictureBox
    
      Private gap As Integer = 5
      Private mcolorPanelMain As System.Drawing.Color = Color.Transparent
      Public mTextGraphTitle As String = "<Alarm History - Alarm Count Graph>"
      Public mTextXaxisTitle As String = "<Distance [cells]>"
      Public mTextYaxisTitle As String = "<Alarms [count]>"
    
      <Description("Threshold Data, 216 points"), Category("Control Specific Data/Flags")> _
      Public Property AlarmCountData() As Integer()
        Get
          AlarmCountData = mAlarmCountData
        End Get
        Set(ByVal value As Integer())
          Try
            mAlarmCountData = value
            Refresh()
          Catch ex As Exception
            appendLog("AlarmCountData exception = " + ex.ToString())
          End Try
        End Set
      End Property
    
     Public Sub New()
        InitializeComponent()
        DoubleBuffered = True
      End Sub
    
      Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
    
          Dim X, Y, i, k As Integer
          Dim X1, Y1, X2, Y2 As Integer
          Dim TickSize As Integer = 5
    
          Dim mPen_I As New Pen(mColorBarGraph, 2)
    
          Dim HorzUnits() As Integer
          Dim VertUnits() As Integer
    
          Dim Xaxis_IncrementPtr As Integer = 0
          Dim XAxis_Increment() As Integer = {1, 2, 5, 10, 25, 50, 100}
          Dim Xwidths(UBound(XAxis_Increment)) As Integer
          Dim Yaxis_IncrementPtr As Integer = 0
          Dim YAxis_Increment() As Integer = {1, 2, 5, 10, 25, 100}
          Dim YHeight(UBound(YAxis_Increment)) As Integer
    
          Dim LeftMargin, TopMargin, GraphWidth, GraphHeight As Integer
    
          Dim BlackPen As New Pen(Color.Black)
          Dim GridPen As New Pen(Color.Black)
          PanelMain.BackColor = mcolorPanelMain
          PanelMain.Width = Me.Width
          PanelMain.Height = Me.Height
    
          If UBound(AlarmCountData) > 3 And UBound(AlarmCountData) < 256 Then
            '=======================================================================================================
            'Check all Data to ensure it is "In-Bounds" 0-96
            '=======================================================================================================
            For i = 0 To UBound(AlarmCountData)
              If AlarmCountData(i) > 96 Then
                AlarmCountData(i) = 96
              End If
              If AlarmCountData(i) < 0 Then AlarmCountData(i) = 0
            Next i
            '=======================================================================================================
            'Determine working graph area
            '=======================================================================================================
            'Determine the LeftMargin of the graph
            If Int(e.Graphics.MeasureString(mYMax.ToString, Me.Font).Width) > Int(e.Graphics.MeasureString(mYMin.ToString, Me.Font).Width) Then
              LeftMargin = CInt(gap + Int(e.Graphics.MeasureString(mTextYaxisTitle, Me.Font).Height) + gap + Int(e.Graphics.MeasureString(Int(mYMax * 100 / 128).ToString, Me.Font).Width) + gap + TickSize)
            Else
              LeftMargin = CInt(gap + Int(e.Graphics.MeasureString(mTextYaxisTitle, Me.Font).Height) + gap + Int(e.Graphics.MeasureString(Int(mYMin * 100 / 128).ToString, Me.Font).Width) + gap + TickSize)
            End If
            'Determine the GraphWidth
            GraphWidth = CInt(Me.ClientSize.Width - LeftMargin - gap - Int(e.Graphics.MeasureString(UBound(mAlarmCountData).ToString, Me.Font).Width))
            'Determine the TopMargin
            TopMargin = CInt(gap + Int(e.Graphics.MeasureString(mTextGraphTitle, Me.Font).Height) + gap + gap)
            'Determine the GraphHeight
            GraphHeight = CInt(Me.ClientSize.Height - TopMargin - Int(e.Graphics.MeasureString(mTextXaxisTitle, Me.Font).Height) - gap - Int(e.Graphics.MeasureString(UBound(mAlarmCountData).ToString, Me.Font).Height) - gap - TickSize - gap - gap - gap)
            '=======================================================================================================
            'Locate & Display the Main Title 
            '=======================================================================================================
            X = CInt(Int(Me.ClientSize.Width / 2) - Int(e.Graphics.MeasureString(mTextGraphTitle, Me.Font).Width / 2))
            Y = gap
            e.Graphics.DrawString(mTextGraphTitle, Me.Font, TextBrush, X, Y)
            '=======================================================================================================
            'Locate & Display Y axis Title - rotate 90 degrees
            '=======================================================================================================
            X = gap
            Y = CInt(TopMargin + GraphHeight / 2 + e.Graphics.MeasureString(mTextYaxisTitle, Me.Font).Width / 2)
            e.Graphics.RotateTransform(CInt(-90))
            e.Graphics.DrawString(mTextYaxisTitle, Me.Font, TextBrush, -Y, X)
            e.Graphics.ResetTransform()
            '=======================================================================================================
            'Locate & Display the X Axis Title 
            '=======================================================================================================
            X = CInt(LeftMargin + Int(GraphWidth / 2) - Int(e.Graphics.MeasureString(mTextXaxisTitle, Me.Font).Width / 2))
            Y = CInt(TopMargin + GraphHeight + TickSize + gap + Int(e.Graphics.MeasureString("XaxisLabel", Me.Font).Height) + gap)
            e.Graphics.DrawString(mTextXaxisTitle, Me.Font, TextBrush, X, Y)
            '=======================================================================================================
            'Translate the grid - X
            '=======================================================================================================
            ReDim HorzUnits(UBound(mAlarmCountData) + 2)
            For i = 0 To UBound(HorzUnits)
              HorzUnits(i) = CInt(Int(i / (UBound(mAlarmCountData) + 2) * (GraphWidth - 1)))
            Next i
            'X Axis - Find the best Grid Increment
            For i = 0 To UBound(XAxis_Increment)
              Xwidths(i) = 0
              For k = 0 To UBound(mAlarmCountData)
                If k / XAxis_Increment(i) = Int(k / XAxis_Increment(i)) Then
                  Xwidths(i) = CInt(Xwidths(i) + Int(e.Graphics.MeasureString(k.ToString, Me.Font).Width) + 2)
                End If
              Next k
            Next i
            Xaxis_IncrementPtr = 0
            While GraphWidth < Xwidths(Xaxis_IncrementPtr) And Xaxis_IncrementPtr < UBound(XAxis_Increment)
              Xaxis_IncrementPtr = Xaxis_IncrementPtr + 1
            End While
            For i = 0 To UBound(mAlarmCountData) + 2
              'Tick Marks
              If i / XAxis_Increment(Xaxis_IncrementPtr) = Int(i / XAxis_Increment(Xaxis_IncrementPtr)) Then
                X1 = LeftMargin + HorzUnits(i) : Y1 = TopMargin + GraphHeight : X2 = LeftMargin + HorzUnits(i) : Y2 = TopMargin + GraphHeight + TickSize
                e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
              End If
              'Grid Labels, X-Axis
              If i / XAxis_Increment(Xaxis_IncrementPtr) = Int(i / XAxis_Increment(Xaxis_IncrementPtr)) Then
                X = CInt(LeftMargin + HorzUnits(i) - Int(e.Graphics.MeasureString(i.ToString, Me.Font).Width / 2))
                Y = TopMargin + GraphHeight + TickSize + gap
                e.Graphics.DrawString(i.ToString, Me.Font, TextBrush, X, Y)
              End If
            Next i
            '=======================================================================================================
            'Translate the grid - Y
            '=======================================================================================================
            ReDim VertUnits(mYMax - mYMin + 1)
            For i = 0 To UBound(VertUnits)
              VertUnits(i) = CInt((GraphHeight - 1) - Int(i / (mYMax - mYMin) * (GraphHeight - 1)))
            Next i
            'Y Axis - Find the best Grid Increment
            For i = 0 To UBound(YAxis_Increment)
              YHeight(i) = 0
              For k = mYMin To mYMax
                If Int(k) / YAxis_Increment(i) = Int(Int(k) / YAxis_Increment(i)) Then
                  YHeight(i) = CInt(YHeight(i) + Int(e.Graphics.MeasureString(k.ToString, Me.Font).Height) + 2)
                End If
              Next k
            Next i
            Yaxis_IncrementPtr = 0
            While GraphHeight < YHeight(Yaxis_IncrementPtr) And Yaxis_IncrementPtr < UBound(YAxis_Increment)
              Yaxis_IncrementPtr = Yaxis_IncrementPtr + 1
            End While
            For i = mYMin To mYMax
              'Y-Axis Tick Marks
              If Int(i) / YAxis_Increment(Yaxis_IncrementPtr) = Int(Int(i) / YAxis_Increment(Yaxis_IncrementPtr)) Then
                X1 = LeftMargin - TickSize : Y1 = TopMargin + VertUnits(i - mYMin) : X2 = LeftMargin : Y2 = TopMargin + VertUnits(i - mYMin)
                e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
              End If
              'Grid Labels, Y-Axis
              If (i) / YAxis_Increment(Yaxis_IncrementPtr) = Int((i) / YAxis_Increment(Yaxis_IncrementPtr)) Then
                X = CInt(LeftMargin - TickSize - gap - Int(e.Graphics.MeasureString((i).ToString, Me.Font).Width))
                Y = CInt(TopMargin + VertUnits(i - mYMin) - Int(e.Graphics.MeasureString(mYMax.ToString, Me.Font).Height / 2))
                e.Graphics.DrawString(Int(i).ToString, Me.Font, TextBrush, X, Y)
              End If
            Next i
            '=======================================================================================================
            'Draw the Vertical Grid lines
            '=======================================================================================================
            For i = 0 To UBound(mAlarmCountData) + 1
              If i / XAxis_Increment(Xaxis_IncrementPtr) = Int(i / XAxis_Increment(Xaxis_IncrementPtr)) Then
                X1 = LeftMargin + HorzUnits(i) : Y1 = TopMargin : X2 = LeftMargin + HorzUnits(i) : Y2 = TopMargin + GraphHeight
                e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
              End If
            Next i
            'Draw the Right most Grid line
            X1 = LeftMargin + GraphWidth - 1 : Y1 = TopMargin : X2 = LeftMargin + GraphWidth - 1 : Y2 = TopMargin + GraphHeight - 1
            e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
            '=======================================================================================================
            'Draw the Horizontal Grid lines
            '=======================================================================================================
            For i = mYMin To mYMax
              If Int(i) / YAxis_Increment(Yaxis_IncrementPtr) = Int(Int(i) / YAxis_Increment(Yaxis_IncrementPtr)) Then
                X1 = LeftMargin : Y1 = TopMargin + VertUnits(i - mYMin) : X2 = LeftMargin + (GraphWidth - 1) : Y2 = TopMargin + VertUnits(i - mYMin)
                e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
              End If
            Next i
            'Draw the Top most Grid line
            X1 = LeftMargin : Y1 = TopMargin : X2 = LeftMargin + (GraphWidth - 1) : Y2 = TopMargin
            e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
            'Draw the Bottom most Grid line
            X1 = LeftMargin : Y1 = TopMargin + (GraphHeight - 1) : X2 = LeftMargin + (GraphWidth - 1) : Y2 = TopMargin + (GraphHeight - 1)
            e.Graphics.DrawLine(GridPen, X1, Y1, X2, Y2)
            '=======================================================================================================
            'Draw the Target_in Bar Trace
            '=======================================================================================================
            Dim xt, yt, wt, ht As Integer
            For i = 0 To UBound(mAlarmCountData)
              xt = LeftMargin + CInt(HorzUnits(i + 1) - ((HorzUnits(i + 1) - HorzUnits(i)) / 2))
              wt = CInt(((HorzUnits(i + 2) - HorzUnits(i + 1)) / 2 + (HorzUnits(i + 1) - HorzUnits(i)) / 2) + 0.5)
              yt = TopMargin + VertUnits(mAlarmCountData(i)) + 1
              ht = VertUnits(0) - VertUnits(mAlarmCountData(i)) - 1
              Dim rect3 As New Rectangle(xt, yt, wt, ht)
              Dim tBurshColor As Color = mColorBarGraph
              Dim OpaqueBursh As New SolidBrush(tBurshColor)
              e.Graphics.FillRectangle(OpaqueBursh, rect3)
            Next i
    
    
          End If
      End Sub
    
    
      <Description("Text associated with the Graph Title"), _
      Category("Control Specific Graphics Text")> _
      Public Property TextGraphTitle() As String
        Get
          TextGraphTitle = mTextGraphTitle
        End Get
        Set(ByVal value As String)
          mTextGraphTitle = value
          PanelMain.Refresh()
        End Set
      End Property
    
      <Description("Text associated with the X-axis Title"), _
      Category("Control Specific Graphics Text")> _
       Public Property TextXaxisTitle() As String
        Get
          TextXaxisTitle = mTextXaxisTitle
        End Get
        Set(ByVal value As String)
          mTextXaxisTitle = value
          PanelMain.Refresh()
        End Set
      End Property
    
      <Description("Text associated with the Y-axis Title"), _
      Category("Control Specific Graphics Text")> _
      Public Property TextYaxisTitle() As String
        Get
          TextYaxisTitle = mTextYaxisTitle
        End Get
        Set(ByVal value As String)
          mTextYaxisTitle = value
          PanelMain.Refresh()
        End Set
      End Property
    
      Dim mColorBarGraph As System.Drawing.Color = Color.Red
      <Description("Color associated with the Bars on the Graph"), _
      Category("Control Specific Graphics Color")> _
      Property ColorBarGraph() As System.Drawing.Color
        Get
          ColorBarGraph = mColorBarGraph
        End Get
        Set(ByVal value As System.Drawing.Color)
          mColorBarGraph = value
          PanelMain.Refresh()
        End Set
      End Property
    
      End Sub
    
    
    End Class
    
    
    • Edited by hazz Saturday, November 27, 2010 4:00 PM
    • Changed type hazz Monday, November 29, 2010 1:10 AM
    Sunday, November 21, 2010 3:30 PM

Answers

  • Actually, since you already have a custom control and are already painting it yourself, why not simply draw the crosshair yourself and calculate the position in a mouse move override?  You might consider working the following code into your existing control:

    Public Class CustomControl1
    
      Private _ReticlePath As System.Drawing.Drawing2D.GraphicsPath
      Private _ReticlePen As Pen
      Private _ReticleOrigin As PointF
    
      Private _HideCursor As Boolean
      Public Property HideCursor() As Boolean
        Get
          Return _HideCursor
        End Get
        Set(ByVal value As Boolean)
          _HideCursor = value
        End Set
      End Property
    
      Private _ReticlePosition As PointF
      Public ReadOnly Property ReticlePosition As PointF
        Get
          Return _ReticlePosition
        End Get
      End Property
    
      Private _ReticleSize As SizeF
      Public Property ReticleSize() As SizeF
        Get
          Return _ReticleSize
        End Get
        Set(ByVal value As SizeF)
          If Not _ReticleSize = value Then
            _ReticleSize = value
            BuildReticlePath()
            Invalidate()
          End If
        End Set
      End Property
    
      Private _ReticleThickness As Integer
      Public Property ReticleThickness() As Integer
        Get
          Return _ReticleThickness
        End Get
        Set(ByVal value As Integer)
          _ReticleThickness = value
          BuildReticlePen()
        End Set
      End Property
    
      Public Sub New()
    
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.
        _ReticlePath = New System.Drawing.Drawing2D.GraphicsPath
        _ReticlePen = New Pen(ForeColor)
        ReticleSize = New Size(24, 24)
        _ReticleThickness = 1
        _HideCursor = True
        DoubleBuffered = True
      End Sub
    
      Protected Overrides Sub OnForeColorChanged(ByVal e As System.EventArgs)
        MyBase.OnForeColorChanged(e)
        BuildReticlePen()
      End Sub
    
      Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
        MyBase.OnMouseEnter(e)
        If _HideCursor Then
          Cursor.Hide()
        End If
      End Sub
    
      Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
        MyBase.OnMouseLeave(e)
        Cursor.Show()
      End Sub
    
      Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseMove(e)
        _ReticlePosition = e.Location
        Invalidate()
      End Sub
    
      Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
    
        'Add your existing paint code here
    
        e.Graphics.TranslateTransform(_ReticlePosition.X - _ReticleOrigin.X, _ReticlePosition.Y - _ReticleOrigin.Y)
        e.Graphics.DrawPath(_ReticlePen, _ReticlePath)
        e.Graphics.ResetTransform()
      End Sub
    
      Private Sub BuildReticlePath()
        If _ReticlePath IsNot Nothing Then
          _ReticlePath.Dispose()
        End If
        _ReticlePath = New System.Drawing.Drawing2D.GraphicsPath
        Dim halfWidth As Single = _ReticleSize.Width / 2.0!
        Dim halfHeight As Single = _ReticleSize.Height / 2.0!
        _ReticleOrigin = New PointF(halfWidth, halfHeight)
        _ReticlePath.AddLine(halfWidth, 0, halfWidth, _ReticleSize.Height)
        _ReticlePath.CloseFigure()
        _ReticlePath.AddLine(0, halfHeight, _ReticleSize.Width, halfHeight)
      End Sub
    
      Private Sub BuildReticlePen()
        If _ReticlePen IsNot Nothing Then
          _ReticlePen.Dispose()
        End If
        _ReticlePen = New Pen(ForeColor, _ReticleThickness)
      End Sub
    End Class
    
    

    Something like that should be easy to work into what you already have and would add the desired functionality to your control.  You could then handle the paint event for your custom control on its container form and update a label with the reticle position.  Something like the following example:

    Public Class Form1
    
      Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    
      End Sub
    
      Private Sub CustomControl11_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles CustomControl11.Paint
        Me.Text = CustomControl11.ReticlePosition.ToString
      End Sub
    
    End Class
    

     


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • Marked as answer by hazz Monday, November 29, 2010 1:10 AM
    Tuesday, November 23, 2010 4:37 PM

All replies

  • You could use the MouseHover event to display a custom cursor.  I found a link to project but have not tried it.

    Custom Mouse Cursors (VB.NET) - CodeProject 

    Or, you could display a small custom form with transparent areas whose position tracks to the mouse location.  Again, you would use the MouseHover event to switch to a new cursor.  This approach may be easier and quicker to implement.  You could simply draw crosshairs on a borderless form in one color, and set the background color to be the transparency key property of the form.

    You might also want to put all of that drawing code into a class that inherits Panel.  Whenever the mouse hovers over the panel, the MouseHover event fires.  One last suggestion about your code.  You seem to have it well commented. 

    I would suggest that you put those sections of code into separate, appropriately named, methods where you can add even more detailed comments.  Your current comments would probably make good method names.   Promote those initial variable declarations to the class level and give them more descriptive names would be good, too.  You want to be able to look at this code months from now and understand it at a glance.

    This type of change is known as refactoring.  There are some add-in tools that can do this automatically for you.  This type of refactoring would meet the definition of an OOP design pattern known as a Template Method. 

    A Template Method pattern simply calls a series of methods, or invokes a series of delegates, one after the other.  By changing the delegates, you could alter the behavior of the templated method at run time.

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    EDIT:  You don't want MouseHover as I describe above.  You want to use MouseEnter and MouseLeave to swap out the cursor, or display the form on/off. Use MouseHover to track the mouse/cursor position if you need it.

    Sunday, November 21, 2010 5:56 PM
  • Thanks for the ideas Rudy. I'll try the MouseEnter/MouseLeave and a small custom form and see how that goes.

    I also noticed what appears to be relevant code in the popcursor sample whose link you sent. Much appreciated.

    Case epopType.Text
      Using bm As Bitmap = New Bitmap(CInt(sz.Width), CInt(sz.Height))
       Using g As Graphics = Graphics.FromImage(bm)
         g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
         g.DrawRectangle(New Pen(_popBorderColor), _
         New Rectangle(0, 0, bm.Width - 1, bm.Height - 1))
         g.DrawString(_popText, Font, New SolidBrush(_popTextColor), _
         New Rectangle(0, 0, bm.Width, bm.Height), _sf)
        Canvas.BackgroundImage = CType(bm.Clone, Image)
        Canvas.Size = bm.Size
        Me.Size = bm.Size
       End Using
      End Using
    
    Tuesday, November 23, 2010 1:34 PM
  • Actually, since you already have a custom control and are already painting it yourself, why not simply draw the crosshair yourself and calculate the position in a mouse move override?  You might consider working the following code into your existing control:

    Public Class CustomControl1
    
      Private _ReticlePath As System.Drawing.Drawing2D.GraphicsPath
      Private _ReticlePen As Pen
      Private _ReticleOrigin As PointF
    
      Private _HideCursor As Boolean
      Public Property HideCursor() As Boolean
        Get
          Return _HideCursor
        End Get
        Set(ByVal value As Boolean)
          _HideCursor = value
        End Set
      End Property
    
      Private _ReticlePosition As PointF
      Public ReadOnly Property ReticlePosition As PointF
        Get
          Return _ReticlePosition
        End Get
      End Property
    
      Private _ReticleSize As SizeF
      Public Property ReticleSize() As SizeF
        Get
          Return _ReticleSize
        End Get
        Set(ByVal value As SizeF)
          If Not _ReticleSize = value Then
            _ReticleSize = value
            BuildReticlePath()
            Invalidate()
          End If
        End Set
      End Property
    
      Private _ReticleThickness As Integer
      Public Property ReticleThickness() As Integer
        Get
          Return _ReticleThickness
        End Get
        Set(ByVal value As Integer)
          _ReticleThickness = value
          BuildReticlePen()
        End Set
      End Property
    
      Public Sub New()
    
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.
        _ReticlePath = New System.Drawing.Drawing2D.GraphicsPath
        _ReticlePen = New Pen(ForeColor)
        ReticleSize = New Size(24, 24)
        _ReticleThickness = 1
        _HideCursor = True
        DoubleBuffered = True
      End Sub
    
      Protected Overrides Sub OnForeColorChanged(ByVal e As System.EventArgs)
        MyBase.OnForeColorChanged(e)
        BuildReticlePen()
      End Sub
    
      Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
        MyBase.OnMouseEnter(e)
        If _HideCursor Then
          Cursor.Hide()
        End If
      End Sub
    
      Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
        MyBase.OnMouseLeave(e)
        Cursor.Show()
      End Sub
    
      Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseMove(e)
        _ReticlePosition = e.Location
        Invalidate()
      End Sub
    
      Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)
    
        'Add your existing paint code here
    
        e.Graphics.TranslateTransform(_ReticlePosition.X - _ReticleOrigin.X, _ReticlePosition.Y - _ReticleOrigin.Y)
        e.Graphics.DrawPath(_ReticlePen, _ReticlePath)
        e.Graphics.ResetTransform()
      End Sub
    
      Private Sub BuildReticlePath()
        If _ReticlePath IsNot Nothing Then
          _ReticlePath.Dispose()
        End If
        _ReticlePath = New System.Drawing.Drawing2D.GraphicsPath
        Dim halfWidth As Single = _ReticleSize.Width / 2.0!
        Dim halfHeight As Single = _ReticleSize.Height / 2.0!
        _ReticleOrigin = New PointF(halfWidth, halfHeight)
        _ReticlePath.AddLine(halfWidth, 0, halfWidth, _ReticleSize.Height)
        _ReticlePath.CloseFigure()
        _ReticlePath.AddLine(0, halfHeight, _ReticleSize.Width, halfHeight)
      End Sub
    
      Private Sub BuildReticlePen()
        If _ReticlePen IsNot Nothing Then
          _ReticlePen.Dispose()
        End If
        _ReticlePen = New Pen(ForeColor, _ReticleThickness)
      End Sub
    End Class
    
    

    Something like that should be easy to work into what you already have and would add the desired functionality to your control.  You could then handle the paint event for your custom control on its container form and update a label with the reticle position.  Something like the following example:

    Public Class Form1
    
      Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    
      End Sub
    
      Private Sub CustomControl11_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles CustomControl11.Paint
        Me.Text = CustomControl11.ReticlePosition.ToString
      End Sub
    
    End Class
    

     


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    • Marked as answer by hazz Monday, November 29, 2010 1:10 AM
    Tuesday, November 23, 2010 4:37 PM
  • It is a common practice for CAD and other types of graphics software to use the invisible form approach.  Actually, one would use a stationary form that covers the entire visible graphics area. 

    This allows you to easily draw selection rectangles, and other types of selection tools with various special effects, without disturbing the underlying graphic images. The invisible form approach also makes it easier to implement other features such as DUR, Do/Undo/Redo.  Makes it easy for the user to set a custom size for the crosshairs, and some other type of intelligent cursor.

    Use Reed's code to implement an invisible, but stationary, form.  Naturally, this form would need to repositon and/or resize itself if the user changes the main form in any way.

    Rudy   =8^D


    Mark the best replies as answers. "Fooling computers since 1971."

    http://rudedog2.spaces.live.com/default.aspx

    Tuesday, November 23, 2010 10:54 PM
  • Agreed.  If you desire the ability to create overlays on top of the graph, then using an additional layer is a good route to take.

    To only retrive a set of coordinate values, the extra layer is probably not necessary.

    Rudy's idea would allow you to potentially exceed the user's expectations.  :)


    Reed Kimble - "When you do things right, people won't be sure you've done anything at all"
    Wednesday, November 24, 2010 5:37 AM
  • Thank you Reed. The Reticle is showing up perfectly on my graph as per this illustration.

    Wednesday, November 24, 2010 8:15 AM