Click here to Skip to main content
15,895,667 members
Articles / Programming Languages / Visual Basic

NHunspellToolTip- A Spellchecking ToolTip using Hunspell for .NET

Rate me:
Please Sign up or sign in to vote.
4.86/5 (13 votes)
17 Feb 2010CPOL5 min read 26.7K   974   16  
This ToolTip will allow you to spell-check the text of items or controls when you can't see the whole text.
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Reflection
Imports System.IO


<ToolboxBitmap(GetType(NHunspellToolTip), "tooltipspellcheck.bmp")> _
Public Class NHunspellToolTip
    Inherits ToolTip

    Private bufferGraphics As Graphics
    Private myBitmap As Bitmap
    Private toolTipGraphics As Graphics
    Private mySpellCheckControl As SpellCheckControl

    Public Sub New()
        Me.OwnerDraw = True
        Dim obj As Object = FromAssembly()

        mySpellCheckControl = New SpellCheckControl(obj)
    End Sub

    Private Shared Function FromAssembly() As Object
        Dim a As Assembly = Assembly.Load(My.Resources.NHunspell)
        Dim type_l As Type = a.GetType("NHunspell.Hunspell")
        Dim types(1) As Type
        types(0) = GetType(String)
        types(1) = GetType(String)
        Dim ctor As ConstructorInfo = type_l.GetConstructor(types)

        Dim USdic, USaff As String
        Dim callingDir As String = Path.GetDirectoryName(Assembly.GetCallingAssembly.Location)

        'Set the paths for the dic and aff files
        USdic = callingDir & "\SpellCheck\en_US.dic"
        USaff = callingDir & "\SpellCheck\en_US.aff"

        'Check if the spell check directory already exists.  If not, add it
        If Not Directory.Exists(callingDir & "\SpellCheck") Then
            Directory.CreateDirectory(callingDir & "\SpellCheck")
            Dim newDirInfo As New DirectoryInfo(callingDir & "\SpellCheck")
            newDirInfo.Attributes = FileAttributes.Hidden
        End If

        'Check if the spell check files already exist.  If not, add it
        If Not File.Exists(USaff) Then
            Try
                File.WriteAllBytes(USaff, My.Resources.en_US)
            Catch ex As Exception
                MessageBox.Show("Error writing en_US.aff file!" & vbNewLine & ex.Message)
            End Try
        End If

        If Not File.Exists(USdic) Then
            Try
                File.WriteAllBytes(USdic, My.Resources.en_US_dic)
            Catch ex As Exception
                MessageBox.Show("Error writing en_US.dic file!" & vbNewLine & ex.Message)
            End Try
        End If

        Dim params(1) As Object
        params(0) = USaff
        params(1) = USdic

        Dim result As Object = Nothing

