Click here to Skip to main content
15,886,919 members
Articles / Desktop Programming / Windows Forms

i00 Spell Check and Control Extensions - No Third Party Components Required!

Rate me:
Please Sign up or sign in to vote.
4.95/5 (117 votes)
11 Jan 2014Ms-PL16 min read 1.3M   22   266   885
Simple to use, open source Spell Checker for .NET
Image 1 Anyone wishing to use this code in their projects may do so, however are required to leave a post on this page stating that they are using this.
A simple "I am using i00 Spell check in my project" will suffice.

Introduction

I wanted a spell check that I could use in .NET, so like most people would have done, I Googled. After many hours of fruitless searching, I decided to make my own; sure there are plenty of spell checkers out there, but I didn't want one that relied on 3rd party components such as Word or require Internet connectivity to work. Introducing i00 .NET Spell Check, the first and only VB.NET Spell Check written completely in VB! Not only that, it is also open source, and easy to use.

Eventually, this project progressed even further into a generic control extension plugin that provides plugins for text box printing, translation, speech recognition and dictation plus more; while also providing a simple method for users to write their own extensions.

Screenshots 

Spell check with definitions

Image 2

In-menu word definitions and Change to...

Image 3

Adding words to dictionary

Image 4

Custom content menus

Image 5

Crossword generator

Image 6

Options

Image 7

Owner draw and RTB support

Image 8

Spell check dialog

Image 9

Support for DataGridViews!

Image 10

Plugin support ... with Label plugin

Image 11

Plugin support ... with FastColoredTextBox plugin

Image 12

Implementation

To implement i00 .NET Spell Check into your project, first either:

  • Add the i00SpellCheck project to your solution and reference it (recommended)
  • Reference the i00SpellCheck.exe file that is output from this project*
  • or you can bring all of *.vb files in the "SpellCheck\Spell Check" folder (from the zip) directly into your own project (VB.Net only)*

NOTE: For the methods with the * you will need to also copy the dictionary files to the applications path

Next, simply place this at the very top of your form:

VB.NET
Imports SpellCheck.i00SpellCheck
C#
using i00SpellCheck;

 

Now you will be able to enable control extensions by going:

VB.NET
Me.EnableControlExtensions()
C#
this.EnableControlExtensions(null);

The above line will enable control extensions on all controls that are supported on the form, and all owned forms that are opened.

 

Some other examples are below:

VB.NET
'To load a single control extension on a control call: 
ControlExtensions.LoadSingleControlExtension(TextBox1, New TextBoxPrinter.TextBoxPrinter)

'To enable spell check on single line textboxes you will need to call:
TextBox1.EnableSpellCheck()

'If you wanted to pass in options you can do so by handling the ControlExtensionAdding event PRIOR to calling EnableControlExtensions:
AddHandler ControlExtensions.ControlExtensionAdding, AddressOf ControlExtensionAdding
'Also refer to the commented ControlExtensionAdding Sub in this form for more info

'You can also enable spell checking on an individual Control (if supported):
TextBox1.EnableSpellCheck()

'To disable the spell check on a Control:
TextBox1.DisableSpellCheck()

'To see if the spell check is enabled on a Control:
Dim SpellCheckEnabled = TextBox1.IsSpellCheckEnabled()
'To see if another control extension is loaded (in this case call see if the TextBoxPrinter Extension is loaded on TextBox1):
Dim PrinterExtLoaded = TextBox1.ExtensionCast(Of TextBoxPrinter.TextBoxPrinter)() IsNot Nothing

'To change spelling options on an individual Control:
TextBox1.SpellCheck.Settings.AllowAdditions = True
TextBox1.SpellCheck.Settings.AllowIgnore = True
TextBox1.SpellCheck.Settings.AllowRemovals = True
TextBox1.SpellCheck.Settings.ShowMistakes = True
'etc

'To set control extension options / call methods from control extensions (in this case call Print() from TextBox1):
Dim PrinterExt = TextBox1.ExtensionCast(Of TextBoxPrinter.TextBoxPrinter)()
PrinterExt.Print()

'To show a spellcheck dialog for an individual Control:
Dim iSpellCheckDialog = TryCast(TextBox1.SpellCheck, i00SpellCheck.SpellCheckControlBase.iSpellCheckDialog)
If iSpellCheckDialog IsNot Nothing Then
    iSpellCheckDialog.ShowDialog()
End If

'To load a custom dictionary from a saved file:
Dim Dictionary = New i00SpellCheck.FlatFileDictionary("c:\Custom.dic")

'To create a new blank dictionary and save it as a file
Dim Dictionary = New i00SpellCheck.FlatFileDictionary("c:\Custom.dic", True)
Dictionary.Add("CustomWord1")
Dictionary.Add("CustomWord2")
Dictionary.Add("CustomWord3")
Dictionary.Save()

'To Load a custom dictionary for an individual Control:
TextBox1.SpellCheck.CurrentDictionary = Dictionary

'To Open the dictionary editor for a dictionary associated with a Control:
'NOTE: this should only be done after the dictionary has loaded (Control.SpellCheck.CurrentDictionary.Loading = False)
TextBox1.SpellCheck.CurrentDictionary.ShowUIEditor()

'Repaint all of the controls that use the same dictionary...
TextBox1.SpellCheck.InvalidateAllControlsWithSameDict()




''This is used to setup spell check settings when the spell check extension is loaded:
Private Sub ControlExtensionAdding(ByVal sender As Object, ByVal e As ControlExtensionAddingEventArgs)
    Dim SpellCheckControlBase = TryCast(e.Extension, SpellCheckControlBase)
    If SpellCheckControlBase IsNot Nothing Then
        Static SpellCheckSettings As i00SpellCheck.SpellCheckSettings 'Static for settings to be shared amongst all controls, use dim for control specific settings...
        If SpellCheckSettings Is Nothing Then
            SpellCheckSettings = New i00SpellCheck.SpellCheckSettings
            SpellCheckSettings.AllowAdditions = True 'Specifies if you want to allow the user to add words to the dictionary
            SpellCheckSettings.AllowIgnore = True 'Specifies if you want to allow the user ignore words
            SpellCheckSettings.AllowRemovals = True 'Specifies if you want to allow users to delete words from the dictionary
            SpellCheckSettings.AllowInMenuDefs = True 'Specifies if the in menu definitions should be shown for correctly spelled words
            SpellCheckSettings.AllowChangeTo = True 'Specifies if "Change to..." (to change to a synonym) should be shown in the menu for correctly spelled words
        End If
        SpellCheckControlBase.Settings = SpellCheckSettings
    End If
End Sub
C#
//To load a single control extension on a control call: 
ControlExtensions.LoadSingleControlExtension(TextBox1, New TextBoxPrinter.TextBoxPrinter());

//To enable spell check on single line textboxes you will need to call:
TextBox1.EnableSpellCheck(null);

//If you wanted to pass in options you can do so by handling the ControlExtensionAdding event PRIOR to calling EnableControlExtensions:
ControlExtensions.ControlExtensionAdding += ControlExtensionAdding;
//Also refer to the commented ControlExtensionAdding Sub in this form for more info

//You can also enable spell checking on an individual Control (if supported):
TextBox1.EnableSpellCheck(null);

//To disable the spell check on a Control:
TextBox1.DisableSpellCheck();

//To see if the spell check is enabled on a Control:
bool SpellCheckEnabled = TextBox1.IsSpellCheckEnabled();
//To see if another control extension is loaded (in this case call see if the TextBoxPrinter Extension is loaded on TextBox1):
var PrinterExtLoaded = TextBox1.ExtensionCast<textboxprinter.textboxprinter>() != null;

//To change options on an individual Control:
TextBox1.SpellCheck(true, null).Settings.AllowAdditions = true;
TextBox1.SpellCheck(true, null).Settings.AllowIgnore = true;
TextBox1.SpellCheck(true, null).Settings.AllowRemovals = true;
TextBox1.SpellCheck(true, null).Settings.ShowMistakes = true;
//etc

//To set control extension options / call methods from control extensions (in this case call Print() from TextBox1):
object PrinterExt = TextBox1.ExtensionCast<textboxprinter.textboxprinter>();
PrinterExt.Print();

//To show a spellcheck dialog for an individual Control:
var iSpellCheckDialog = TextBox1.SpellCheck(true,null) as i00SpellCheck.SpellCheckControlBase.iSpellCheckDialog;
if (iSpellCheckDialog != null) {
    iSpellCheckDialog.ShowDialog();
}

//To load a custom dictionary from a saved file:
i00SpellCheck.FlatFileDictionary Dictionary = new i00SpellCheck.FlatFileDictionary("c:\\Custom.dic", false);

