Click here to Skip to main content
15,867,686 members
Articles / DevOps / Load Testing

NHunspell Component for Visual Studio

Rate me:
Please Sign up or sign in to vote.
4.90/5 (31 votes)
1 Sep 2013CPOL6 min read 111.7K   4.1K   46   48
NHunspell component example for WindowsForms
Image 1

Contents

Introduction

This article shows how to create reusable and extendible spellchecking component based on NHunspell.

Background

The history began about a year ago when I decided to write an application with spellchecking. I discovered lots of .NET components doing what I want, but all of them except NetSpell were proprietary. I looked through lots of C++ code while investigating how things work in other solutions. But then I understood that work is still in the starting phase and started with NetSpell to get some rough solution.

NetSpell

NetSpell is free spellchecking engine that was developed in 2004 for .NET.

The first goal was to add spellchecking functionality to RichTextBox control. When I realized it, I was very proud of myself. It was working! But then, I needed to add such functionality to MaskedTextBox control. I realized that my implementation was too sticky and I should spend almost the same time for integration to any new control. Implementation of such things should be at least as possible binding to any control or project. It must be self-sufficient. It should be some attachable thing that I can use any time I want it, like a book to check spelling and put it back on shelf.

Then I read some design patterns and began writing my implementation for any control use. I used Singleton for Instance property, and used it from any form to notify spellchecking engine about control it should check spelling. Then of course, I wanted to add some wavy underlining to the text and found WINAPI solution here. When I realized it, I was very proud of myself. My spellchecking engine worked for any text control and also could draw wavy underlines. But then I discovered that WINAPI underlining does not always work properly with different fonts. This was a crash. I desperately didn't want to draw those lines myself. But then another solution came to me from the skies here. It was describing how to draw those underlines overriding WndProc method of control and adding some custom painting functionality.

NHunspell

Hunspell is a wrapper for commonly used Hunspell engine. Hunspell is C++ library that is used in such application as: OpenOffice, Mozilla Firefox/Thunderbird, Opera 10, The Bat! and many others.

After a couple of months, I discovered that there is a .NET wrapper for Hunspell. I looked into my last solution and found that if I want to add spellchecking to other projects, I still will have to copy lots of code with my hands. This was a crash. I understood that such things should be not only not binding a control, but also they should not be bound to the initial solution. The code must be reusable. If you write code with this thought in mind, your code can become more than just one time implementation for the same time. I needed to write a component that is possible to just include in my solution and use. The component should do everything it needs by self means and should expose as many events to satisfy every programmers' needs (I hand some problems using NetSpell because there were no events I desperately needed and I had to rewrite some code in existing library and recompile it).

I understand that some controls should handle underlining and some need not. So I described basic interface ISpellingControl and then extended it with IUnderlineable interface. Now I have IUnderlineableSpellingControl interface for richTextBox like controls and ISpellingControl for all others.

Concepts

NHunspellComponent/SpellingWorker.png

As you can see in the first diagram, NHunspellWrapper lives its own life and exposes Instance property to others. We need only 1 instance per application. We can have as many SpellingWorkers as we need. SpellingWorker class has link to Editor it works with.

Attention: In different libraries, properties can have the same name and even declaration but in runtime, if you try to cast one type to other, property will return null.

For example: DevExpress.XtraEditors.BaseEdit has Text property. In your code, you have written some class that works with TextBoxBase that also has such property. If you try to cast one type to another and use its Text property, it will return a null value. So be careful with this.

The next diagram shows how you can solve this issue.

NHunspellComponent/ISpellingControl.png

As I pointed before, we use Interface injection here. So now our component can work with most classes that have identical definitions through this interface. Anyway if ClassA we want to use doesn't have the needed definitions, we should describe them for component to work with ClassA properly.

Using the Code

To use the SpellingWorker component, you will have to add reference to NHunspellComponent to your project. Also add compatible dictionaries to your project and Hunspellx86/x64.dll and set your project properties post-build action to look like this:

xcopy "$(ProjectDir)Dictionaries" "$(TargetDir)" /ICRY
xcopy "$(ProjectDir)Hunspell" "$(TargetDir)" /ICRY 

Drag the component onto the form and select the appropriate editor (that implements ISpellingControl or IUnderlinableSpellingControl interfaces). That's it.

Spelling Control

Implementing ISpellingControl in general is usually doesn't need much work because all properties and methods are already present in text editor control. So as you can see in the example application, we need to add this interface to CustomMaskedTextBox and implement unique properties of ISpellingControl only (those are IsSpellingEnabled, IsSpellingAutoEnabled, IsPassWordProtected). You can read about interface injection in Inversion of control and Dependency injection articles.

Spelling Window

Image 4

Image 5

Spelling window should notify NHunspellWrapper about the changes it makes. Also it should expose properties that will allow other classes to change its view.

Options are stored in spelling worker for each control. So you can setup individual options.

Wavy Underlines

Image 6

One of the main problems during spell check is notifying the user about the mistake just made. You can use any action you want. You can launch fireworks at each misspelled word on UnderliningChanged event. But I prefer underlining them with red waves. So I describe how it was done in the sample application:

  1. Method WndProc of control that implements IUnderlinable was overridden to call CustomPaint method.
  2. CustomPaint method used to manipulate bitmaps of editor's region. It draws wavy underlines under each item of UnderlinedSections property.
  3. DrawWave method is used to draw a zigzag line from point to point.

