Click here to Skip to main content
12,690,140 members (30,945 online)
Click here to Skip to main content
Add your own
alternative version


15 bookmarked

NHunspellToolTip- A Spellchecking ToolTip using Hunspell for .NET

, 17 Feb 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
This ToolTip will allow you to spell-check the text of items or controls when you can't see the whole text.



As programmers, we are often asked to develop an interface to allow easier access to databases. In my case, I was dealing with a database structured using a snowflake schema. Certain tables held indices relating to other tables that contained the data. As an example, there were three levels of "Actions": "Objectives", "Recovery Actions", and "Action Steps". Any one of these could contain information on Priorities, Cost, Duration, Recovery Partners, and comments, along with the text of the action. To the human eye, the database was unreadable.

In developing the GUI for the user to access, it was unnecessary for the user to be able to modify all of the information from one screen. Instead, I wanted to provide a snapshot of the data to the user and use ToolTips to provide the more detailed information and ContextMenus for editing that information. The control that provided the information was a custom ListView (as seen below).


This worked well, and the users found it easy to navigate through the database using the GUI. However, I soon realized that our users were not very careful while typing and produced a large number of spelling errors. So, I wanted to provide a way for the user to have a visual cue that they had made a spelling error.

To that end, I developed an IExtenderProvider that would extend TextBoxes to provide Microsoft Word style spell-as-you-type spell checking capabilities. I used Hunspell for .NET and created the NHunspellTextBoxExtender. I was able to use the TextBox extender when they were editing the text, but that still didn't provide any spell checking capability on the main form where the information was provided by ListViews.

So, the question was, how could I provide that visual cue that something was misspelled within the information? I was already using ToolTips to provide the information, so why not create a ToolTip that could spell-check its text and provide the visual cues that way? Then, the user could see that there was a spelling error, and go in and fix it. This custom ToolTip provides the functionality seen below:


The Spell Checking ToolTip

Unlike my previous control (the NHunspellTextBoxExtender), for this control I didn't need to use an IExtenderProvider since I was only changing one ToolTip. Instead, I chose to create a new Control that inherited ToolTip. The spell checking was very similar to the TextBox version. But because I was limiting it to display only, and because the control's text would only be set, and not appended or trimmed, I could pare down the SpellCheckControl.


The SpellCheckControl handles all of the spell checking. When the ToolTip receives the Draw command, it calls the SetText method of the SpellCheckControl. The SetText method goes through all of the text and finds any misspelled words and identifies them using CharacterRanges.

The class structure is shown below:

Imports System.Drawing
Imports System.IO
Imports System.Windows.Forms
Imports System.Reflection

Public Class SpellCheckControl
    Private FullText As String
    Private _Text(,) As String
    Public myNHunspell As Object = Nothing
    Private _spellingErrors() As String
    Private _spellingErrorRanges() As CharacterRange

    Public Sub New(ByRef HunspellObject As Object) 

    Public Sub SetText(ByVal Input As String)

    Private Function FindFirstLetterOrDigitFromPosition(_
                     ByVal SelectionStart As Long) As Long

    Private Function FindLastLetterOrDigitFromPosition(_
                     ByVal SelectionStart As Long) As Long

    Public Function GetSpellingErrorRanges() As CharacterRange()

    Public Function HasSpellingErrors() As Boolean

    Public Sub SetSpellingErrorRanges()

End Class

ToolTip Draw

Once the spelling errors have been identified (which is remarkably quick, thanks to NHunspell), the ToolTip then needs to be drawn. To do this, I simply handle the ToolTip_Draw method. At this point, I've already drawn the background using the e.DrawBackground() command. The next step is to draw the wavy red line. To do this, we first have to identify where the spelling errors are and determine where the red line should be drawn. This proved to be a bit tricky because we had to keep track of which line the current text was on.

But, once we had determined where to draw the line, we then had to actually draw it. I did this using some code that can be found at this blog. The full code of the Draw event is shown below:

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


    '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

        '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)

    toolTipGraphics = e.Graphics

    toolTipGraphics.DrawImageUnscaled(myBitmap, 0, 0)

    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

Using the Code

That's really it! This tool is very simple to use... simply download the DLL using one of the links above, and add it to one of your Toolboxes. The spell checking is done automatically, and this ToolTip can be used in the same way as the standard ToolTip.

Points of Interest

I was able to implement something different with this ToolTip. I am not sure why I could not get it to work with the NHunspellTextBoxExtender, but I am able to load this ToolTip without the NHunspell.dll file. This means that when I package up this tool, I do not need to include the NHunspell.dll as well. To accomplish this, I embed the NHunspell.dll file in the project. Then, when I create the Hunspell object, I create it using the raw assembly.

This takes a bit of work, and you have to know the paramaters before hand. To start, I have to load the assembly. This is done with a single line of code:

Dim a As Assembly = Assembly.Load(My.Resources.NHunspell)