//To create a new blank dictionary and save it as a file
i00SpellCheck.FlatFileDictionary Dictionary = new i00SpellCheck.FlatFileDictionary("c:\\Custom.dic", true);
Dictionary.Add("CustomWord1");
Dictionary.Add("CustomWord2");
Dictionary.Add("CustomWord3");
Dictionary.Save(Dictionary.Filename, true);

//To Load a custom dictionary for an individual Control:
TextBox1.SpellCheck(true, null).CurrentDictionary = Dictionary;

//To Open the dictionary editor for a dictionary associated with a Control:
//NOTE: this should only be done after the dictionary has loaded (Control.SpellCheck.CurrentDictionary.Loading = False)
TextBox1.SpellCheck(true, null).CurrentDictionary.ShowUIEditor();

//Repaint all of the controls that use the same dictionary...
TextBox1.SpellCheck(true, null).InvalidateAllControlsWithSameDict(true);




//This is used to setup spell check settings when the spell check extension is loaded:
static i00SpellCheck.SpellCheckSettings SpellCheckSettings = null;//Static for settings to be shared amongst all controls, use "i00SpellCheck.SpellCheckSettings SpellCheckSettings = null;" in the method below for control specific settings...
private void ControlExtensionAdding(object sender, i00SpellCheck.MiscControlExtension.ControlExtensionAddingEventArgs e)
{
    var SpellCheckControlBase = e.Extension as SpellCheckControlBase;
    if (SpellCheckControlBase != null)
    {
        //i00SpellCheck.SpellCheckSettings SpellCheckSettings = null;
        if (SpellCheckSettings == null)
        {
            SpellCheckSettings = new i00SpellCheck.SpellCheckSettings();
            SpellCheckSettings.AllowAdditions = true; //Specifies if you want to allow the user to add words to the dictionary
            SpellCheckSettings.AllowIgnore = true; //Specifies if you want to allow the user ignore words
            SpellCheckSettings.AllowRemovals = true; //Specifies if you want to allow users to delete words from the dictionary
            SpellCheckSettings.AllowInMenuDefs = true; //Specifies if the in menu definitions should be shown for correctly spelled words
            SpellCheckSettings.AllowChangeTo = true; //Specifies if "Change to..." (to change to a synonym) should be shown in the menu for correctly spelled words
        }
        SpellCheckControlBase.Settings = SpellCheckSettings;
    }
}</textboxprinter.textboxprinter></textboxprinter.textboxprinter>

Even more examples are included in the Test project in the download.

Plugins

Since version 20120618 i00SpellCheck has plugin support.

Plugins in i00SpellCheck allow components to be spell checked by making a dll or exe file that contains a public class that inherits i00SpellCheck.SpellCheckControlBase.

They automatically get picked up and allow the spellchecking of extra controls, with no reference to the file itself required. However you will need to place them in the applications path.

The use of plugins allow users to enable spellchecking of their controls, without having to change the i00SpellCheck project.

Another use for them could be to allow the programmer to quickly see if they have spelling errors on forms etc. For example placing the LabelPlugin.exe file in an application path (that already uses i00SpellCheck) will cause all labels in the existing project to be spell checked ... with NO code changes! When the developer wants to deploy their application they simply need to remove the LabelPlugin.exe file, and labels will no longer be corrected.

The basic procedures for creating a plugin are as follows:

  • Start by creating a new project (class library or exe)
  • Reference i00SpellCheck
  • Make a class that inherits i00SpellCheck.SpellCheckControlBase
  • Override the ControlType Property to return the type of control that you want your plugin to spell check
  • Add your code

For examples on inheriting i00SpellCheck.SpellCheckControlBase check out the examples in the Plugins path in the download.

Projects

The included projects and a brief description of each are as follows:

  • i00SpellCheck - Contains the classes for the spellcheck core / TextBox and DataGridView plugins plus the core components required for other plugins to work
  • Plugins\LabelPlugin - Contains a plugin that checks the spelling of Labels
  • Plugins\OSControlRenderer - Contains aplugin that renders the TreeView and ListViews to the same that they appear in Windows
  • Plugins\SelectedControlHighlight - Contains a plugin that extends a variety of Controls so that they appear to have a "glow" when selected
  • Plugins\TextBoxPrinter - Contains a plugin that extends TextBoxBase to support printing
  • Plugins\TextBoxSpeechRecognition - Contains a plugin that extends TextBoxBase to include support for voice input (double tap F12) and dictation
  • Plugins\TextBoxTranslator - Contains a plugin that extends TextBoxBase to support Google Translation
  • Plugins\3rd Party\FastColoredTextBoxPlugin - Contains a plugin that checks the spelling of the FastColoredTextBox control by Pavel Torgashov
  • Tests\BasicTest - A test project that contains basic implementation of i00 SpellCheck
  • Tests\CSharpTest - A test project demonstrating the use of i00 Spell Check in C#
  • *Tests\Test - A test project that contains advanced implementation of i00 SpellCheck and control extensions
  • Tests\3rd Party Dictionaries\HanksDictionaryTest - A test project that demonstrates the use of 3rd party spelling engines in i00 Spell Check. Hank's dictionary in this case (by tewuapple (Hank))
  • Tests\3rd Party Dictionaries\OpenOfficeHunspellDictionaryTest - A test project that demonstrates the use of 3rd party spelling engines in i00 Spell Check. Hunspell in this case which has support for open office dictionaries
  • Tests\3rd Party Dictionaries\WordDictionaryTest - A test project that demonstrates the use of 3rd party spelling engines in i00 Spell Check. Microsoft Word in this case

* = default startup project

Points of Interest

The words that get checked are added to a dictionary cache to speed up checking - the smaller cache is checked first. If the word is not found in the cache, then it checks the main dictionary.

I use fields (public variables) instead of properties for some basic classes, as they are about 2x faster than properties.

Downloads

Total Downloads: Image 13

Downloads per day:
Image 14

Change Log

(Bold items are things that end users will notice)

20140111