CreateNewHunspell:
        Try
            result = ctor.Invoke(params)
        Catch ex As Exception
            If TypeOf ex.InnerException Is System.DllNotFoundException Then
                'Get where the dll is supposed to be
                Dim DLLpath As String = Trim(Strings.Mid(ex.InnerException.Message, InStr(ex.InnerException.Message, "DLL not found:") + 14))
                Dim DLLName As String = Path.GetFileName(DLLpath)

                'Find out which DLL is missing
                If DLLName = "Hunspellx64.dll" Then
                    'Copy the dll to the directory
                    Try
                        File.WriteAllBytes(DLLpath, My.Resources.Hunspellx64)
                    Catch ex2 As Exception
                        MessageBox.Show("Error writing Hunspellx64.dll" & vbNewLine & ex2.Message)
                    End Try

                    'Try again
                    GoTo CreateNewHunspell
                ElseIf DLLName = "Hunspellx86.dll" Then 'x86 dll
                    'Copy the dll to the directory
                    Try
                        File.WriteAllBytes(DLLpath, My.Resources.Hunspellx86)
                    Catch ex3 As Exception
                        MessageBox.Show("Error writing Hunspellx86.dll" & vbNewLine & ex3.Message)
                    End Try

                    'Try again
                    GoTo CreateNewHunspell
                ElseIf DLLName = "NHunspell.dll" Then
                    Try
                        File.WriteAllBytes(DLLpath, My.Resources.NHunspell)
                    Catch ex4 As Exception
                        MessageBox.Show("Error writing NHunspell.dll" & vbNewLine & ex4.Message)
                    End Try
                Else
                    MessageBox.Show(ex.Message & ex.StackTrace)
                End If
            Else
                MessageBox.Show("SpellChecker cannot be created." & vbNewLine & "Spell checking will be disabled." & _
                                vbNewLine & vbNewLine & ex.Message & ex.StackTrace)
                Return Nothing
            End If
        End Try

        Return result
    End Function


    Private Sub NHunspellToolTip_Draw(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawToolTipEventArgs) _
        Handles Me.Draw
        e.DrawBackground()

        mySpellCheckControl.SetText(e.ToolTipText)

        'Now we do the custom drawing
        myBitmap = New Bitmap(e.Bounds.Width, e.Bounds.Height)
        bufferGraphics = Graphics.FromImage(myBitmap)
        bufferGraphics.Clip = New Region(e.Bounds)

        Dim currentRange As CharacterRange

        For Each currentRange In mySpellCheckControl.GetSpellingErrorRanges
            Dim startPoint, endPoint As Point
            Dim bottom, left, right As Integer

            'Determine which line the current word is on
            Dim lastNewline As Integer = 1
            For i = 1 To currentRange.First
                If Mid(e.ToolTipText, i, 1) = vbLf Then
                    lastNewline = i + 1
                End If
            Next

            'Get the text of the line to the end of the word
            Dim lineToEndofWord As String = Mid(e.ToolTipText, lastNewline, _
                                                ((currentRange.First - lastNewline) + currentRange.Length + 1))

            'Figure out how wide and tall the text before this is word is
            'Measure the text starting from the beginning to the end of the word to get the borrom coordinates
            Dim newSize As SizeF = TextRenderer.MeasureText(Microsoft.VisualBasic.Strings.Left(e.ToolTipText, _
                                                            (currentRange.First + currentRange.Length)), _
                                                            e.Font, e.Bounds.Size, TextFormatFlags.Left)
            bottom = newSize.Height - 2

            'Now measure the text from the beginning of the current line to the end of the word to get the right coordinates
            newSize = TextRenderer.MeasureText(lineToEndofWord, e.Font, e.Bounds.Size, TextFormatFlags.Left)
            right = newSize.Width
            endPoint = New Point(right, bottom)

            'Now we can backtrack and find out how wide that text is
            newSize = TextRenderer.MeasureText(Mid(e.ToolTipText, currentRange.First + 1, currentRange.Length), _
                                               e.Font, e.Bounds.Size, TextFormatFlags.Left)
            left = right - newSize.Width
            startPoint = New Point(left, bottom)

            startPoint.X += 2
            endPoint.X -= 4

            DrawWave(startPoint, endPoint)
        Next

        toolTipGraphics = e.Graphics

        toolTipGraphics.DrawImageUnscaled(myBitmap, 0, 0)

        e.DrawBorder()
        Dim stringFlags As New StringFormat()
        stringFlags.Alignment = StringAlignment.Near
        stringFlags.LineAlignment = StringAlignment.Near

        TextRenderer.DrawText(e.Graphics, e.ToolTipText, e.Font, e.Bounds, Color.Black, flags:=TextFormatFlags.Left)
    End Sub

    ''' <summary>
    ''' Draws the wavy red line given a starting point and an ending point
    ''' </summary>
    ''' <param name="StartOfLine">A Point representing the starting point</param>
    ''' <param name="EndOfLine">A Point representing the ending point</param>
    ''' <remarks></remarks>
    Private Sub DrawWave(ByVal StartOfLine As Point, ByVal EndOfLine As Point)
        Dim newPen As Pen = Pens.Red

        If (EndOfLine.X - StartOfLine.X) > 4 Then
            Dim pl As New ArrayList
            For i = StartOfLine.X To (EndOfLine.X - 2) Step 4
                pl.Add(New Point(i, StartOfLine.Y))
                pl.Add(New Point(i + 2, StartOfLine.Y + 2))
            Next

            Dim p() As Point = CType(pl.ToArray(GetType(Point)), Point())
            bufferGraphics.DrawLines(newPen, p)
        Else
            bufferGraphics.DrawLine(newPen, StartOfLine, EndOfLine)
        End If
    End Sub
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other NOAA
United States United States
I am a commissioned officer with the NOAA Commissioned Corps.

Currently I am a GIS Analyst with the National Marine Fisheries Service. I have a Master's in Environmental Science and code more as a means to an end.

Comments and Discussions