Free spell checking in Windows Forms
So our back-end application at work runs on Windows Forms. Windows Presentation Foundation looks cool, and I’ll certainly want to learn it some day, but it didn’t exist at the time that our back-end application was started, it’s hard for a developer without a good sense of the aesthetic to build a decent interface, it pretty much requires Visual Studio 2008 (which I don’t have the luxury of purchasing at this time), and it flies in the face of over a decade of user32 programming. So we’ll be stuck with Windows Forms for a while.
Why do I bring WPF up? Well, the default text controls in WPF have “wavy red line spell checking” built right in, which is awesome. But there’s no joy for the Windows Forms world. There are lots of components out there for this, but they’re either expensive or bundled together with much larger control suites that I don’t need. (By expensive, I generally mean anything over fifty bucks.)
Being used to being able to Google just about anything and find an open source version somewhere, I was pretty surprised that I haven’t been able to track down a free version of a spell checker. I found plenty of free spell checking engines (one of which I used in my implementation) as well as snippets from people who have written wavy red line algorithms for MFC-based controls, but there was no love for Windows Forms. I was, however, able to cobble together a solution from these snippets and piece together something that works. It’s not very heavyweight or robust, but it’s great in small text boxes, such as the product description field in our application.
An Overview of What We’re Building
To make this as painless to use as possible, I built the spell checker as an IExtenderProvider. If you’ve used things like Tooltip or ErrorProvider in Windows Forms before, it works the same way: you drag and drop a SpellChecker onto your form in the Visual Studio visual designer. Then, any control on that form that inherits from TextBoxBase magically gets a SpellCheckEnabled on mSpellChecker property. Set that to true and the visual designer adds some magic in the designer file that registers the textbox with the spellchecker, enabling it to get the wavy red line spellchecking behavior that I’ve programmed.
A Spell Checking Interface
When I first used the control, I piggybacked onto Microsoft Word to do the spellchecking for me. It worked well for the spike, but I quickly discovered that it was a pain to have to ensure that every client has the right version of Word installed on their computer. So I switched the engine implementation over to NetSpell, a free and open source spell checking engine. In the process, however, I abstracted out the spell checking engine that my new SpellChecker uses and made it pluggable.
So, without any further ado, here’s the interface that these spell checking engine providers need to adhere to:
/// <summary> /// For a service to provide spell checking capabilities to the SpellChecker /// extender provider, it needs to implement this interface. /// </summary> public interface ISpellCheckerProvider : IDisposable { #region Methods /// <summary> /// Adds a user-defined word to the dictionary. /// </summary> /// <param name="word">the word</param> void AddWord(string word); /// <summary> /// Tests to see if the given word is in the dictionary /// (that is, not misspelled). /// </summary> /// <param name="word">the word to test</param> /// <returns>whether or not the word is in the dictionary</returns> bool IsWordInDictionary(string word); /// <summary> /// Returns an array of suggested spellings for the given misspelled /// word. /// </summary> /// <param name="word">the word</param> /// <returns>the suggested spellings</returns> string[] SuggestSpellings(string word); #endregion #region Properties /// <summary> /// Gets whether or not the object has been disposed. /// </summary> bool IsDisposed { get; } /// <summary> /// Gets whether or not the provider supports adding user-defined /// words to its dictionary. /// </summary> bool SupportsAddingWords { get; } #endregion }
As you can see, it’s a pretty simple interface: you can add words to your local dictionary (which not all implementors may support), you can test to see if a word is misspelled, and you can suggest some alternative spellings for a misspelled word.
Here’s how the implementation that uses NetSpell looks:
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using NetSpell.SpellChecker; using NetSpell.SpellChecker.Dictionary; public class NetSpellSpellCheckerProvider : Component, ISpellCheckerProvider { #region Members private Spelling mSpelling; private readonly object mSpellingLock = new object(); #endregion #region Constructors public NetSpellSpellCheckerProvider() { mSpelling = new Spelling(); mSpelling.Dictionary = new WordDictionary(); mSpelling.Dictionary.EnableUserFile = true; } public NetSpellSpellCheckerProvider(IContainer container) : this() { container.Add(this); } #endregion #region Public Methods public void AddWord(string word) { lock (mSpellingLock) { if (SupportsAddingWords) { mSpelling.Dictionary.Add(word); } } } public bool IsWordInDictionary(string word) { bool isInDictionary; lock (mSpellingLock) { isInDictionary = mSpelling.TestWord(word); } return isInDictionary; } public string[] SuggestSpellings(string word) { string[] suggestions; lock (mSpellingLock) { mSpelling.Suggest(word); suggestions = new string[mSpelling.Suggestions.Count]; mSpelling.Suggestions.CopyTo(suggestions); } return suggestions; } #endregion #region Properties public string DictionaryFileName { get { return mSpelling.Dictionary.DictionaryFile; } set { mSpelling.Dictionary.DictionaryFile = value; } } public bool IsDisposed { get { return false; } } public bool SupportsAddingWords { get { return mSpelling.Dictionary.UserFile != null; } } #endregion }
How simple is that? The only thing that makes it slightly complicated is being sure to do a lock whenever we’re dealing with the spelling dictionary. It turns out that NetSpell isn’t happy if, say, one thread is adding a word to the dictionary while another thread is trying to read one. Not too difficult to work around, however.
Implementing the IExtenderProvider
So now let’s write the bit of code that lets the Visual Studio visual designer add those magic SpellCheckEnabled properties to our textboxes.
Let’s start at the beginning:
/// <summary> /// An extender provider that will provide as-you-type spell checking (with /// wavy red underlines) to a control that inherits from TextBoxBase. /// </summary> [ProvideProperty("SpellCheckEnabled", typeof(TextBoxBase))] public class SpellChecker : Component, IExtenderProvider, ISupportInitialize { // TODO }
The ProvideProperty attribute is what tells Visual Studio to add a SpellCheckEnabled property to controls that inherit from TextBoxBase. When we change the default property value on one of those controls, Visual Studio will call GetSpellCheckEnabled() and SetSpellCheckEnabled() methods. (It does this by well-known name mangling. If I had called it DeathDefyingStuntsEnabled, then Visual Studio would be calling methods named GetDeathDefyingStuntsEnabled() and SetDeathDefyingStuntsEnabled().) So let’s add those to our new class:
/// <summary>
/// A mapping of the TextBoxBase controls that are being extended to a
/// class that contains various information about the control.
/// </summary>
private Dictionary<TextBoxBase, SpellCheckInfo> mControls;
/// <summary>
/// Gets whether or not spell checking is enabled for the given control.
/// </summary>
/// <param name="control">the control to test</param>
/// <returns>whether or not spell checking is enabled for the control</returns>
public bool GetSpellCheckEnabled(TextBoxBase control)
{
return control != null && mControls.ContainsKey(control);
}
/// <summary>
/// Sets whether or not spell checking is enabled for the given control.
/// </summary>
/// <param name="control">the control to enable or disable</param>
/// <param name="enabled">the enabled status</param>
public void SetSpellCheckEnabled(TextBoxBase control, bool enabled)
{
if (control == null)
{
throw new ArgumentNullException("control");
}
if (enabled)
{
mControls.Add(control, new SpellCheckInfo());
}
else
{
mControls.Remove(control);
}
}What I’m doing is having the SpellChecker instance store a dictionary that maps the TextBoxBases that are added by a call to SetSpellCheckEnabled to their corresponding SpellCheckInfo objects. The SpellCheckInfo object is just a quick and dirty class (it could have been a structure, really) that’s used when drawing the wavy red lines underneath the control. I’ll get to how that’s used later, but essentially all you need to know is that it holds the list of misspelled words in the textbox.
Drawing the wavy red lines
So how do we alter the behavior of these textboxes to draw wavy red lines underneath the misspelled words? We obviously can’t just inherit from TextBox. Otherwise, you’d have to drag and drop a new control on your form instead of a regular TextBox, and that would defeat the whole purpose of having the extender provider.
The secret is to use NativeWindow to listen to the messages that get pumped to each registered TextBox. Whenever they repaint themselves, we’ll paint on top of them the wavy red lines that are necessary. The textboxes won’t even know what hit them!
Our extension of NativeWindow, which I’ve called SpellCheckingTextBox, takes a TextBoxBase and our own SpellCheckInfo object in its constructor and attaches itself so that its WndProc method will see all of the messages that are getting pumped to that TextBoxBase. Here’s what our WndProc looks like:
/// <summary> /// Processes Windows messages. /// </summary> /// <param name="m">the message to process</param> protected override void WndProc(ref Message m) { switch (m.Msg) { case (int)WindowsMessages.WM_PAINT: mControl.Invalidate(); base.WndProc(ref m); Repaint(); break; case (int)WindowsMessages.WM_CONTEXTMENU: Point pt; pt = GetPointFromLParam(m.LParam); pt = mControl.PointToClient(pt); if (!DisplaySpellingSuggestionsMenu(pt)) { base.WndProc(ref m); } break; default: base.WndProc(ref m); break; } }
There’s nothing too exotic going on here. When the TextBoxBase is told by Windows to paint itself, we go ahead and let it do that. But then we call our own Repaint() method.
Similarly, if Windows is asking the TextBoxBase control to draw a popup menu (usually because the user right-clicked their mouse), then we intercept that little message and decide if we want to pop up our own menu of spelling suggestions. If we do, we show it at the correct position on screen with our own DisplaySpellingSuggestionsMenu() method and swallow the message. Otherwise, we pass the message onto the TextBoxBase itself by calling base.WndProc().
The Repaint() method is where the fun happens:
/// <summary> /// Draws the red wavy lines under misspelled words. /// </summary> private void Repaint() { Graphics g; g = Graphics.FromHwnd(mControl.Handle); g.Clip = new Region(mControl.ClientRectangle); lock (mMisspelledWords) { foreach (Match word in mMisspelledWords) { int endIndex; Point start; Point stop; endIndex = word.Index + word.Length; start = mControl.GetPositionFromCharIndex(word.Index); // For some reason, GetPositionFromCharIndex() freaks out if called // on the last character in the textbox (that is, the misspelled word // is also the last word). We have to do one character prior and figure // out how long the line should be. if (endIndex == mControl.TextLength) { Size charSize; charSize = TextRenderer.MeasureText(word.Value.Substring(word.Length - 1), mControl.Font); stop = mControl.GetPositionFromCharIndex(endIndex - 1); stop.Offset(charSize.Width, 0); } else { stop = mControl.GetPositionFromCharIndex(endIndex); } start.Offset(0, mControl.Font.Height); stop.Offset(0, mControl.Font.Height); DrawWavyLine(g, start, stop); } } g.Dispose(); }
There’s nothing too hard going on here since the TextBoxBase provides some great methods for determining the pixel positions of words that are contained within it. On a paint event, we just loop through our SpellCheckInfo object (called mMisspelledWords here). For each of those words, we measure the size of the word in the text box and then we call DrawWavyLine at those points, a neat method that I totally lifted from some guy’s blog:
/// <summary> /// Draws a red wavy line at the given start and stop points with the given /// graphics context. Got to give credit where it's due -- this function (and /// pretty much the inspiration for this entire class) is lifted straight /// from the great work of Andrei Alecu at /// http://www.codedblog.com/2007/09/17/owner-drawing-a-windowsforms-textbox/. /// </summary> /// <param name="g">the graphics context</param> /// <param name="start">the start point</param> /// <param name="stop">the stop point</param> private void DrawWavyLine(Graphics g, Point start, Point stop) { Pen pen; pen = Pens.Red; if ((stop.X - start.X) > 4) { List<Point> points; points = new List<Point>(); for (int i = start.X; i <= (stop.X - 2); i += 4) { points.Add(new Point(i, start.Y)); points.Add(new Point(i + 2, start.Y + 2)); } g.DrawLines(pen, points.ToArray()); } else { // If the line is really short, don't bother with // any waves g.DrawLine(pen, start, stop); } }
That’s pretty much the meat of the NativeWindow implementation–it’s concerned about the display and painting of the information in the SpellCheckInfo object. The actual behind-the-scenes spellchecking is performed by a background thread that’s maintained by the SpellChecker extender provider:
/// <summary> /// Performs the spell checking on the control. /// </summary> /// <param name="control">the control</param> private void PerformAsYouTypeSpellChecking(TextBoxBase control) { SpellCheckInfo info; List<Match> misspelledWords; string controlText; MatchCollection words; if (control == null) { throw new ArgumentNullException("control"); } if (control.InvokeRequired) { IAsyncResult result = control.BeginInvoke((ReturnControlTextDelegate)delegate() { return control.Text; }); controlText = (string)control.EndInvoke(result); } else { controlText = control.Text; } if (mControls.TryGetValue(control, out info)) { misspelledWords = new List<Match>(); words = mReWords.Matches(controlText); foreach (Match word in words) { string text; text = word.Captures[0].Value; try { if (!mSpellCheckerProvider.IsWordInDictionary(text)) { misspelledWords.Add(word); } } catch (ApplicationException) { // If the spell checking provider went down, let's not // bother checking it any more this session break; } if (mIsRefreshPending) { break; } } // If the text has changed during the time it took us to process this // request, let's just forget everything and try again if (mIsRefreshPending) { mIsRefreshPending = false; PerformAsYouTypeSpellChecking(control); } else { lock (info.MisspelledWords) { info.MisspelledWords.Clear(); info.MisspelledWords.AddRange(misspelledWords); } control.Invalidate(); } } }
Is it the cleanest code? Probably not–there’s some issues with disposing that are kind of nasty. And there are some optimizations that could be made: for example, the wavy red lines disappear while you’re typing. And it probably breaks down for very long passages of text since it basically rescans the entire textbox every time the text changes. But in general, it works pretty well, especially for an in-house app. Download it and see how well it works for you!
Spell Check Sample Application
If you have any suggestions for improvement, drop me a line. Good luck!




Hi there, this is a great piece of work! Like you I was let down by google until I stumbled across your blog. Ive implemented your code but I have a problem, I want to be able to change the dictionary say from en-GB to en-US. At the moment I am trying to use the property “DictionaryFileName” which changes the dictionary file however the application seems un-effected. It does not update the mispelled words or even change. i.e. Aluminium vs. Aluminum. How do i go about changing this?
Thanks again!
Hmm … I downloaded/dusted off this sample and got the en-GB.dic file to work. Some things to check:
* Include the en-GB.dic file in your project.
* In the properties page, remember to set “Copy to Output Directory” to “Copy if newer”. If it doesn’t get copied, then NetSpell can’t find the dictionary and probably just doesn’t do anything. (I have a feeling this is what the problem is.)
* Remember to change the DictionaryFileName property on the spell checker provider to en-GB.dic.
Good luck!
Thanks for the quick reply! I must have explained my problem poorly, ill try again :-)
Getting the sample to work isnt an issue or changing the dictionary to another .dic file BEFORE debug, but if I try to change this while debugging it doesnt do anything. Any Ideas??
Thanks
Mmm. It doesn’t support changing the dictionary at runtime. But I bet if you went to set accessor for DictionaryFileName in the NetSpellSpellCheckerProvider and added a call to mSpelling.Dictionary.Initialize(), it would work. Something like:
public string DictionaryFileName { get { return mSpelling.Dictionary.DictionaryFile; } set { mSpelling.Dictionary.DictionaryFile = value; mSpelling.Dictionary.Initialize(); } }If you look at the NetSpell implementation, it lazily initializes the dictionary on the first spell check. This just worked because on a typical application startup, this property is set before the first spell checking operation is invoked. If we diddle with the dictionary file name, we need to tell it to rebuild its dictionary.
Hope that helps.
Ah! Of course!! I was nearly there just forgot to re- initialize LOL Thanks for all your help!! and again great bit of code
Sorry 2 quick things:
1.
I now need it to refresh the spell check, redraw the wavy lines. It only updates with keyboard input I cant see a method to produce this, and refreshing or invalidating the control doesnt work.
2.
Is there a way to turn the autoSpell fucntion off, again I tried to use the method “setSpellCheckEnabled”
but this seems to turn off but not turn it back on again.
Any suggestions?
Some thoughts:
1. The spell checker attaches to the textbox’s TextChanged event. It sets a timer to delay for a few seconds (in case the user is still typing quickly). It then rescans all of the text and searches for invalid words. When it’s done, it calls Refresh() on the textbox. This causes Windows to send a WM_PAINT method. The NativeWindow inheritor sees this WM_PAINT in its WndProc method, lets the textbox paint itself, and then paints its wavy red lines on top. If a Refresh() isn’t working for you, then something bizarre is going on, or I’ve got a bug in there somewhere.
2. I never really designed this to be turned off and on at runtime because I didn’t need this functionality. But it shouldn’t be too hard to rework the SpellChecker class to support this. The main issue is that by using SetSpellCheckEnabled() you are indeed adding it to the SpellChecker’s internal list of textboxes, but it isn’t registering with the TextChanged and Disposed event of the textbox to see any text changes. That happens in the EndInit() method that is usually called at the end of the InitializeComponent() method of the form. You’ll have to create a version of EndInit() that you can call after calling SetSpellCheckEnabled(), one that hooks up any newly-added textboxes but doesn’t double-hook up any existing ones. (You shouldn’t add it within SetSpellCheckEnabled() itself because the various textboxes and their handles are not guaranteed to be in a valid state in the InitializeComponent() method of the form.)
Hope that gives some insight. I think that the classes are a good starting point, but they certainly aren’t feature complete and may take some TLC on your part to get them working the way you want. Have at it!
Thanks for the advice i’ll have a play around and see what I can come up with! :-) Thanks again for all the help its very much appreciated!
Just to let you know made a easy work around for the refresh issue, I simply created a public method which does exactly the same as the attached text changed event, therefore you can call it at any time. i.e:
public void RefreshControl(object sender)
{
TextBoxBase control;
SpellCheckInfo info;
control = sender as TextBoxBase;
if (mSpellCheckerProvider != null)
{
if (control != null && mControls.TryGetValue(control, out info))
{
info.LastTextChangeTimestamp = DateTime.Now.Ticks;
lock (info.MisspelledWords)
{
info.MisspelledWords.Clear();
}
// Re-launch idle wait thread if necessary
if (info.IdleTimestampCheckerThread == null
|| info.IdleTimestampCheckerThread.ThreadState == ThreadState.Stopped)
{
ParameterizedThreadStart threadStart;
threadStart = new ParameterizedThreadStart(WaitForIdleTimeToElapse);
info.IdleTimestampCheckerThread = new Thread(threadStart);
info.IdleTimestampCheckerThread.Start(control);
}
else
{
mIsRefreshPending = true;
}
}
}
control.Refresh();
}
Great! I’m glad that it’s working out for you. Unless I state otherwise, everything on my blog is public domain so you can modify it and use it to your heart’s content. Good luck.
Your blog has some great information for small businesses. Thanks for taking the time to share your knowledge!
Hello, my english is bad, i want to know how to change the dictionary to spanish.
Thank you
Hi,
I want my spell checker should ignore number like phone number/mobile number and date like 12/04/2011
Currently it is showing error with red line under all these number.
Please suggest some way-out for this problem
Thanks
We have succesfully used this in several of our applications, and it simply rocks. However, the only annoyance is the ‘flicker’ you get when typing in long text. We integrated this into our chat program, and when you type in about 2 sentences, you begin to see a nice flicker effect. Probably due to the refresh or repaint.
I assume there is no way around this?