i00SpellCheck
  • Fixed single line textbox issue where EnableSpellCheck would be required to be needed 2x (found by hoodch)
  • Updated the plugin manager to the latest ... now supports paths and shadow copying (this is not implemented in this project)
  • Added help button to spell check dialog (for people who don't know about holding F1)
Test
  • Made enabled button work for each tab on demo form
TextBoxSpeechRecognition
  • Fixed a few issues with the dictation where it would not put in the dictated text
  • Enter now commits dictated text
KonamiCode WebpageImplementation
  • Started implementation for a web interface (currently at a very basic stage)

20130521

i00SpellCheck
  • Fixed bug that would occur when multiple textboxbases shared a context menu (found by Adviser)
  • DataGridView CellStyle no longer has to be set to WrapMode.True for corrections to appear in the DataGridView
  • Fixed a possible rendering issue where the error underlines would not be drawn in some instances
  • Fixed a possible bug where, on some rare occasions, the spell check dialog would error upon opening (found by TheComputerMan08 (Brent))
  • Fixed an issue where words starting with a capital were not getting picked up by the spell check
  • Fixed a bug where if selecting a new dictionary with the default ShowUIEditor function the control would not clear old correct words from cache
  • Now automatically spell checks controls in SplitContainers (found by cognositivo)
FastColoredTextBoxPlugin
  • Added HTML example to FCTB with html color highlighting
  • Updated FCTB to latest version
  • Fixed an issue with FCTB where when inserting text all of the text would be spellchecked instead of just the visible range, speeding up large copy and pastes
HanksDictionaryTest
  • Added another example of using a different dictionary with i00 Spell Check ... Hanks Dictionary (by tewuapple (Hank))
WordDictionaryTest
  • Added another example of using a different dictionary with i00 Spell Check ... Word Dictionary

20130114

i00SpellCheck
  • Added engine to make more generic control extensions
  • Changed the workings of SpellCheckControlBase to use the more generic control extensions
  • Default dictionary load is now threadded even when just calling .SpellCheck
  • Control extensions can now specify multiple ControlTypes
  • Put the TextBoxBase change case feature into its own control extension
  • Put the nicer TextBoxBase context menu into its own control extension
  • Made the window animations smoother and more stable for F7 etc
  • Control extensions can now be dependant on other control extensions (like references but within control extensions)
TextBoxPrinter
  • Added TextBoxPrinter plugin
TextBoxSpeechRecognition
  • Added buttons to trigger dictate and speech
  • Custom Karaoke rendering control added to test form
  • Now uses the new, more generic, control extension rather than extending SpellCheckTextBox
  • Speech is no longer "broken up" at the end of each line in Windows 8
OSControlRenderer
  • Added OSControlRenderer plugin
SelectedControlHighlight
  • Added SelectedControlHighlight plugin
TextBoxTranslator
  • Added TextBoxTranslator plugin
Test
  • Neatened up Draft Plan rendering

20121102

i00SpellCheck
  • Made SpellCheckDialog more universal (so it can be used with other controls more easily)
  • Fixed a bug when using the SpellCheckDialog to spell check where the textbox would flicker and repaint several times upon confirming the changes
  • Added IgnoreWordsInUpperCase setting (requested by TheMperor)
  • Added IgnoreWordsWithNumbers setting (requested by TheMperor)
  • Fixed a bug that would cause the balloon tooltip to stuffup if it was on a screen to the left of the primary screen, this could cause the spell check dialog to crash
Test
  • Added some options to the Performance Monitor window
OpenOfficeHunspellDictionary
  • Hunspell now has case error underlineing
  • Added Hunspell syninoum lookup to Hunspell test project
TextBoxSpeechRecognition
  • Tray icon appears when using speech
  • Added a karaoke style content menu item when speaking
  • Now when triggering speek or dictate all instances of speech (across all applications that use i00 spell check) are terminated so that you can't get "multiple people" talking at once

20120920

  • Added performance counter
  • Fixed a bug with the built in dictionary where the dictionary index would be filled up with user words and potentially cause errors, this also has speed up spellchecking a lot
  • Fixed an issue with the spell check dialog, if you had alot of data it would "freeze"

20120914

  • Added Redo to context menu for rich text boxes
  • Changed the way the items are added to the spell check text box content menus to make them more expandable
  • TextBoxSpeechRecognition now adds menu items to TextBoxBase context menus
  • Added properties to TextBoxSpeechRecognition to adjust various settings
  • Fixed a bug that would cause an error when getting sugguestions for a word, where no sugguestsions could be made

20120907

  • Removed some redundant stuff from the project
  • Added a button to the test form that brings up the dictionary editor
  • Changed the way the flat files dictionary index is stored
  • Changed the way the flat files dictionary is stored in memory
  • Changed the dictionary alot to allow for easy user dictionary creation for inherreted dictionary classes
  • Added a C# test project
  • Added a test project to demonstrate how people can use other spelling engines in i00 Spell Check. Hunspell in this case which has support for open office dictionaries

20120903

  • Added SpellCheckControlAdding event that allows you to pass back e.Cancel = True to prevent the control from being checked
  • Fixed a bug that could produce an error if you call .EnableSpellCheck multiple times on several forms (found by rfreedlund)
  • Indexed dictionary - added slightly to the initial loading time... but sped up checking significantly (requested by Maverickz)
  • Fixed a rare occuring bug where an error would be thrown with the spellcheck cache
  • User dictionary file is now separate from the built in dictionary
  • Ignored words are now stored in the user dictionary
  • Updated the FlatFile dictionary editor to support the new user dictionary
  • Added i00Binding List to the project for the FlatFile Dictionary editor... you can remove the reference, if you want, but will first have to remove the dictionary editor if you don't require it (found in i00SpellCheck\Spell Check\Engine\Dictionary\Flat File\Editor)
  • Changed the dictionary dramatically to support custom classes to enable the checking of other dictionary formats
  • Added a plugin that extends the SpellCheckTextbox, adding voice recognition to it! However Microsoft's inbuilt speech recognition isn't great. Press F12 twice quickly to perform speech recognition
  • Made built in plugins more extendable

20120625

  • Changed the way the words get added to the dictionary cache (increased spell checking speed)
  • Improved the speed of the FastColoredTextBox Plugin
  • Made the test project automatically pickup any plugins in the project path and add them to tabs automatically - the references to the plugins are not required, they were added for the LabelPlugin and FastColoredTextBoxPlugin so that they automatically get placed in the same folder when the project is built!

20120622 - FastColoredTextBox

  • Changed some internal workings of the spell checker
  • Added support for FastColoredTextBox with included plugin!

20120618 - Plugins!

  • Added test plugin to project that grants the ability to spell check labels
  • Changed the structure / inner workings of the spell check alot / made the spell check more modular and plugins possible!
  • Fixed a bug where the settings were not being applied under certain circumstances to spell check controls
  • Grid view spell checking now is shown on cells even when their not being edited

20120609 - DataGridView's are go!

  • Added support for DataGridViews (requested by in2tech)
  • Fixed a bug where the underlines would not draw in the correct positions when word wrapping is disabled
  • Fixed a bug where single line TextBoxes would not always show spelling errors

20120608 - Disabling and bug fixes

  • Fixed a bug where if you had an apostrophe at the start of a word it would push the underline spacing out (found by jwinney)
  • Added ability to disable the spell check on a TextBox (requested by Gabriel X)
  • Fixed a bug where the standard TextBox was not refreshing properly since the new rendering method was added
  • Fixed bug where if a TextBox contained errors and all text was deleted the underlines would still show (found by jim400)
  • Some minor interface changes with splash screen and "alt" for menu

20120203

  • Tooltips now have image support and images for some definitions
  • Fixed tool tip rendering issues
  • You can now press F3 to change case of the selected text (requested by rykk)
  • Made "-" be classified as a word break char
  • Fixed an error that would re-paint the textbox errors 2x
  • Disabled Cut/Copy/Paste options in menu if not on an STA thread as this would error

20120102 - Happy New Year!

  • Modified definitions to lookup from file dynamically rather than being loaded into memory to reduce RAM usage ~56MB saved!
  • Changed settings so that all the spellcheck settings are in a single class
  • Cleaned up the SpellCheckTextBox class and subclasses to make settings easily editable with a property grid
  • Added property grid to the test project
  • Added a dictionary editor
  • Added a "bare-bones" test project to the solution, to make it simpler for users to see how easy it can be to use i00 .NET Spell Check in your projects!
  • Changed the render method to eliminate redraw flicker, added a setting to revert to the old render method "RenderCompatibility"

20111202 - Now with dialog!

  • Cleaned up some stuff ... moved HTML formatted tooltip + HTML ToolStripItem into their own controls
  • Made the Text ToolStripSeperator look and function better
  • Made the right click menu items portable so that they can be added to any menu for other things - not so tightly bound to the text box
  • Implementing a spell check dialog for F7 style spell checking (select text box and press F7!... can also be called with: TextBoxName.SpellCheck.ShowDialog())

20111109 - In-menu definitions, synonyms and fixes!

  • Changed tooltip definitions to match more words from their word base, e.g. "suggestions" matches "suggestion" for definition since no definition is matched explicitly for suggestions and states that it is plural in the tip
  • Tooltip for definitions is now owner draw so that it appears a little nicer
  • Fixed a case matching bug where "This" would suggest "this" rather than "This" (requested by TxDeadhead)
  • Words like "Chris's" now suggest "Chris'" instead of "Chris's"
  • Words that end in an ' no longer appear as being misspelled
  • Made the context menu position itself a little better if near the bottom or right sides of a screen
  • Fixed a bug where, if you press the context menu button on the keyboard multiple times, the menu would add multiple of the same corrections to the context menu
  • Sped up loading of dictionary file
  • Fixed a bug in the definition file - all adjectives and adverbs were mixed up (i.e., all adjectives were listed as adverbs, and all adverbs were listed as adjectives)!
  • Various speed optimizations in finding word suggestions, to lookup misspelled word "suggestions" used to take ~250ms, now down to ~150ms
  • Improved suggestion lookup now adds higher weight to words with extra duplicates or missing duplicates (such as "running", "running" > "running")
  • Added in-context-menu definitions for correctly spelled words (requested by Dean)
  • Words with interesting cases (such as SUpport, SupporT etc) now get picked up (requested by TxDeadhead)
  • Now doesn't fall over if the dictionary, definitions or synonyms files have been removed - just removes functionality for that bit
  • Added synonyms; "Change to..." menu item (requested by NtEditor)

20111106 - Been busy!

  • Various speed optimizations
  • Dictionary is now stored as a Flat File for portability
  • Added owner draw support for spelling errors
  • Added the ability to customize colors for highlighting misspelled words
  • Added some examples of how to customize the appearance of the spell check
  • Word definitions added for spelling suggestions ... so if you are unsure of the correct spelling out of the suggestions, you can pick the correct one from the definition
  • The right click menu in .NET comes up from the middle of a text box when pressing the context menu button on the keyboard - It has now been modified to pop-out from the carets location
  • Cross word generator - plan to make solver later too!
  • Support added for Rich Text Boxes
  • Added Suggestion Lookup Example

20111011 - Some fun extras

  • Added anagram lookup
  • Added Scrabble helper

20111008 - Minor changes

  • Fixed a bug where the text box underlines would not always draw initially until the textbox was scrolled or had some text changed
  • Cleaned up the interface to made it look more professional

20111006

  • Initial Release

Possible Issues

SpellCheckTextBox

Since the Textbox has no way to really draw on it "nicely", I used to capture the WM_PAINT of the control and then draw on the textbox graphics that was set by going Graphics.FromHwnd... this seemed to work well but produced a slight flicker that I thought was undesirable...

As of version 20120102, the render method now uses layered windows (by default), this basically means that all of the underlines that appear to be drawn on the control are actually drawn on another window over the top of the control ...

So how does this affect the user? Well in most cases it doesn't, the form is click-through and only the drawings are visible not the form itself. In fact if you press start + tab in Windows Vista / 7 to activate Flip3D, it even appears on the same window!

As I said above "in most cases"...

MIDI forms I haven't tested, but am quite sure that they won't work using the new render method.

With overlapping textbooks the text corrections on the underneath control "float"over the control that is on top, and if the textbox is off the form, the corrections "float" off the form!

So in cases such as the above, you will have to go back to the older "compatible" rending, this can be done in these cases by going:

C#
DirectCast(TextBox.SpellCheck, SpellCheckTextBox).RenderCompatibility = True

Thanks

A special thanks to Pavel Torgashov for his excellent FastColoredTextBox control. This control is used in the solution to test i00SpellCheck's plugin architecture with 3rd party controls. i00 has not modified this control in any way and is only responsible for integrating the spell checking ability to it via an i00SpellCheck plugin. In no way is this control required for spell checking functions in other projects within the solution.

Thanks for downloading.

Suggestions on possible improvements are much appreciated.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
i00
Software Developer (Senior) i00 Productions
Australia Australia
I hope you enjoy my code. It's yours to use for free, but if you do wish to say thank you then a donation is always appreciated.
You can donate here.

Comments and Discussions

 
GeneralRe: No easy way to bring *.vb files directly into project Pin
i0010-Feb-20 15:14
i0010-Feb-20 15:14 
QuestionI am using i00 spell check in my project Pin
Member 1461003829-Oct-19 15:58
Member 1461003829-Oct-19 15:58 
QuestionI am using i00 spell check in my project Pin
Tom00729-Oct-19 12:20
Tom00729-Oct-19 12:20 
QuestionConsideration when using this tool inside custom controls Pin
Robert Gustafson26-Oct-19 19:21
Robert Gustafson26-Oct-19 19:21 
AnswerRe: Consideration when using this tool inside custom controls Pin
Robert Gustafson18-Nov-19 10:57
Robert Gustafson18-Nov-19 10:57 
QuestionI am using i00 Spell Check in my project Pin
Robert Gustafson23-Oct-19 1:20
Robert Gustafson23-Oct-19 1:20 
QuestionSome issues with text-changing and RECOMMENDED CHANGES Pin
Robert Gustafson17-Oct-19 18:05
Robert Gustafson17-Oct-19 18:05 
AnswerADDITIONAL RECOMMENDED CHANGES (for RichTextBox-es) Pin
Robert Gustafson11-Oct-22 16:32
Robert Gustafson11-Oct-22 16:32 
Another deficiency I've noticed with i00SpellCheck is its inability to properly handle text within a rich-text box that is protected or hidden (invisible). Fortunately, I've figured out a fix for that--which, unfortunately, requires making strategic changes in several code files. First, I'll explain the change in functionality that my modifications will do:

1. It causes the SpellCheckDialog to skip over any and all cases of a word whose text protected or semi-protected, and, unless otherwise directed, any and all cases of a word whose text is hidden or semi-hidden.
2. It prevents context-menu options from being added when the user right-clicks on a word in a rich-text box which is protected/semi-protected.
3. It still draws a wavy line, when that feature is enabled, under "misspelled" words that are wholly or partially protected/hidden. One can use the extension methods below to disable that line-drawing in such cases, but I've found that that slows down the rendering of rich text. If you want to keep wavy lines from appearing under protected/hidden text, you should modify the Drawing.vb file's CustomPaint procedure to invoke parentTextBox.SetRedraw(False) and Dim ScrollPosition As Point = parentTextBox.GetScrollPosition() before it goes through the text, check the value of parentTextBox.GetTextType(LetterIndex, words(iWord).Length) as well as (the already-checked-for) e.Default before drawing a line, then invoke parentTextBox.SetScrollPosition(ScrollPosition) and parentTextBox.SetRedraw(True) after the text is done being parsed. (Better yet, use a Try block with after redrawing is turned off and the scroll position is saved, with the resetting of these attributes occurring the Finally clause in order to ensure that the text box can redraw and is in the correct scroll position after CustomPaint finishes even if an exception occurs during the word-parsing.)

First of changes:
In the SpellCheck\Engine\Extension.vb file, create a new module, named ExtendedTextReading, and move the ExtendedText and FullSelectionLength extension methods thereto. Also, add the SetRedraw, GetScrollPosition, SetScrollPosition, HasFlag, (only included because this method doesn't come automatically with pre-4.0 versions of .NET!), and GetTextType extension methods, and the various Private components--as follows:

BASIC
#Region "Extended Text Reading"
 Public Module ExtendedTextReading
	'   private components

	'      constants
	Private Const WM_USER As Int32 = &H400&
	Private Const EM_GETSCROLLPOS As Integer = WM_USER + 221, _
		EM_SETSCROLLPOS As Integer = WM_USER + 222
	Private Const WM_SETREDRAW As Integer = &HB
	Private Const HiddenTextSyntax As String = "(^|[^\\])(\\\\)*\\v[^0]", _
		ProtectedTextSyntax As String = "(^|[^\\])(\\\\)*\\protect[^0]"

	'      variables
	Private HiddenTextChecker As Regex = _
			New Regex(HiddenTextSyntax, _
				RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture), _
		ProtectedTextChecker As Regex = _
			New Regex(ProtectedTextSyntax, _
				RegexOptions.IgnoreCase Or RegexOptions.Compiled Or RegexOptions.ExplicitCapture)

	'      DLL imports

	'         for scrolling
	<DllImport("user32.dll")> _
	Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
		ByVal wParam As Int32, ByRef lParam As Point) As Int32
	End Function
	'         for auto-redraw setting
	<DllImport("user32.dll")> _
	Private Function SendMessage(ByVal hWnd As IntPtr, ByVal msg As Int32, _
		ByVal wParam As Integer, ByVal lParam As Integer) As Int32
	End Function

	'   public components

	'      enums

	''' <summary>
	''' Enum to flag whether a word contains hidden and/or protected text
	''' </summary>
	<Flags()> _
	Public Enum TypeOfText
		Normal = 0
		Hidden = 1
		[Protected] = 2
	End Enum

	'      extension methods

	''' <summary>
	''' Determine if TypeOfText value has certain flags
	''' (This method isn't automatically supported until .NET Framework 4!)
	''' </summary>
	''' <param name="TypeFlag">TypeOfText flags to text</param>
	''' <returns>True if all flags in TypeFlag are set in TextType, else False</returns>
	<System.Runtime.CompilerServices.Extension()> _
	Public Function HasFlag(ByVal TextType As TypeOfText, _
		ByVal TypeFlag As TypeOfText) As Boolean
		Return (TextType And TypeFlag) = TypeFlag
	End Function

	''' <summary>
	''' Get current scroll position
	''' </summary>
	''' <returns>Point structure with horizontal and vertical scroll positions</returns>
	<System.Runtime.CompilerServices.Extension()> _
	Public Function GetScrollPosition(TextBox As TextBoxBase) As Point
	'   get scroll position of RichTextBox
	Dim RTBScrollPoint As Point = Nothing
	SendMessage(TextBox.Handle, EM_GETSCROLLPOS, 0, RTBScrollPoint)
	Return RTBScrollPoint
	End Function

	''' <summary>
	''' Set scroll position
	''' </summary>
	''' <param name="RTBScrollPoint">Point structure with horizontal and vertical scroll positions</param>
	<System.Runtime.CompilerServices.Extension()> _
	Public Sub SetScrollPosition(TextBox As TextBoxBase, _
		ByVal RTBScrollPoint As Point)
	'   set scroll position of RichTextBox
	SendMessage(TextBox.Handle, EM_SETSCROLLPOS, 0, RTBScrollPoint)
	End Sub

	''' <summary>
	''' Turn redraw mode on or off
	''' </summary>
	''' <param name="OnOrOff">True to enable redrawing, False to disable it</param>
	<System.Runtime.CompilerServices.Extension()> _
	Public Sub SetRedrawMode(TextBox As TextBoxBase, _
		ByVal OnOrOff As Boolean)
	'   set redraw mode
	SendMessage(TextBox.Handle, WM_SETREDRAW, CType(OnOrOff, Integer) And 1, 1)
	If OnOrOff Then
		TextBox.Refresh()
	End If
	End Sub


	''' <summary>
	''' Gets full length of selected text, including any hidden text
	''' </summary>
	''' <returns>Full length of selection</returns>
	''' <remarks>This is different from the SelectionLength property only when
	''' the host program is targeted for .NET Framework 4.7 or later, TextBox is
	''' a RichTextBox, and hidden text is present in document</remarks>
	<System.Runtime.CompilerServices.Extension()> _
	Public Function FullSelectionLength(TextBox As TextBoxBase) As Integer
		If TypeOf TextBox IsNot RichTextBox Then
			'   standard text box
			Return TextBox.SelectionLength
		End If
		'   rich text box--get start and end of selection
		Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
		Dim TypeRTB As Type = RichTextBox.GetType
		Dim fi As FieldInfo = _
			TypeRTB.GetField("curSelStart", _
				BindingFlags.Instance Or BindingFlags.NonPublic)
		Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
		fi = TypeRTB.GetField("curSelEnd", _
			BindingFlags.Instance Or BindingFlags.NonPublic)
		Dim nCurSelEnd As Integer = _
			Math.Min(CType(fi.GetValue(RichTextBox), Integer), RichTextBox.TextLength)
		If nCurSelStart = -1 AndAlso nCurSelEnd = -1 Then
			Return 0
		Else
			Return _
				nCurSelEnd - nCurSelStart
		End If
	End Function

	''' <summary>
	''' Gets full plain text of rich-text box (or selection thereof),
	''' including any hidden text
	''' </summary>
	''' <param name="SelectionOnly">True to get only selected text,
	''' False (default) to get entire text</param>
	''' <returns>Full plain text, be it entire document or selection</returns>
	''' <remarks>This is different from the Text or SelectedText property only when
	''' the host program is targeted for .NET Framework 4.7 or later, TextBox is a
	''' RichTextBox, and hidden text is present in document</remarks>
	<System.Runtime.CompilerServices.Extension()> _
	Public Function ExtendedText(TextBox As TextBoxBase, _
		Optional SelectionOnly As Boolean = False) As String
		If TypeOf TextBox IsNot RichTextBox Then
			'   standard text box
			If SelectionOnly Then
				Return TextBox.SelectedText
			Else
				Return TextBox.Text
			End If
		End If
		'   rich text box--get plain text including any hidden text
		Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
		Dim TypeRTB As Type = RichTextBox.GetType
		'   access private WindowText property
		Dim prop As PropertyInfo = _
			TypeRTB.GetProperty("WindowText", _
				BindingFlags.Instance Or BindingFlags.NonPublic)
		Dim Text As String = _
			CType(prop.GetValue(RichTextBox, {}), String).Replace(ControlChars.Cr, "")
		If SelectionOnly Then
			'   get start and end of selection
			Dim fi As FieldInfo = _
				TypeRTB.GetField("curSelStart", _
					BindingFlags.Instance Or BindingFlags.NonPublic)
			Dim nCurSelStart As Integer = CType(fi.GetValue(RichTextBox), Integer)
			fi = TypeRTB.GetField("curSelEnd", _
				BindingFlags.Instance Or BindingFlags.NonPublic)
			Dim nCurSelEnd As Integer = _
				Math.Min(CType(fi.GetValue(RichTextBox), Integer), Text.Length)
			'   extract substring
			If (nCurSelStart = -1 AndAlso nCurSelEnd = -1) _
					OrElse nCurSelStart = nCurSelEnd Then
				Text = "" 'no selection
			Else
				Text = Text.Substring(nCurSelStart, nCurSelEnd - nCurSelStart)
			End If
		End If
		Return Text
	End Function


	''' <summary>
	''' Determine whether a given word in a text bocx or rich text box
	''' is wholly or partially hidden/protected
	''' </summary>
	''' <param name="StartIndex">Start of word in TextBox</param>
	''' <param name="WordLength">Length of word in TextBox</param>
	''' <returns>Value indicating if word contains hidden text, protected text, both, or neither</returns>
	<System.Runtime.CompilerServices.Extension()> _
	Public Function GetTextType(TextBox As TextBoxBase, _
		ByVal StartIndex As Integer, ByVal WordLength As Integer) As TypeOfText
		Dim TextType As TypeOfText = TypeOfText.Normal 'assume normal at first
		If TypeOf TextBox Is RichTextBox Then
			Dim RichTextBox As RichTextBox = DirectCast(TextBox, RichTextBox)
			With RichTextBox
				'   save current selection and select word
				Dim CurrentStart As Integer = .SelectionStart, CurrentLength As Integer = .SelectionLength
				.Select(StartIndex, WordLength)
				'   set flags as needed
				If HiddenTextChecker.IsMatch(.SelectedRtf) _
						OrElse .SelectionLength < WordLength Then
					TextType = TextType Or TypeOfText.Hidden
				End If
				If .SelectionProtected _
						OrElse ProtectedTextChecker.IsMatch(.SelectedRtf) Then
					TextType = TextType Or TypeOfText.Protected
				End If
				'   restore original selection
				.Select(CurrentStart, CurrentLength)
			End With
		End If
		Return TextType
	End Function


#End Region

Next, make the following additions and modifications to the file HTMLSpellCheck.vb, within class HTMLSpellCheck:
BASIC
	 Private Function CanChangeWord(ByVal StartIndex As Integer, ByVal Length As Integer) As Boolean
	 '   verify that word isn't protected or semi-protected, and
	 '   isn't hidden or semi-hidden unless we can spell-check hidden text
	 If Length = 0 Then
		Return False 'can't spell-check a non-existant word!
	  ElseIf Me.TextBox Is Nothing OrElse TypeOf Me.TextBox Is TextBox Then
		Return True 'standard text box--no protected or hidden text
	 End If
	 '   rich text box--look for hidden/protected text
	 With Me.TextBox
		Dim TextType As TypeOfText = .GetTextType(StartIndex, Length)
		Select Case True
			Case TextType.HasFlag(TypeOfText.Protected)
				Return False 'can't spell-check protected text
			Case TextType.HasFlag(TypeOfText.Hidden)
				Return SpellCheckHiddenText 'can only spell-check hidden text if asked to
			Case Else
				Return True 'can always spell-check normal text
		End Select
	 End With
	 End Function

    Private Sub UpdateDocument()
        'Dim DocumentText As String = ""
        Dim DocumentText As New System.Text.StringBuilder
        Dim UpTo = 0
		  Dim UsesTextBox As Boolean = Me.TextBox IsNot Nothing
        For Each item In mc_Words
            RemoveHandler item.UpdateClassEvent, AddressOf UpdateClassEvent
            RemoveHandler item.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
            RemoveHandler item.UpdateWordEvent, AddressOf UpdateWordEvent
        Next
        mc_Words.Clear()
		  Dim ScrollPosition As Point
		  If UsesTextBox Then
			  Me.TextBox.SetRedrawMode(False)
			  ScrollPosition = Me.TextBox.GetScrollPosition()
		  End If
		  Try
			  For Each item In _
						Dictionary.Formatting.RemoveWordBreaks(mc_Text).Split(" "c)
					Dim ThisWordItem = New SpellCheckDialogWords(item, UpTo, CanChangeWord(UpTo, item.Length))
					AddHandler ThisWordItem.UpdateClassEvent, AddressOf UpdateClassEvent
					AddHandler ThisWordItem.SelectionChangedEvent, AddressOf WordSelectionChangedEvent
					AddHandler ThisWordItem.UpdateWordEvent, AddressOf UpdateWordEvent
					mc_Words.Add(ThisWordItem)
					If ThisWordItem.OrigWord <> "" Then
						 DocumentText.Append("<a href='" & mc_Words.Count - 1 & "' class='Pending' id='" _
							& mc_Words.Count - 1 & "'>" & ThisWordItem.OrigWord & "</a>")
					End If
					UpTo += item.Length + 1
					If UpTo <= mc_Text.Length Then
						 DocumentText.Append(mc_Text.Substring(UpTo - 1, 1).Replace("&", "&"))
					End If
			  Next item
		   Finally
			  If UsesTextBox Then
				  Me.TextBox.SetScrollPosition(ScrollPosition) : Me.TextBox.SetRedrawMode(True)
			  End If
		  End Try
        mc_AllowNavigation = True
        MyBase.DocumentText = HTMLStyle() & "<BODY onselectstart='return false;'>" & System.Text.RegularExpressions.Regex.Replace(DocumentText.ToString, "\r\n|\n\r|\r|\n", "<BR>")
        mc_AllowNavigation = False
    End Sub

#End Region

'   (Public Class SpellCheckDialogWords 'nested class)

		  Public CanChangeWord As Boolean

        Public Sub New(ByVal OrigWord As String, _
				Optional ByVal StartIndex As Integer = 0, _
				Optional ByVal CanChangeText As Boolean = True)
            Me.OrigWord = OrigWord : mc_NewWord = OrigWord
				Me.CanChangeWord =  CanChangeText : Me.StartIndex = StartIndex
        End Sub

        Public OrigWord As String
        Dim mc_NewWord As String
        
		  Public Property NewWord(Optional ByVal Autoupdate As Boolean = True) As String
            Get
                Return mc_NewWord
            End Get
            Set(ByVal value As String)
					 If Me.CanChangeWord AndAlso value <> mc_NewWord Then
	                mc_NewWord = value : Me.Changed = True
					    If Autoupdate Then
						     UpdateWord()
						 End If
					 End If
            End Set
        End Property
'   (End Class 'nested class)

#Region "Constructors"
 	 Public TextBox As TextBoxBase = Nothing
	 Public SpellCheckHiddenText As Boolean = False

    Public Sub New(Optional TextBox As TextBoxBase = Nothing, _
		Optional ByVal SpellCheckHiddenText As Boolean = False)
        MyBase.AllowWebBrowserDrop = False
        MyBase.IsWebBrowserContextMenuEnabled = False
		  Me.TextBox = TextBox : Me.SpellCheckHiddenText = SpellCheckHiddenText
    End Sub

#End Region

#Region "Other Public Subs"
     'Sets the HTML to the document string
    Dim mc_Text As String

    Private Settings As New SpellCheckSettings

    Public Sub SetText(Optional ByVal Text As String = "", Optional ByVal Settings As SpellCheckSettings = Nothing)
        If Settings IsNot Nothing Then Me.Settings = Settings
		  If String.IsNullOrEmpty(Text) AndAlso Me.TextBox IsNot Nothing Then
				mc_Text = Me.TextBox.ExtendedText()
			Else
				mc_Text = Text
		  End If
        UpdateDocument()
    End Sub

Now change the extTextBoxContextMenu_Opening procedure of SpellCheck\Controls\Menu.vb as follows:
BASIC
    Private Sub extTextBoxContextMenu_MenuOpening(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles extTextBoxContextMenu.MenuOpening

        'If ContextMenuStrip.SourceControl Is parentTextBox Then
        'SpellMenuItems.RemoveSpellMenuItems()

        If OKToSpellCheck Then
            SpellMenuItems.ContextMenuStrip = extTextBoxContextMenu.ContextMenuStrip
            If Settings.IgnoreWordOverride(extTextBoxContextMenu.MenuSpellClickReturn.Word) Then
					'word is in caps etc...
             Else
'   make sure word isn't wholly/partly protected or hidden
					With extTextBoxContextMenu.MenuSpellClickReturn
						Beep
						parentTextBox.SetRedrawMode(False)
						Dim ScrollPosition As Point = parentRichTextBox.GetScrollPosition()
						Dim TextType As TypeOfText = parentTextBox.GetTextType(.WordStart, .Word.Length)
						If TextType = TypeOfText.Normal Then
							'   word can be changed		               SpellMenuItems.AddItems(.Word, CurrentDictionary, CurrentDefinitions, CurrentSynonyms, Settings)
						End If
parentRichTextBox.SetScrollPosition(ScrollPosition)
						parentTextBox.SetRedrawMode(True)
					End With
            End If
        End If
        'Else
        ''no word clicked on
        'End If

    End Sub

Next, go to the SpellCheckDialog.vb file, and make the following additions and modifications:
BASIC
	 Private SourceTextBox As TextBoxBase, SpellCheckHiddenText As Boolean, ShowingSuggestions As Boolean


	 Private Function EnablingWordChange(Word As HTMLSpellCheck.SpellCheckDialogWords) As Boolean
	 '   should we enable word-changing controls?
	 Return ShowingSuggestions AndAlso Word.CanChangeWord
	 End Function

   Public Overloads Function ShowDialog(ByVal owner As TextBoxBase, _
		ByVal SpellCheckControlBase As SpellCheckControlBase, Optional ByVal Text As String = "", _
		Optional ByVal SpellCheckHiddenText As Boolean = False) As List(Of HTMLSpellCheck.SpellCheckDialogWords)
        Me.SourceTextBox = owner : Me.SpellCheckHiddenText = SpellCheckHiddenText
		  Me.SpellCheckControlBase = SpellCheckControlBase
		  If String.IsNullOrEmpty(Text) Then
				Text = owner.ExtendedText()
		  End If
		  With HtmlSpellCheck1
			  .TextBox = owner : .SpellCheckHiddenText = SpellCheckHiddenText
			  .Dictionary = SpellCheckControlBase.CurrentDictionary
			  .SetText(Text)
		  End With
        pbChangeAll.Maximum = HtmlSpellCheck1.Words.Count - 1
		  If HtmlSpellCheck1.Words.Count > 0 AndAlso Not EnablingWordChange(HtmlSpellCheck1.Words(0)) Then
			  '   make sure we don't start with a word that can't be spell-checked due to protected/hidden status
			  btnChange.Enabled = False : btnSkip_Click(btnSkip, EventArgs.Empty)
		  End If
        MyBase.StartPosition = FormStartPosition.CenterParent
        MyBase.ShowDialog(owner)
        Return HtmlSpellCheck1.Words
    End Function

    Private Function SelectedWord() As HTMLSpellCheck.SpellCheckDialogWords
		Dim sw As HTMLSpellCheck.SpellCheckDialogWords = _
			(From xItem In HtmlSpellCheck1.Words Where xItem.Selected).FirstOrDefault
		btnChange.Enabled = _
			sw IsNot Nothing AndAlso EnablingWordChange(sw)
		Return sw
    End Function

    Private Sub HtmlSpellCheck1_SelectionChanged(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) _
		Handles HtmlSpellCheck1.SelectionChanged
        If pnlSuggestions.InvokeRequired Then
            Dim HtmlSpellCheck1_SelectionChanged_cb As New HtmlSpellCheck1_SelectionChanged_cb(AddressOf HtmlSpellCheck1_SelectionChanged)
            pnlSuggestions.Invoke(HtmlSpellCheck1_SelectionChanged_cb, sender, e)
         Else
            FillSuggestions()
            ShowHideSuggestions(True)
            txtChangeTo.Focus()
				btnChange.Enabled = _
					e.Word IsNot Nothing AndAlso EnablingWordChange(e.Word)
            ChangeToUseOldWord = True
            txtChangeTo_TextChanged(txtChangeTo, EventArgs.Empty)
            ChangeToChanged = False
				If e.Word.Selected AndAlso Not btnChange.Enabled Then
					MoveToNextWordError()
				End If
        End If
    End Sub

Private Sub ShowHideSuggestions(ByVal Show As Boolean)
		  ShowingSuggestions = Show
        If pnlSuggestions.InvokeRequired Then
            Dim ShowHideSuggestions_cb As New ShowHideSuggestions_cb(AddressOf ShowHideSuggestions)
            pnlSuggestions.Invoke(ShowHideSuggestions_cb, Show)
        Else
            Dim theSelectedWord = SelectedWord()

            pnlSuggestions.Visible = Show

            btnIgnore.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = False AndAlso theSelectedWord.SpellCheckState <> HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK

            btnSkip.Enabled = Show AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case) AndAlso xItem IsNot theSelectedWord).Count > 0
				tstbChangeTo.Enabled = _
					MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
            btnRevert.Enabled = Show AndAlso theSelectedWord IsNot Nothing AndAlso theSelectedWord.Changed = True
            btnChangeAll.Enabled = Not ChangeingAll AndAlso (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
            btnRevertAll.Enabled = (From xItem In HtmlSpellCheck1.Words Where xItem.Changed = True).Count > 0
        End If

    End Sub

    Private Sub txtChangeTo_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles txtChangeTo.KeyUp
        Select Case e.KeyCode
            Case Keys.Apps
                e.Handled = True
                'had to do this as the line above doesn't work for app button :(...
                If txtChangeTo.ContextMenu Is Nothing Then
                    txtChangeTo.ContextMenu = New ContextMenu
                End If
                'now show the html spell check context menu!
                Dim theSelectedWord = SelectedWord()
                If theSelectedWord IsNot Nothing Then
                    MenuCurrentWord = theSelectedWord
						  tstbChangeTo.Enabled = _
								MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
                    UpdateMenuItems()
                    OpenedByClick = False
                    cmsHTMLSpellCheck.Show(HtmlSpellCheck1, Point.Empty)
                End If
        End Select
    End Sub

    Private Sub SpellMenuItems_WordChanged(ByVal sender As Object, ByVal e As Menu.AddSpellItemsToMenu.SpellItemEventArgs) Handles SpellMenuItems.WordChanged
		  If MenuCurrentWord.CanChangeWord Then
	        MenuCurrentWord.NewWord = e.Word
		     MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
		  End If
        WordUpdatedFromMenu()
    End Sub

    Private Sub tstbChangeTo_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles tstbChangeTo.KeyPress
        If e.KeyChar = vbCr Then
			   If MenuCurrentWord.CanChangeWord Then
					'   word is unprotected--change it
	            MenuCurrentWord.NewWord = tstbChangeTo.Text
		         'assume this word is correct since it is user-entered...
			      MenuCurrentWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
				 Else
					'   protected word--include just in case (shouldn't land here!)
					Beep() : MessageBox.Show("This word is protected and/or hidden!")
			   End If
            UpdateMenuItems()
            WordUpdatedFromMenu()
        End If
    End Sub

    Private Sub UpdateMenuItems()
        If Replace(Dictionary.Formatting.RemoveWordBreaks(MenuCurrentWord.NewWord), " ", "") = MenuCurrentWord.NewWord Then
            SpellMenuItems.ContextMenuStrip = cmsHTMLSpellCheck
            SpellMenuItems.RemoveSpellMenuItems()
				If MenuCurrentWord.CanChangeWord Then
	            SpellMenuItems.AddItems(MenuCurrentWord.NewWord, SpellCheckControlBase.CurrentDictionary, SpellCheckControlBase.CurrentDefinitions, SpellCheckControlBase.CurrentSynonyms, SpellCheckControlBase.Settings)
				End If
        End If
        mtsChangeTo.Text = "Change " & MenuCurrentWord.NewWord & " to:"
        tstbChangeTo.Text = MenuCurrentWord.NewWord
        tsiRevertTo.Text = "Revert to " & MenuCurrentWord.OrigWord
        tsiRevertTo.Visible = MenuCurrentWord.Changed
        'hrm... had to put this in as the location did not get updated the first go otherwise???
        cmsHTMLSpellCheck_LocationChanged(cmsHTMLSpellCheck, EventArgs.Empty)
    End Sub

    Private Sub HtmlSpellCheck1_WordSpellChecked(ByVal sender As Object, ByVal e As HTMLSpellCheck.HTMLWordEventArgs) Handles HtmlSpellCheck1.WordSpellChecked

        If pbChangeAll.Visible Then
            Dim Progress As Integer = HtmlSpellCheck1.Words.IndexOf(e.Word)
            pbChangeAll.SafeInvoke(Function(x As ProgressBar) InlineAssignHelper(x.Value, Progress))
        End If

        pnlPleaseWait.SafeInvoke(Function(x As Panel) InlineAssignHelper(x.Visible, True))
        If e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case Then
            If ChangeingAll Then
                Dim Suggestions = GetSuggestions(e.Word.NewWord)
                If Suggestions.Count > 0 AndAlso e.Word.CanChangeWord Then
                    'change to first
	                 e.Word.NewWord = Suggestions.First
                    e.Word.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
                    If SelectedWord() Is e.Word Then
                        HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = e.Word})
                    End If
                End If
                Exit Sub
            End If
            If pnlSuggestions.Visible = False Then
                'select this word :)
                e.Word.Selected = True
            End If
            ShowHideSuggestions(pnlSuggestions.Visible)

            'If btnSkip.Enabled = False Then
            '    Dim SelectedWord = Me.SelectedWord()
            '    Dim btnSkipEnabled = (From xItem In HtmlSpellCheck1.Words Where (xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error OrElse xItem.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case) AndAlso xItem IsNot SelectedWord).Count > 0
            '    If btnSkip.Enabled <> btnSkipEnabled Then
            '        btnSkip.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, btnSkipEnabled))
            '    End If
            'End If
        End If
    End Sub

    Private Function MoveToNextWordError(Optional ByVal DoRecheck As Boolean = True) As Boolean
	 With HtmlSpellCheck1
			MoveToNextWordError = False
        'find the selected word
        Dim StartIndex = 0
        Dim theSelectedWord = SelectedWord()
        If theSelectedWord IsNot Nothing Then
				theSelectedWord.Selected = False 'deselect current word
            StartIndex = .Words.IndexOf(theSelectedWord) + 1
        End If
ReCheck:
        For i = StartIndex To .Words.Count - 1
				If .Words(i) IsNot Nothing Then
	            Select Case .Words(i).SpellCheckState
		            Case HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error, _
								HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case
							If .Words(i).CanChangeWord Then
				            '   select word if we're allowed to change it
					         .Words(i).Selected = True
						      MoveToNextWordError = True : Exit Function
							End If
				   End Select
					If .Words(i).Selected Then
						.Words(i).Selected = False 'deselect word if it's selected
					End If
				End If
        Next
        'if we got here ... start over?
        If DoRecheck Then
            If StartIndex = 0 Then
                CompleteSpellCheck() : Exit Function 'already checked from the start
            End If
            StartIndex = 0
            GoTo ReCheck
        End If
	 End With
    End Function

    Private Sub btnChange_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChange.Click
        Dim theSelectedWord = SelectedWord()
        If theSelectedWord IsNot Nothing Then
				If theSelectedWord.CanChangeWord Then
					'   word is unprotected--change it
	            theSelectedWord.NewWord = txtChangeTo.Text
		         theSelectedWord.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
				 Else
					'   protected word--included just in case (shouldn't land here!)
					Beep() : MessageBox.Show("This word is protected and/or hidden!")
				End If
        End If
        btnSkip_Click(btnSkip, EventArgs.Empty)
        ShowHideSuggestions(pnlSuggestions.Visible)
    End Sub

	 Private Sub StartChangeAll()
    btnAdd.Enabled = False
    ChangeingAll = True
    If mt_ChangeAll IsNot Nothing AndAlso mt_ChangeAll.IsAlive Then
        mt_ChangeAll.Abort()
    End If
    Dim SelectedWord = Me.SelectedWord
   NewTextForChangeAll = txtChangeTo.Text
	OldTextForChangeAll = SelectedWord.OrigWord
   If SelectedWord IsNot Nothing Then
        SelectedWord.Selected = False
    End If
    ShowHideSuggestions(False)

    btnChangeAll.Enabled = False
    mt_ChangeAll = New System.Threading.Thread(AddressOf ChangeAll)
    mt_ChangeAll.Name = "Spell Check - Change all"
    mt_ChangeAll.IsBackground = True
    mt_ChangeAll.Start()
	End Sub

	Private Sub ChangeAll()

    'Dim st = Now

    Dim WordErrors = _
		(From xItem In HtmlSpellCheck1.Words _
			Where String.Compare(xItem.OrigWord, OldTextForChangeAll, True) = 0 _
				AndAlso (xItem.SpellCheckState = _
						HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
					OrElse xItem.SpellCheckState = _
						HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error))
    For Each item In WordErrors.ToArray
       '   word in question found
		 If item.CanChangeWord Then
			 '   word is unprotected--change it
          Dim Suggestions = GetSuggestions(item.NewWord)
	       If Suggestions.Count > 0 Then
		       If String.IsNullOrEmpty(NewTextForChangeAll) Then
			       'change to first if no text offered
				    item.NewWord = Suggestions.First
				  Else
		          '   change to offered text
			       item.NewWord = NewTextForChangeAll
			    End If
	          item.SpellCheckState = HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.OK
		       If SelectedWord() Is item Then
			       HtmlSpellCheck1_SelectionChanged(HtmlSpellCheck1, _
						New HTMLSpellCheck.HTMLWordEventArgs() With {.Word = item})
				 End If
			 End If
		  Else
			 '   protected word
			 Beep()
		 End If
    Next
    btnRevertAll.SafeInvoke(Function(x As Button) InlineAssignHelper(x.Enabled, True))

    'complete... unless...
    If HtmlSpellCheck1.mt_SpellCheck IsNot Nothing _
			AndAlso HtmlSpellCheck1.mt_SpellCheck.IsAlive Then
       '... we are still spell checking :(
	  Else
       Dim btnChangeAllEnabled = _
			(From xItem In HtmlSpellCheck1.Words _
				Where (xItem.SpellCheckState = _
						HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Case _
					OrElse xItem.SpellCheckState = _
						HTMLSpellCheck.SpellCheckDialogWords.SpellCheckStates.Error)).Count > 0
       ChangeingAll = False
       If btnChangeAllEnabled = True Then
          '...or we have words left that suggestions could not be found for...
          btnChangeAll.SafeInvoke(Function(x As Button) _
				InlineAssignHelper(x.Enabled, btnChangeAllEnabled))
          btnSkip_Click(btnSkip, EventArgs.Empty)
        Else
          CompleteSpellCheck()
       End If
    End If

    'MsgBox(Now.Subtract(st).TotalMilliseconds)

	End Sub

	 Private Sub AddWordButton(ByVal CaseSensitive As Boolean)
        Dim theSelectedWord = SelectedWord()
        If theSelectedWord IsNot Nothing Then
            'change the word...
				Dim WordCanBeChanged As Boolean = theSelectedWord.CanChangeWord
            Dim Word As String = txtChangeTo.Text
            If ChangeToChanged = False Then
                Word = theSelectedWord.NewWord
             Else
                theSelectedWord.NewWord = Word
            End If
				Try
		         If SpellCheckControlBase.CurrentDictionary.SpellCheckWord(Word) <> i00SpellCheck.Dictionary.SpellCheckWordError.OK Then
	                If CaseSensitive = False Then
						     Word = LCase(Word)
						End If
						AddWordToDict(Word, theSelectedWord)
					End If
				 Finally
					If Not WordCanBeChanged Then
						'   word is protected
						theSelectedWord.NewWord = theSelectedWord.OrigWord
						theSelectedWord.Changed = False
					End If
				End Try
        End If
        'move next
        btnSkip_Click(btnSkip, EventArgs.Empty)
    End Sub

    Private Sub cmsHTMLSpellCheck_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsHTMLSpellCheck.Opening
        If OpenedByClick Then
            Dim pCursor = Cursor.Position
            Dim pHtml = HtmlSpellCheck1.PointToScreen(New Point(0, 0))
            pCursor.X = pCursor.X - pHtml.X
            pCursor.Y = pCursor.Y - pHtml.Y

            Dim element = HtmlSpellCheck1.Document.GetElementFromPoint(pCursor)
            If element IsNot Nothing AndAlso element.Id <> "" Then
                Dim ElementNo As Integer
                If IsNumeric(Integer.TryParse(element.Id, ElementNo)) AndAlso HtmlSpellCheck1.Words.Count > ElementNo Then
                    MenuCurrentWord = HtmlSpellCheck1.Words.Item(ElementNo)
						  tstbChangeTo.Enabled = _
							  MenuCurrentWord IsNot Nothing AndAlso EnablingWordChange(MenuCurrentWord)
                    UpdateMenuItems()
                Else
                    e.Cancel = True
                End If
            Else
                e.Cancel = True
            End If
        Else
            OpenedByClick = True
        End If
    End Sub

Finally, go to file Misc.vb, and change the ShowDialog procedure as follows:
BASIC
#Region "Show spellcheck dialog"
     Public Sub ShowDialog(Optional ByVal SpellCheckHiddenText As Boolean = False) Implements iSpellCheckDialog.ShowDialog
        If CurrentDictionary IsNot Nothing AndAlso CurrentDictionary.Loading = False Then
            Using SpellCheckDialog As New SpellCheckDialog
                Dim SpellCheckResults = _
						SpellCheckDialog.ShowDialog(parentTextBox, Me, _
							parentTextBox.ExtendedText, SpellCheckHiddenText)

                'update the text box...

                'lock window from updating
                DrawSpellingErrors = False
                extTextBoxCommon.LockWindowUpdate(Control.Handle)
                Control.SuspendLayout()
                Dim SelStart = parentTextBox.SelectionStart
                Dim SellLength = parentTextBox.FullSelectionLength


                'Dim mc_parentRichTextBox = TryCast(parentTextBox, RichTextBox)
                If parentRichTextBox IsNot Nothing Then
                    'rich text box :(... use alternate method ... this will ensure that the formatting isn't lost
                    'qwertyuiop - would be nicer if this did this in one undo move...
						  Dim Offset As Integer = 0
                    For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
                        parentRichTextBox.Select(word.StartIndex - Offset, word.OrigWord.Length)
								parentRichTextBox.SelectedText = word.NewWord
                    Next
                    CType(parentRichTextBox, TextBoxBase).ClearUndo()
                Else
                    'standard text box .. can just replace all of the text

                    'Get old scroll bar position
                    Dim OldVertPos = extTextBoxCommon.GetScrollBarLocation(parentTextBox)

                    Dim NewText As String = parentTextBox.ExtendedText
                    For Each word In (From xItem In SpellCheckResults Where xItem.Changed = True AndAlso xItem.NewWord <> xItem.OrigWord).ToArray.Reverse
                        NewText = Strings.Left(NewText, word.StartIndex) & _
                                  word.NewWord & _
                                  Strings.Right(NewText, Len(NewText) - (word.StartIndex + word.OrigWord.Length))
                    Next

                    'replace the text
                    parentTextBox.Text = NewText

                    'Set scroll bars to what they were
                    extTextBoxCommon.SetScrollBarLocation(parentTextBox, OldVertPos) ' set Vscroll to last saved pos.
                End If

                parentTextBox.SelectionStart = SelStart
                parentTextBox.SelectionLength = SellLength

                'unlock window updates
                DrawSpellingErrors = True
                Control.ResumeLayout()
                extTextBoxCommon.LockWindowUpdate(IntPtr.Zero)

            End Using
        End If
    End Sub

#End Region

Notice that when one invokes ShowDialog(True), the spell-checker spell-checks hidden (invisible) text, but not protected text. If one invokes ShowDialog(False) or simply ShowDialog(), then neither hidden nor protected text is spell-checked.
GeneralEVEN MORE ADDITIONAL RECOMMENDED CHANGES !!! Pin
Robert Gustafson19-Feb-23 13:48
Robert Gustafson19-Feb-23 13:48 
QuestionI am using i00 Spell check in my project Pin
Member 1131270020-Sep-19 0:30
Member 1131270020-Sep-19 0:30 
QuestionI am using i00 Spell check in my project Pin
Member 1303975328-Aug-19 14:00
Member 1303975328-Aug-19 14:00 
Questioni00SpellCheck Pin
Member 1075424712-Aug-19 2:49
Member 1075424712-Aug-19 2:49 
QuestionI am using i00 Spell check in my project! And it is awesome! Pin
Member 1288935230-Jun-19 4:11
Member 1288935230-Jun-19 4:11 
QuestionThanks heaps Pin
Member 814715410-Jun-19 1:17
Member 814715410-Jun-19 1:17 
GeneralI am using i00 Spell check in my projec Pin
Member 125797679-Jun-19 9:16
Member 125797679-Jun-19 9:16 
QuestionI am using i00 Spell check in my project! Pin
Member 144795053-Jun-19 14:14
Member 144795053-Jun-19 14:14 
QuestionI am using i00 Spell check in my project Pin
Member 1380685523-May-19 16:32
Member 1380685523-May-19 16:32 
PraiseI am using i00 Spell check in my project Pin
Markus 141697133-Mar-19 12:08
Markus 141697133-Mar-19 12:08 
QuestionProblem only occurred if i changed Windows Scale and Layout to more than 100% Pin
NasserGhoul26-Feb-19 6:23
NasserGhoul26-Feb-19 6:23 
AnswerRe: Problem only occurred if i changed Windows Scale and Layout to more than 100% Pin
i0010-Feb-20 15:18
i0010-Feb-20 15:18 
Question"I am using i00 Spell check in my project" Pin
kopkistre13-Jan-19 1:24
kopkistre13-Jan-19 1:24 
Question"I am using i00 Spell check in my project" Pin
kopkistre13-Jan-19 1:24
kopkistre13-Jan-19 1:24 
QuestionI am using i00 Spell check in my project Pin
steveatquinata21-Dec-18 12:48
steveatquinata21-Dec-18 12:48 
QuestionI am using i00 Spell check in my project Pin
SherMags22-Nov-18 0:52
SherMags22-Nov-18 0:52 
QuestionI am using i00 Spell check in my project Pin
Ob1kanami14-Nov-18 6:31
Ob1kanami14-Nov-18 6:31 

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.