Click here to Skip to main content
13,902,146 members
Click here to Skip to main content
Add your own
alternative version


84 bookmarked
Posted 19 Feb 2007
Licenced CPOL

Spelling Suggestions in a WPF TextBox

, 25 Feb 2007
Rate this:
Please Sign up or sign in to vote.
Examines an intuitive way to correct typos in a TextBox.
Screenshot - SmartTextBox.png


This article reviews a TextBox subclass which provides support for helping the user easily fix typos. The SmartTextBox extends WPF's built-in support for spellchecking by popping up a customizable list of "suggestions" for a misspelled word. The list of suggestions can be shown manually via the F1 key, or programmatically with one simple method call. When the list of suggestions is shown, its height is briefly animated to provide a subtle visual cue for the user.

SmartTextBox can give a WPF application that extra little touch of convenience and professionalism, which most users really appreciate. It might be appropriate for an application which allows the user to enter notes or comments that will be read by others.


WPF provides support for spellchecking the text in a TextBox and RichTextBox. The SpellCheck class exposes the primary APIs used for spellchecking. As of WPF v1 the spellchecking support is still not full-featured, but it is expected to be more mature in subsequent releases.

For example, only a few languages are supported (I believe only English, German, and French), and you cannot use a custom dictionary. For more information about the spellchecking support, read more about it here.

The TextBox control is integrated with WPF's spellchecking feature. When spellchecking is enabled, a misspelled word is displayed with a red squiggly line beneath it. Also, if you right-click on a misspelled word the ContextMenu will provide spelling suggestions, as seen below:

The SmartTextBox's features complement this standard functionality.

Using the SmartTextBox


SmartTextBox offers several public members you can use to control its behavior and appearance. Here is its public API:


  • AreSuggestionsVisible - Returns true if the list of suggestions is currently displayed.
  • IsCurrentWordMisspelled - Returns true if the word at the caret index is misspelled.
  • SuggestionListBoxStyle - Gets/sets the Style applied to the ListBox which displays spelling suggestions. This is a dependency property.


  • GetSpellingError() - Returns a SpellingError for the word at the current caret index, or null if the current word is not misspelled.
  • HideSuggestions() - Hides the list of suggestions and returns focus to the input area. If the list of suggestions is not already displayed, nothing happens.
  • ShowSuggestions() - Shows the list of suggestions for the word at the caret index. If the current word is not misspelled, this method does nothing.

The basics

You can create an instance of SmartTextBox in XAML the same way as any other element:

<jas:SmartTextBox>I contian a typo!</jas:SmartTextBox>

By default the spellchecking features are enabled. If you need to disable that functionality, you can do it several ways:

this.smartTextBox.SpellCheck.IsEnabled = false;


SpellCheck.SetIsEnabled( this.smartTextBox, false );

or (in XAML)

<jas:SmartTextBox SpellCheck.IsEnabled="false" />

Styling the list of suggestions

The list of spelling suggestions is shown in the SmartTextBox's adorner layer. The list itself is a ListBox control, which lives in an Adorner. If you want to stylize the list of suggestions, set the SmartTextBox's SuggestionListBoxStyle dependency property to a Style.

For example, here are the Styles used in the demo application, as seen in the screenshot at the top of the article:

<Style x:Key="SuggestionListBoxStyle" TargetType="ListBox">
  <Setter Property="BitmapEffect">
      <DropShadowBitmapEffect ShadowDepth="1" Softness="0.5" />
  <Setter Property="BorderBrush" Value="Gray" />
  <Setter Property="ItemContainerStyle">
      <Style TargetType="ListBoxItem">
        <Setter Property="Border.BorderBrush" Value="LightGray" />
        <Setter Property="Border.BorderThickness" Value="0,0,0,0.5" />
        <Setter Property="FontWeight" Value="Bold" />
        <Setter Property="Margin" Value="2,4" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />

<Style TargetType="jas:SmartTextBox">
  <Setter Property="AcceptsReturn" Value="True" />
  <Setter Property="BorderBrush" Value="Gray" />
  <Setter Property="Margin" Value="4" />


    Value="{StaticResource SuggestionListBoxStyle}" />
  <Setter Property="TextWrapping" Value="Wrap" />

How it works

The remainder of this article discusses how the SmartTextBox works. You do not need to read further in order to use the control in your applications.