I then have to get the Type of the object that I am trying to create, and create a ConstructorInfo class. The constructor for the Hunspell object takes two strings. The code to do this takes a few more lines of code, but it's still relatively simple:

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)

The last thing that has to be done is to Invoke the constructor which will return an Object. To do this, I have to set up my parameters that will be passed in. The Invoke method takes an array of Objectss. The first parameter for the Hunspell is the .aff file and the second is the .dic file. To do this without any error checking looks like:

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

Dim result As Object = Nothing
result = ctor.Invoke(params)

When I do this for real, I place result = ctor.Invoke(params) into a Try/Catch block as I have to make sure that a couple of unmanaged DLLs are available. I have provided more information on that under my NHunspellTextBoxExtender article.

The only problem with this method is that the IntelliSense of Visual Studio will not work this way. That is why this step is not implemented until the very end. Before that, I include a reference to the NHunspell DLL which gives me my IntelliSense. Then, before finalizing it, I simply comment out that Import statement and change the Hunspell objects to Object.


  • 17 February 2010: Article created.


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


About the Author

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

You may also be interested in...


Comments and Discussions

GeneralText wrapping in the tooltip Pin
hairball.dh@gmail.com29-Mar-10 18:34
memberhairball.dh@gmail.com29-Mar-10 18:34 
GeneralRe: Text wrapping in the tooltip Pin
William Winner30-Mar-10 7:09
memberWilliam Winner30-Mar-10 7:09 
Well, there would be a way to set the maximum width (I assume that's what you meant because you want to wordwrap). But I did that in the base form, not in the tooltip. When I created this, the only thing that I was interested in changing with the tooltip was adding the wavy lines.

But, if you wanted to do it, I can show you how I've done it in the past. When I set the tooltip property, it generally looks something like this:

toolTip = Join(SplitLines(newItem.Description, "", 55, False, False), vbNewLine)

SplitLines is a function that I wrote that returns an array of for each line. It was specific for my project, but it could be modified. It takes as input the text you want to split, a type, which can be set to "", or you could just take it out of the code (I used it to add spaces to offset different levels in my program. It also takes the MaxCharacters on a line, whether or not we're continuing the previous line or this is a new line, and whether or not to indent the other lines. It looks like:

Public Function SplitLines(ByVal InputLine As String, _
                           ByVal Type As String, _
                           ByVal MaxCharacters As Integer, _
                           ByVal boolCont As Boolean, _
                           Optional ByVal IndentSecondLine As Boolean = True) As String()
        If Type = "O" Then
            InputLine = "     " & InputLine
        ElseIf Type = "R" Then
            InputLine = "          " & InputLine
        ElseIf Type = "A" Then
            InputLine = "               " & InputLine
        End If

        If boolCont And IndentSecondLine Then
            InputLine = "    " & InputLine
        End If

        Dim splitString() As String
        ReDim splitString(0)
        splitString(0) = ""

        If Len(InputLine) < MaxCharacters Then
            splitString(0) = InputLine
            'Find split point based on words
            Dim intSplit As Integer = MaxCharacters

            While Mid(InputLine, intSplit, 1) <> " " And Mid(InputLine, intSplit, 1) <> "-"
                intSplit -= 1
            End While

            If intSplit = 0 Then
                splitString(0) = Left(InputLine, MaxCharacters)

                Dim remainingSplit() As String = SplitLines(Right(InputLine, Len(InputLine) - MaxCharacters), _
                                                            Type, MaxCharacters, True, IndentSecondLine)
                ReDim Preserve splitString(UBound(remainingSplit) + 1)
                For i = 0 To UBound(remainingSplit)
                    splitString(i + 1) = remainingSplit(i)
                splitString(0) = Left(InputLine, intSplit)

                Dim remainingSplit() As String = SplitLines(Right(InputLine, Len(InputLine) - intSplit), _
                                                            Type, MaxCharacters, True, IndentSecondLine)
                ReDim Preserve splitString(UBound(remainingSplit) + 1)
                For i = 0 To UBound(remainingSplit)
                    splitString(i + 1) = remainingSplit(i)
            End If
        End If

        Return splitString
End Function

You can feel free to modify that code or use it as is. That's how I created the tooltip with the text wrapped.
GeneralA brilliant article Pin
Ant210018-Feb-10 2:41
memberAnt210018-Feb-10 2:41 
GeneralRe: A brilliant article Pin
William Winner18-Feb-10 7:57
memberWilliam Winner18-Feb-10 7:57 
GeneralRe: A brilliant article Pin
William Winner22-Feb-10 10:41
memberWilliam Winner22-Feb-10 10:41 
GeneralRe: A brilliant article Pin
Ant210022-Feb-10 10:47
memberAnt210022-Feb-10 10:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170117.1 | Last Updated 17 Feb 2010
Article Copyright 2010 by William Winner
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid