SpellChecker in WPF

Abhi2434
Posted by in WPF category on for Beginner level | Views : 44392 red flag
Rating: 3.71 out of 5  
 7 vote(s)

WPF has an inbuilt feature of Spellchecking. In this article I have explored the SpellCheck functionality of WPF TextBoxBase object.

CustomDictionaries introduced with WPF 4.0 is also discussed with this article


 Download source code for SpellChecker in WPF

Introduction


WPF comes with an inbuilt feature of SpellCheck. SpellCheck is added to TextBoxBase object, which inherits all the textual input controls automatically. Thus any input control will automatically derive the Spell Checking functionality.

In this article I would like to introduce how you can explore this feature to your own application.


Basic Usage


WPF provides a new object called SpellCheck. This object can be applied to any TextBoxBase object and thereby the object will behave automatically with the spellcheck functionality. To do this, lets look at the example :

<StackPanel Height="150" Margin="50">
<TextBlock Text="Enter Content" ></TextBlock>
<TextBox x:Name="txtBox" SpellCheck.IsEnabled="True" AcceptsReturn="True" />
</StackPanel>
In the above example, you can see the TextBox txtBox, have SpellChecker enabled. This ensures the textbox automatically detect misspelled words and will put an underline on the word. When you run the application, you will see the textbox to appear like this :



You can see all the misspelled word is shown with red underline. These words can easily be corrected by using the TextBox default ContextMenu. Right clicking on the word will give you a list of suggestions for the current word which you might choose to change the word in the paragraph.

SpellCheck.IsEnabled is actually a Dependency property. So you can easily use it to set in Style setters. You can also use global setter to apply SpellChecking to every control you put in the window very easily.

<Style TargetType="{x:Type TextBoxBase}" x:Key="txtBasic" >
<Setter Property="SpellCheck.IsEnabled" Value="true" />
<Setter Property="SpellCheck.SpellingReform" Value="PreAndPostreform" />
</Style>

Now you can easily define any TextBox or RichTextBox or any control that derives TextboxBase and use the Style to apply SpellChecker. You can also use

<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource txtBasic}" />
<Style TargetType="{x:Type RichTextBox}" BasedOn="{StaticResource txtBasic}" />

By doing this, any TextBox or RichTextBox which you place in your Window will automatically have spellChecking enabled.

You can also enable/disable the SpellCheck from codebehind.

SpellCheck.SetIsEnabled(txtBox, true);
SpellCheck.SetSpellingReform(txtBox, SpellingReform.PreAndPostreform);

Spelling error can be detected based on the carat location. TextBoxBase contains a few methods that you can invoke to get Spelling errors. SpellingError is a class which provides the current Spelling error word with an enumerable of Suggessions.

Lets say you want to display the suggessions into your own Listbox.

<TextBlock Text="Enter Content"  ></TextBlock>
<TextBox x:Name="txtBox" AcceptsReturn="True" Keyboard.KeyUp="txtBox_KeyUp"/>
<TextBlock Text="Suggessions" ></TextBlock>
<ListBox x:Name="lstSuggessions" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Thus you can see, I have declared a ListBox which will show you the Suggessions that are populated in the contextmenu. To do this you can write like this :

private void txtBox_KeyUp(object sender, KeyEventArgs e)
{
int catatPos = txtBox.CaretIndex;
lstSuggessions.Items.Clear();
SpellingError error = txtBox.GetSpellingError(catatPos);
if (error != null)
{
foreach (string suggession in error.Suggestions)
lstSuggessions.Items.Add(suggession);
}
}

Here we are using SpellingError class to get the Suggessions. CaretIndex returns the index where the carat is in the textbox. GetSpellingError can return SpellingError object only when the current Carat location has a word with errors and also SpellCheck is enabled for the TextBox. 


You can use EditingCommands.CorrectSpellingError to correct the spelling.

Building ContextMenu


It is true that the default contextmenu cannot be styled. You need to redefine the contextMenu again to have it styled. Let us look how you can redefine the ContextMenu yourself.