Adorner vs. Popup

When I first designed this class I tried to host the suggestions ListBox in a Popup. I ran into a couple of problems with using the Popup in this situation, so I decided to use a custom Adorner to host the suggestions instead.

The Popup did not "follow" the SmartTextBox when the window was moved. If I changed the window's state to 'Maximized' or 'Normal' the Popup would snap back into place, but not when the window moved. Adorners do not have that problem because they are not separate top-level windows, like the Popup.

Also, I encountered some frustrating issues relating to input focus when using a Popup. Those issues disappeared when I used the adorner approach.

The only drawback I'm aware of with using adorners to show the list of suggestions is that they can be clipped. If the bottom of the window which contains a SmartTextBox is very close to a misspelled word, the list of suggestions might not be entirely visible. I don't think this is too big of an issue, though. Here's what I'm referring to:

Showing the list of suggestions

When the caret is in a misspelled word and the user presses the F1 key, the SmartTextBox needs to display a list of suggested spellings. That is accomplished by the following (much abridged) code:

protected override void OnPreviewKeyDown( KeyEventArgs e )
 base.OnPreviewKeyDown( e );
 if( e.Key == Key.F1 )
  if( this.AreSuggestionsVisible )
   this.suggestionList.SelectedIndex = 0;

void AttemptToShowSuggestions()
 if( this.AreSuggestionsVisible )

 // If there is no spelling error, there is no
 // need to show the list of suggestions.
 SpellingError error = this.GetSpellingError();
 if( error == null )

 this.suggestionList.ItemsSource = error.Suggestions;

public SpellingError GetSpellingError()
 int idx = this.FindClosestCharacterInCurrentWord();
 return idx < 0 ? null : base.GetSpellingError( idx );

public void ShowSuggestions()
 if( this.AreSuggestionsVisible || !this.IsCurrentWordMisspelled )

 AdornerLayer layer = AdornerLayer.GetAdornerLayer( this );
 if( layer == null )

 // Position the adorner beneath the misspelled word.
 int idx = this.FindBeginningOfCurrentWord();
 Rect rect = base.GetRectFromCharacterIndex( idx );
 this.adorner.SetOffsets( rect.Left, rect.Bottom );

 // Add the adorner into the adorner layer.
 layer.Add( this.adorner );

 // Since the ListBox might have a new set of items but has not
 // rendered yet, we force it to calculate its metrics so that
 // the height animation has a sensible target value.
    new Size( Double.PositiveInfinity, Double.PositiveInfinity ) );
    new Rect( new Point(), this.suggestionList.DesiredSize ) );

 // Animate the ListBox's height to the natural value.
 DoubleAnimation anim = new DoubleAnimation();
 anim.From = 0.0;
 anim.To = this.suggestionList.ActualHeight;
 anim.Duration = new Duration( TimeSpan.FromMilliseconds( 200 ) );
 anim.FillBehavior = FillBehavior.Stop;
 this.suggestionList.BeginAnimation( ListBox.HeightProperty, anim );

 this.areSuggestionsVisible = true;

Fixing a typo

When the user has selected a suggested word to replace a typo, the SmartTextBox needs to update the text and hide the list of suggestions. The heavy-lifting for most of that task is handled by the SpellingError class, as seen below:

void suggestionList_PreviewKeyDown( object sender, KeyEventArgs e )
 if( this.suggestionList.SelectedIndex < 0 )

 if( e.Key == Key.Escape )
 else if( e.Key == Key.Space || e.Key == Key.Enter || e.Key == Key.Tab )
  // Mark the event as handled so that the keystroke
  // does not propogate to the TextBox.
  e.Handled = true;

void ApplySelectedSuggestion()
 if( !this.AreSuggestionsVisible || this.suggestionList.SelectedIndex < 0 )

 SpellingError error = this.GetSpellingError();
 if( error != null )
  string correctWord = this.suggestionList.SelectedItem as string;
  error.Correct( correctWord );
  base.CaretIndex = this.FindEndOfCurrentWord();

public void HideSuggestions()
 if( !this.AreSuggestionsVisible )

 this.suggestionList.ItemsSource = null;

 AdornerLayer layer = AdornerLayer.GetAdornerLayer( this );
 if( layer != null )
  layer.Remove( this.adorner );

 this.areSuggestionsVisible = false;