Points of Interest

I learned how to write components and the most irritating thing is that you have to do some dancing with tambourine to draw those wavy underlining we are so used to as users. They are not necessary conditions for components to work and are shown in TestApplication project in CustomPaintRichText class. You can see it as an example, but if you need something that really works, it needs to be well tested.

Also I had trouble with displaying SpellingWorker component in Visual Studio toolbox.

The most clever thing about writing any library is to implement it as a reusable library, so you won't need to rewrite your code in future. Design patterns and practices will help you with this and of course solve problems again and again to gain experience. You won't learn anything from just reading articles unless you are a person with extrasensory perception.

History

This is the initial version of the article.

Updated source code

License

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


Written By
Software Developer
Ukraine Ukraine
"a man can do no more than he can" (proverb)
Don't you think there are a lot of things you couldn't do couple of years before but now you can? Things didn't change, but the only thing that changed is belief in yourself.

Comments and Discussions

 
PraiseThank you for making this. Pin
dcarl66110-Mar-24 7:49
dcarl66110-Mar-24 7:49 
PraiseThanks Pin
Ahmed Alayat6-Jul-17 12:58
Ahmed Alayat6-Jul-17 12:58 
QuestionWave Underline in TextBox, not in RichTextBox Pin
diddlik16-Nov-15 1:26
diddlik16-Nov-15 1:26 
BugSpellcheck Underline beaks at times Pin
Ahmed Cader13-Jul-15 15:51
Ahmed Cader13-Jul-15 15:51 
QuestionDeployment Pin
Elias Alemayehu27-May-15 1:18
Elias Alemayehu27-May-15 1:18 
QuestionUnable to handle the proper underline if some text format (Font size, Font family etc) of the text is changed Pin
Member 102378357-Sep-14 20:55
Member 102378357-Sep-14 20:55 
QuestionLoad other dictionaries Pin
Member 125813810-Feb-14 6:12
Member 125813810-Feb-14 6:12 
GeneralMy vote of 5 Pin
Franc Morales23-Sep-13 20:37
Franc Morales23-Sep-13 20:37 
GeneralRe: My vote of 5 Pin
DmytroSokhach24-Sep-13 9:28
DmytroSokhach24-Sep-13 9:28 
GeneralRe: My vote of 5 Pin
Franc Morales14-Aug-14 11:07
Franc Morales14-Aug-14 11:07 
QuestionPlease Help to Add word to personal Dictionary Pin
Member 1011006620-Sep-13 7:53
Member 1011006620-Sep-13 7:53 
AnswerRe: Please Help to Add word to personal Dictionary Pin
DmytroSokhach20-Sep-13 10:00
DmytroSokhach20-Sep-13 10:00 
GeneralRe: Please Help to Add word to personal Dictionary Pin
Member 1011006620-Sep-13 16:59
Member 1011006620-Sep-13 16:59 
AnswerRe: Please Help to Add word to personal Dictionary Pin
DmytroSokhach21-Sep-13 6:44
DmytroSokhach21-Sep-13 6:44 
QuestionWords Not added to the dictionary Pin
Alfred2415-Sep-13 15:33
Alfred2415-Sep-13 15:33 
AnswerRe: Words Not added to the dictionary Pin
DmytroSokhach15-Sep-13 23:05
DmytroSokhach15-Sep-13 23:05 
GeneralRe: Words Not added to the dictionary Pin
fsunole0311-Jun-14 13:16
fsunole0311-Jun-14 13:16 
AnswerRe: Words Not added to the dictionary Pin
DmytroSokhach20-Sep-13 10:04
DmytroSokhach20-Sep-13 10:04 
QuestionExcellent Work - Problem in Bangla Language Pin
Member 101100667-Sep-13 17:15
Member 101100667-Sep-13 17:15 
GeneralMy vote of 5 Pin
fredatcodeproject2-Sep-13 2:46
professionalfredatcodeproject2-Sep-13 2:46 
QuestionProblem not solved Pin
Member 1023783531-Aug-13 19:20
Member 1023783531-Aug-13 19:20 
AnswerRe: Problem not solved Pin
DmytroSokhach1-Sep-13 6:18
DmytroSokhach1-Sep-13 6:18 
GeneralRe: Problem not solved Pin
Member 102378351-Sep-13 6:54
Member 102378351-Sep-13 6:54 
Dear DmytroSokhach

Thanks for early reply

I changed and rebuild the same but could not get result as you get. I think I not defined the constants PATTERN_IS_DEVANAGARI as you specified properly. Could you please give the correct position and the file name in which i can define this properly.

Please note that i updated the method private static bool CheckCharBelongsToWord(char ch) in the Word.cs file just before the public static Word GetWordFromPosition(string Text, int position) method.

Anurag
AnswerRe: Problem not solved Pin
DmytroSokhach1-Sep-13 9:29
DmytroSokhach1-Sep-13 9:29 
GeneralRe: Problem not solved Pin
Member 102378352-Sep-13 5:32
Member 102378352-Sep-13 5:32 

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.