private void txtBox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
ContextMenu menu = this.Resources["ctxMenu"] as ContextMenu;
this.ClearSpellCheckMenuItems(menu);
int catatPos = txtBox.CaretIndex;
SpellingError error = txtBox.GetSpellingError(catatPos);
if (error != null)
{
this.txtBox.ContextMenu.Items.Insert(0, new Separator());
MenuItem item = this.GetMenu("Ignore All", EditingCommands.IgnoreSpellingError, this.txtBox);
item.Tag = "S";
this.txtBox.ContextMenu.Items.Insert(0, item);
foreach (string suggession in error.Suggestions)
{
item = this.GetMenu(suggession, EditingCommands.CorrectSpellingError, this.txtBox);
item.Tag = "S";
this.txtBox.ContextMenu.Items.Insert(0, item);
}

}
}
private MenuItem GetMenu(string header, ICommand command, TextBoxBase target)
{
MenuItem item = new MenuItem();
item.Header = header;
item.Command = command;
item.CommandParameter = header;
item.CommandTarget = target;
return item;
}
private void ClearSpellCheckMenuItems(ContextMenu menu)
{
for (int i = 0; i < menu.Items.Count; i++)
{
MenuItem item = menu.Items[i] as MenuItem;
if (item != null && item.Tag != null)
menu.Items.RemoveAt(i);
}
}
Here I have built the contextmenu dynamically. The EditingCommand.CorrectSpellingError will take a string parameter which I have passed from Suggession and the target as the control. The GetMenu will build every menuItem. IgnoreAll can also be created using EditingCommand.IgnoreSpellingError.

It is to be noted that I have added each menuitem from the beginning. It is because each item is added at 0th index so that each new item will come above the other.  It is preferable to use the reverse always.

Limitation of SpellCheck


SpellCheck of WPF has few basic Limitations.

  1. SpellChecking functionality is restricted to English, Spanish, French and German.
  2. You cannot create CustomDictionaries for Textboxes. So if you want to ignore a specific word, it will be ignored once only for the current application run. If you rerun the application, you need to ignore the same word again.

CustomDictionaries with WPF 4.0


With the introduction of WPF 4.0, SpellCheck object is somehow modified to support CustomDictionaries. This is really a good addition to WPF Spell Checker. Let us look how you can use it:

<TextBox x:Name="txtCustom" SpellCheck.IsEnabled="True">
<SpellCheck.CustomDictionaries>
<sys:Uri>location\dictionary.lex</sys:Uri>
</SpellCheck.CustomDictionaries>
</TextBox>
In the above Code you can see I have added custom dictionaries to the TextBox. sys points to System in System.dll(xmlns:sys="clr-namespace:System;assembly=System"). CustomDictionaries is a collection of all dictionaries which you can add to your textbox control so that when text is entered into the textbox it will ignore if misspelled but exists in the CustomDictionary file.

CustomDictionary file is actually a Text file which have every words placed in new lines. While loading the textbox, WPF reads the entire file into context and recreates its dictionary dynamically.

You can also create a custom "Add to Dictionary" to add the current item into dictionary. To do this,

MenuItem item = new MenuItem();
item.Header = "Add to Dictionary";
item.Click += new RoutedEventHandler(AddDict_Click);
item.Command = EditingCommands.IgnoreSpellingError;
item.CommandParameter = currentText;
item.CommandTarget = txtBox;
So now the AddDict_Click will be called automatically when we select the Add To Dictionary object. You can see I have also used item.Command as EditingCommands.IgnoreSpellingError, which will ignore the word to current textbox. Thus you only need to write the logic to open the Dictionary file and write the element in CommandParameter to the file. 

Conclusion


SpellCheck is a great option to WPF. You can easily use it to build a better solution for your client. I hope this article will help you to a great extent. Please feel free to write your feedback.
Page copy protected against web site content infringement by Copyscape

About the Author

Abhi2434
Full Name: Abhishek Sur
Member Level: Silver
Member Status: Member,Microsoft_MVP,MVP
Member Since: 12/2/2009 4:19:08 AM
Country: India
www.abhisheksur.com
http://www.abhisheksur.com
Working for last 2 and 1/2 years in .NET environment with profound knowledge on basics of most of the topics on it.

Login to vote for this post.

Comments or Responses

Posted by: Abhijit Jana on: 4/11/2010
Good Job Abhishek ! Keep it up !!
Posted by: Abhi2434 on: 4/11/2010
Thank you so much Abhijit. Cheers.

Login to post response

Comment using Facebook(Author doesn't get notification)