Revision History

  • February 19, 2007 - Created article.
  • February 25, 2007 - Fixed a bug involving the inheritance of dependency property values into the ListBox shown in the SmartTextBox's adorner layer. Zhou Yong (aka Sheva) and Ian Griffiths helped find the solution. Sheva blogged about it here. The issue was resolved in this thread on the WPF Forum. I removed an incorrect statement about the relationship between an element's visual tree and its adorner layer from this article. I also updated the article's source code download.


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


About the Author

Josh Smith
Software Developer (Senior) Black Pixel
United States United States
Josh creates software, for iOS and Windows.

He works at Black Pixel as a Senior Developer.

Read his iOS Programming for .NET Developers[^] book to learn how to write iPhone and iPad apps by leveraging your existing .NET skills.

Use his Master WPF[^] app on your iPhone to sharpen your WPF skills on the go.

Check out his Advanced MVVM[^] book.

Visit his WPF blog[^] or stop by his iOS blog[^].

See his website Josh Smith Digital[^].

You may also be interested in...

Comments and Discussions

GeneralPopup vs Adorner Pin
MGutman27-May-09 5:09
memberMGutman27-May-09 5:09 
RantSeems buggy to me Pin
Lance Roberts30-Apr-09 11:11
memberLance Roberts30-Apr-09 11:11 
Generalcool Pin
zhujinlong1984091322-Apr-09 23:04
groupzhujinlong1984091322-Apr-09 23:04 
QuestionDefining mispelt words Pin
Paul Coldrey11-Nov-08 13:53
groupPaul Coldrey11-Nov-08 13:53 
AnswerRe: Defining mispelt words Pin
Josh Smith12-Nov-08 8:12
memberJosh Smith12-Nov-08 8:12 
QuestionWindow maximized, size change, etc Pin
cf200622-Aug-08 8:00
membercf200622-Aug-08 8:00 
Questiononly for VISTA? Pin
Boniolopez27-Feb-07 0:57
memberBoniolopez27-Feb-07 0:57 
AnswerRe: only for VISTA? Pin
Josh Smith27-Feb-07 2:01
memberJosh Smith27-Feb-07 2:01 
GeneralSpellcheck for any platform [modified] Pin
Boniolopez27-Feb-07 3:21
memberBoniolopez27-Feb-07 3:21 
AnswerRe: only for VISTA? Pin
yassir hannoun7-Mar-08 6:29
memberyassir hannoun7-Mar-08 6:29 
GeneralGreat article...But I have a question Pin
Sven Cipido26-Feb-07 20:49
memberSven Cipido26-Feb-07 20:49 
GeneralRe: Great article...But I have a question Pin
Josh Smith27-Feb-07 1:58
memberJosh Smith27-Feb-07 1:58 
AnswerRe: Great article...But I have a question Pin
Adrian Betschart27-Feb-07 5:01
memberAdrian Betschart27-Feb-07 5:01 
GeneralRe: Great article...But I have a question Pin
Josh Smith27-Feb-07 5:17
memberJosh Smith27-Feb-07 5:17 
GeneralSmart & Delicious...the two things I like in a control. Pin
Capt Phunkosis20-Feb-07 8:39
memberCapt Phunkosis20-Feb-07 8:39 
GeneralRe: Smart & Delicious...the two things I like in a control. Pin
Josh Smith20-Feb-07 8:58
memberJosh Smith20-Feb-07 8:58 
GeneralGreat article! Pin
Marc Clifton20-Feb-07 5:20
protectorMarc Clifton20-Feb-07 5:20 
GeneralRe: Great article! Pin
Josh Smith20-Feb-07 6:21
memberJosh Smith20-Feb-07 6:21 
GeneralRe: Great article! Pin
Marc Clifton20-Feb-07 10:36
protectorMarc Clifton20-Feb-07 10:36 
GeneralRe: Great article! Pin
Josh Smith20-Feb-07 10:52
memberJosh Smith20-Feb-07 10:52 
GeneralRe: Great article! Pin
Marc Clifton20-Feb-07 11:22
protectorMarc Clifton20-Feb-07 11:22 
GeneralRe: Great article! Pin
Josh Smith20-Feb-07 12:11
memberJosh Smith20-Feb-07 12:11 

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.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190306.1 | Last Updated 26 Feb 2007
Article Copyright 2007 by Josh Smith
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid