Spelling Correction Modes and UI Provider
The spell check dialog is one way to correct words; a panel docked beside the editor, a correction bar or a dialog in your own design are others. For those, TX Spell .NET provides the SpellingCorrectionUIProvider. It keeps track of the word being corrected, its suggestions and which commands make sense for it, and your controls bind to these values with Windows Forms data binding. You write the layout; the provider decides when Change is enabled and what the list of suggestions holds.
What the Provider Does
You get the provider from the SpellingCorrectionUIProvider property of the TXSpellChecker. It works through the misspelled words of a TextControl or a text, one at a time, and it always knows the state of the correction: whether there is a word to correct, whether suggestions were found, whether the user is editing the word. This state is its mode.
Five properties of the provider split the work. Each returns an object of a nested class with a different name:
| Property | Returns | Holds |
|---|---|---|
| StateManaging | StateManager | The connected control, the current word and the mode |
| CorrectionHandling | CorrectionHandler | The commands, such as Change and Ignore, and whether each is enabled |
| SuggestionsHandling | SuggestionsHandler | The suggestions for the current word |
| SuggestionDictionariesHandling | SuggestionDictionariesHandler | The dictionaries the suggestions come from |
| PreviewHandling | PreviewHandler | The position of the sentence that contains the current word |
Code uses the property names: txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.Change(...). The five objects raise PropertyChanged when one of their values changes, which is what data binding needs. Every change of the current word updates the mode, the suggestions and the enabled states, and bound controls follow without an event handler of yours.
The Correction Modes
Mode returns a value of TXSpell.SpellingCorrectionMode:
| Mode | Meaning |
|---|---|
InitialState | No TextControl and no text is connected. This is the state before the first SetCurrent, and after Cancel or SetCurrent with null |
NoWordsToCorrectFound | The connected control or text has no words to correct |
NoWordToCorrectSelected | SetCurrent with a text position found no word to correct at that position; CurrentWordToCorrect is null |
SuggestionsCreated | There is a word to correct, and suggestions were created for it |
NoSuggestionsCreated | There is a word to correct, but no suggestions were found |
WordToCorrectIsDuplicate | The word to correct repeats the word in front of it |
WordToCorrectEditing | The user is editing the word by hand, for example in a preview |
OptionsSettings | The options are being changed, for example in the options dialog |
CorrectionCompleted | Every word was handled; nothing is left to correct |
The mode decides which commands of the CorrectionHandler are enabled:
| Command | Enabled in |
|---|---|
| Change, ChangeAll | SuggestionsCreated, WordToCorrectEditing |
| Delete, Ignore | SuggestionsCreated, NoSuggestionsCreated, WordToCorrectIsDuplicate |
| IgnoreAll | SuggestionsCreated, NoSuggestionsCreated |
| AddToDictionary | SuggestionsCreated, NoSuggestionsCreated, when an editable user dictionary of the word's language exists |
A duplicated word can be deleted or ignored but not changed: there is nothing to replace it with. Each command has a matching property, such as IsChangeEnabled, IsIgnoreEnabled and IsDeleteEnabled, to bind the Enabled property of a button to.
Build a Correction Panel
This form has a TextControl, and to its right a list of suggestions with three buttons: Change, Ignore and Delete. It creates its controls in code and has its own Main method, so in a new Windows Forms project with the TX Text Control and TX Spell .NET packages it replaces the generated Form1.cs, Form1.Designer.cs and Program.cs:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using TXTextControl;
using TXTextControl.Proofing;
public class Form1 : Form
{
private readonly TextControl textControl1 = new TextControl();
private readonly TXSpellChecker txSpellChecker1 = new TXSpellChecker();
private readonly ListBox listBoxSuggestions = new ListBox();
// System.Windows.Forms.Button, because TXTextControl has a Button class, too
private readonly System.Windows.Forms.Button buttonChange = new System.Windows.Forms.Button { Text = "Change" };
private readonly System.Windows.Forms.Button buttonIgnore = new System.Windows.Forms.Button { Text = "Ignore" };
private readonly System.Windows.Forms.Button buttonDelete = new System.Windows.Forms.Button { Text = "Delete" };
public Form1()
{
Text = "Spelling Correction";
Width = 800;
Height = 500;
FlowLayoutPanel panel = new FlowLayoutPanel { Dock = DockStyle.Right, Width = 200, FlowDirection = FlowDirection.TopDown };
listBoxSuggestions.Width = 180;
listBoxSuggestions.Height = 200;
panel.Controls.AddRange(new Control[] { listBoxSuggestions, buttonChange, buttonIgnore, buttonDelete });
textControl1.Dock = DockStyle.Fill;
textControl1.SpellChecker = txSpellChecker1;
textControl1.IsSpellCheckingEnabled = true;
Controls.Add(textControl1);
Controls.Add(panel);
buttonChange.Click += buttonChange_Click;
buttonIgnore.Click += buttonIgnore_Click;
buttonDelete.Click += buttonDelete_Click;
Load += Form1_Load;
}
private void Form1_Load(object sender, EventArgs e)
{
textControl1.Text = "This is a sampke text with a mistkae.";
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
// Select each new word to correct in the editor
provider.StateManaging.PropertyChanged += StateManaging_PropertyChanged;
// Connect the editor: its first misspelled word becomes the word to correct
provider.StateManaging.SetCurrent(textControl1);
// The list shows the suggestions for the current word
listBoxSuggestions.DataBindings.Add(new Binding("DataSource", provider.SuggestionsHandling, "Suggestions"));
listBoxSuggestions.DataBindings.Add(new Binding("Enabled", provider.SuggestionsHandling, "IsSuggestionListEnabled"));
// Each button is enabled when its command makes sense
buttonChange.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsChangeEnabled"));
buttonIgnore.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsIgnoreEnabled"));
buttonDelete.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsDeleteEnabled"));
}
private void StateManaging_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != "CurrentWordToCorrect") return;
if (txSpellChecker1.SpellingCorrectionUIProvider.StateManaging.CurrentWordToCorrect is MisspelledWord word)
{
word.Select();
}
else
{
textControl1.Select(0, 0);
}
}
private void buttonChange_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.Change(listBoxSuggestions.Text);
private void buttonIgnore_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.Ignore();
private void buttonDelete_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.Delete();
[]
private static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
The code works as follows:
- SetCurrent connects the
TextControl. Its first misspelled word becomes CurrentWordToCorrect, aMisspelledWord. If the text has none, the mode becomesNoWordsToCorrectFound. Another overload takes a string, for text that is not in aTextControl. - The first binding fills the list with Suggestions. The others bind the
Enabledproperty of each control to the provider, so a button is grayed out whenever its command is not available in the current mode. PropertyChangedof theStateManagerreports each new word; the handler selects it, so the user sees which word the panel is about.Change,IgnoreandDeletehandle the current word, and the provider moves on to the next one. After the last word, the mode isCorrectionCompletedand all buttons are disabled.
Add More Commands and Settings
The SpellingCorrectionUIProvider Advanced sample of the setup, in the folder TextControl\SpellingCorrectionUIProvider\SpellingCorrectionUIProvider Advanced, extends this panel with four features: commands for every occurrence of a word, a choice of the dictionary, a preview of the sentence and a box to correct the word by hand. Each binds to other members of the provider. The code below adds them to the Form1 class above; declare each new control as a field and add it to the FlowLayoutPanel in the constructor, as the panel does with the first three buttons.
Change All, Ignore All and Add to Dictionary
Three more buttons handle the current word and every other occurrence of it. Their Enabled properties bind to IsChangeAllEnabled, IsIgnoreAllEnabled and IsAddToDictionaryEnabled, and their Click events call the commands:
// In Form1_Load
buttonChangeAll.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsChangeAllEnabled"));
buttonIgnoreAll.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsIgnoreAllEnabled"));
buttonAddToDictionary.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsAddToDictionaryEnabled"));
// Methods of Form1
private void buttonChangeAll_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.ChangeAll(listBoxSuggestions.Text);
private void buttonIgnoreAll_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.IgnoreAll();
private void buttonAddToDictionary_Click(object sender, EventArgs e) =>
txSpellChecker1.SpellingCorrectionUIProvider.CorrectionHandling.AddToDictionary();
Connect the three handlers to the Click events in the constructor. AddToDictionary adds the word to an editable user dictionary; see User Dictionaries.
Choose the Dictionary of the Suggestions
When several dictionaries of the word's language are loaded, the user can pick the one the suggestions come from. SuggestionDictionaries holds the dictionaries in the language of the current word, and IsSuggestionDictionariesEnabled says whether it provides any. A ComboBox binds to both:
// In Form1_Load
comboBoxDictionaries.DataBindings.Add(new Binding("Enabled", provider.SuggestionDictionariesHandling, "IsSuggestionDictionariesEnabled"));
comboBoxDictionaries.DataBindings.Add(new Binding("DataSource", provider.SuggestionDictionariesHandling, "SuggestionDictionaries"));
comboBoxDictionaries.SelectedIndexChanged += comboBoxDictionaries_SelectedIndexChanged;
// A method of Form1
private void comboBoxDictionaries_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBoxDictionaries.SelectedItem is TXTextControl.Proofing.Dictionary dictionary)
{
// Create the suggestions from the chosen dictionary only
txSpellChecker1.SpellingCorrectionUIProvider.SuggestionDictionariesHandling.SelectedSuggestionDictionaries = new TXTextControl.Proofing.Dictionary[] { dictionary };
}
}
SelectedSuggestionDictionaries is an array of Dictionary objects, so it can hold more than one. A change creates new suggestions and updates Mode, and the bound ListBox shows the new list without further code.
Show the Sentence of the Word
A word is often easier to correct when the user sees it in context. PreviewSentenceBounds is an int array of two values, the start and the length of the sentence that contains the current word. It is set again whenever CurrentWordToCorrect changes, and it is null unless the mode is SuggestionsCreated, NoSuggestionsCreated, WordToCorrectIsDuplicate or WordToCorrectEditing.
The array holds positions, not text. To show the sentence in a RichTextBox, bind its Text to PreviewSentenceBounds and turn the bounds into text in the Format event of the binding:
// In Form1_Load
Binding sentenceBinding = new Binding("Text", provider.PreviewHandling, "PreviewSentenceBounds");
sentenceBinding.Format += sentenceBinding_Format;
richTextBoxSentence.DataBindings.Add(sentenceBinding);
// A method of Form1
private void sentenceBinding_Format(object sender, ConvertEventArgs e)
{
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
int[] bounds = provider.PreviewHandling.PreviewSentenceBounds;
if (bounds == null || !(provider.StateManaging.CurrentWordToCorrect is MisspelledWord word))
{
e.Value = "";
return;
}
// The main text, a header, a footer or a text frame
IFormattedText textPart = (IFormattedText)provider.StateManaging.CurrentIFormattedText;
// MisspelledWord.Start is one-based, GetItem expects a zero-based position
Paragraph paragraph = textPart.Paragraphs.GetItem(word.Start - 1);
// Paragraph.Start is one-based, too
e.Value = paragraph.Text.Substring(bounds[0] - (paragraph.Start - 1), bounds[1]);
}
The code works as follows:
- The binding reads
PreviewSentenceBoundseach time the provider reports a change of it, andFormatreplaces the array with the text theRichTextBoxshows. - CurrentIFormattedText is the part of the document that contains the current word, as an IFormattedText: the main text, a header or footer, or a text frame. Its type is
object, so it is cast. - Paragraphs.GetItem returns the paragraph at a zero-based text position; the Start of the misspelled word is one-based, hence
- 1. - The start of the bounds counts from the beginning of the text part, the way the sample of the setup uses it. Subtracting the zero-based start of the paragraph, Paragraph.Start
- 1, gives the position of the sentence in Paragraph.Text, and the second value is its length.
Correct the Word by Hand
When no suggestion fits, the user types the correction into a TextBox. The text box shows the current word, is marked as being edited on the first key press and passes its text to Change:
// In Form1_Load: show the current word, but never write the box back to the provider
Binding wordBinding = new Binding("Text", provider.StateManaging, "CurrentWordToCorrect", true, DataSourceUpdateMode.Never);
wordBinding.Format += wordBinding_Format;
textBoxEdit.DataBindings.Add(wordBinding);
buttonUndoEdit.DataBindings.Add(new Binding("Enabled", provider.CorrectionHandling, "IsUndoEditEnabled"));
textBoxEdit.KeyDown += textBoxEdit_KeyDown;
textBoxEdit.TextChanged += textBoxEdit_TextChanged;
buttonUndoEdit.Click += buttonUndoEdit_Click;
// Methods of Form1
private void wordBinding_Format(object sender, ConvertEventArgs e)
{
e.Value = txSpellChecker1.SpellingCorrectionUIProvider.StateManaging.CurrentWordToCorrect is MisspelledWord word
? word.Text
: "";
}
private void textBoxEdit_KeyDown(object sender, KeyEventArgs e)
{
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
// Switch to the WordToCorrectEditing mode on the first key press
if (provider.StateManaging.CurrentWordToCorrect != null)
{
provider.StateManaging.IsWordToCorrectEditing = true;
}
}
private void textBoxEdit_TextChanged(object sender, EventArgs e)
{
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
if (provider.StateManaging.Mode != TXSpell.SpellingCorrectionMode.WordToCorrectEditing) return;
if (provider.CorrectionHandling.CheckCorrectedWord(textBoxEdit.Text, out string message))
{
labelEditResult.Text = "";
}
else
{
labelEditResult.Text = message;
}
}
private void buttonUndoEdit_Click(object sender, EventArgs e)
{
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
// Leave the editing mode and show the misspelled word again
provider.StateManaging.IsWordToCorrectEditing = false;
if (provider.StateManaging.CurrentWordToCorrect is MisspelledWord word)
{
textBoxEdit.Text = word.Text;
}
}
Change and Change All now take the typed word while the user edits, and the selected suggestion otherwise. Replace the Click handler of Change from the panel, and write the one of Change All the same way with ChangeAll:
private void buttonChange_Click(object sender, EventArgs e)
{
SpellingCorrectionUIProvider provider = txSpellChecker1.SpellingCorrectionUIProvider;
string correction = provider.StateManaging.IsWordToCorrectEditing
? textBoxEdit.Text
: listBoxSuggestions.Text;
provider.CorrectionHandling.Change(correction);
}
The code works as follows:
- The binding of the text box uses
DataSourceUpdateMode.Never: the provider fills the box, and typing never writes back toCurrentWordToCorrect. ItsFormatevent turns theMisspelledWordinto its text. - Setting IsWordToCorrectEditing to
trueswitches the mode toWordToCorrectEditing, in which Change and Change All are enabled. It can only be set while there is a word to correct. - CheckCorrectedWord returns
truewhen the typed word is in one of the selected suggestion dictionaries. Otherwise it returnsfalseand a message for the user in itsoutparameter. It works in the modesSuggestionsCreatedandWordToCorrectEditing. - IsUndoEditEnabled is
truein theWordToCorrectEditingmode, so Undo Edit is only enabled while the user edits. Its handler ends the editing and restores the misspelled word in the box.
Limit the Number of Suggestions
MaxSuggestions limits the list of suggestions; the default is 10.
Build a Complete Spell Check Dialog
The third sample of the setup, Custom SpellCheckDialog, builds a full spell check dialog on the provider, with the same parts as the dialog of TX Spell .NET:

Such a dialog adds three rules to the panel above:
- Open it only when there is something to correct. Call
SetCurrentfirst and readMode. If it isNoWordsToCorrectFound, show a message instead of the dialog. Either way, reset the provider with Cancel afterward, which returns it toInitialState. - Switch buttons by mode. In
WordToCorrectEditing, the Ignore button becomes Undo Edit. ForWordToCorrectIsDuplicate, Change becomes Delete and callsDelete. - Protect ignored words during the options dialog. Set IsOptionsSettings to
truebefore you open the options dialog with OptionsDialog: this keeps the words ignored so far. Set it back tofalsewhen the dialog closes; theTextControlis then checked again with the new settings, and the stored ignored words are reset.
Spell Check Dialog describes the ready-made dialog, which is often enough, and Spell Checking Examples lists the three samples.
Next Steps
- Events: the event-based alternative, for the dialog and the context menus of TX Spell .NET.
- Incorrect Words and Suggestions: the lists behind the provider.
- Suggestion Controls: the suggestion lists of the spell check dialog and the context menu.
- Options Dialog: the settings the user can change during a correction.