Click here to Skip to main content
15,042,088 members
Articles / Desktop Programming / WPF
Article
Posted 21 May 2012

Tagged as

Stats

39K views
1.6K downloads
12 bookmarked

Spell Check Enabled RichText Editor in WPF

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
13 Apr 2013CPOL2 min read
Creating a spell check enabled RichText editor.

Image 1

Font Selection

Image 2

Formatting Text

Image 3

Spell Checking

Introduction

In this article, I am describing how to create a spell check enabled Rich Text editor in WPF. My main aim is to develop a rich text editor in the fastest possible way using minimum steps without going into the extreme intricacies of WPF.

Background

The RichTextBox control can be used to create a Rich Text Editor. A RichTextBox control allows you to work with text, paragraphs, images, tables, and other such content. A Rich Text Box can be added by using the following code:

XML
<RichTextBox Margin="11,100,14,9" Name="richTextBox1"/>
Adding the spell check feature is fairly simple. You just need to set the SpellCheck.IsEnabled dependency property to true as follows:
<RichTextBox Margin="11,100,14,9" Name="richTextBox1" SpellCheck.IsEnabled="True" />

The RichTextBox control allows you to manipulate its content using a Document object. The Document object can be retrieved using the Document property of the RichTextBox control.

Using the code

The following XAML code is used to create bitmap resources for the toolbar icons:

XML
<Window.Resources>
    <BitmapImage x:Key="newicon" UriSource="new.png"/>
    <BitmapImage x:Key="openicon" UriSource="open.png"/>
    <BitmapImage x:Key="saveicon" UriSource="save.png"/>
    <BitmapImage x:Key="exiticon" UriSource="exit.png"/>
    <BitmapImage x:Key="undoicon" UriSource="undo.png"/>
    <BitmapImage x:Key="redoicon" UriSource="redo.png"/>
    <BitmapImage x:Key="selectallicon" UriSource="selectall.png"/>
    <BitmapImage x:Key="cuticon" UriSource="cut.png"/>
    <BitmapImage x:Key="copyicon" UriSource="copy.png"/>
    <BitmapImage x:Key="pasteicon" UriSource="paste.png"/>
    <BitmapImage x:Key="lefticon" UriSource="leftalign.png"/>
    <BitmapImage x:Key="centericon" UriSource="centeralign.png"/>
    <BitmapImage x:Key="righticon" UriSource="rightalign.png"/>
    <BitmapImage x:Key="boldicon" UriSource="bold.png"/>
    <BitmapImage x:Key="italicicon" UriSource="italic.png"/>
    <BitmapImage x:Key="underlineicon" UriSource="underline.png"/>
    <BitmapImage x:Key="increasefonticon" UriSource="increasefont.png"/>
    <BitmapImage x:Key="decreasefonticon" UriSource="decreasefont.png"/>
    <BitmapImage x:Key="abouticon" UriSource="about.png"/>
</Window.Resources>

The following code creates a Grid and adds CommandBindings to its CommandBindings collection:

XML
<Grid>
    <Grid.CommandBindings>
        <CommandBinding x:Name="newbinding" Command="ApplicationCommands.New" 
           CanExecute="newbinding_CanExecute" Executed="newbinding_Executed"/>
        <CommandBinding x:Name="openbinding" Command="ApplicationCommands.Open" 
           CanExecute="openbinding_CanExecute" Executed="openbinding_Executed"/>
        <CommandBinding x:Name="savebinding" Command="ApplicationCommands.Save" 
           CanExecute="savebinding_CanExecute" Executed="savebinding_Executed"/>
        <CommandBinding x:Name="exitbinding" Command="local:CustomCommands.Exit" 
           CanExecute="exitbinding_CanExecute" Executed="exitbinding_Executed"/>

In the above code, the Command attribute of the CommandBinding specifies the commands as New, Open, Save, and Exit. The CanExecute event determines whether the command can be executed on the specified command target. The Executed event specifies the action that should occur when the command is executed.

A point worth noting here is that there is no Exit command in the ApplicationCommands class. As a workaround, we can create a custom Exit command as follows:

C#
public static class CustomCommands
{
    static CustomCommands()
    {
        exitCommand = new RoutedCommand("Exit", typeof(CustomCommands));
    }
    public static RoutedCommand Exit
    {
        get
        {
            return (exitCommand);
        }
    }
    static RoutedCommand exitCommand;
}

To use our custom exit command, we add the following reference in the window:

XML
xmlns:local="clr-namespace:RichTextPad"

Following is the code for our commands:

C#
private void newbinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true; // Allow the command to execute.
}

private void newbinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
    TextRange range = new TextRange(richTextBox1.Document.ContentStart, 
        richTextBox1.Document.ContentEnd); // Get RichTextBox content.
    range.Text = "";    // Clear RichTextBox content.
    this.Title = "Untitled";
}

private void openbinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

private void openbinding_Executed(object sender, ExecutedRoutedEventArgs e)
// Code to open existing RTF file.
{
    try
    {
        FileDialog dialog = new OpenFileDialog();
        dialog.Filter = "RTF Files (*.rtf)|*.rtf|All Files(*.*)|*.*";
        dialog.FilterIndex = 1;
        if (dialog.ShowDialog() == true)
        {
            TextRange range = new TextRange(richTextBox1.Document.ContentStart, 
                                  richTextBox1.Document.ContentEnd);
            FileStream stream = new FileStream(dialog.FileName, FileMode.Open, 
                                    FileAccess.Read, FileShare.None);
            range.Load(stream, DataFormats.Rtf);
            stream.Close();
            this.Title = dialog.FileName;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

private void savebinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

private void savebinding_Executed(object sender, ExecutedRoutedEventArgs e)
// Code to save the current file.
{
    try
    {
        FileDialog dialog = new SaveFileDialog();
        dialog.Filter = "RTF Files (*.rtf)|*.rtf|All Files(*.*)|*.*";
        dialog.FilterIndex = 1;
        if (dialog.ShowDialog() == true)
        {
            TextRange range = new TextRange(richTextBox1.Document.ContentStart, 
                                            richTextBox1.Document.ContentEnd);
            FileStream stream = new FileStream(dialog.FileName, FileMode.Create, 
                                               FileAccess.Write, FileShare.None);
            range.Save(stream, DataFormats.Rtf);
            stream.Close();
            this.Title = dialog.FileName;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

private void exitbinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

private void exitbinding_Executed(object sender, ExecutedRoutedEventArgs e)
// Code to exit from the application
{
    Application.Current.Shutdown();
}

In the above functions, I am using the TextRange class to get the RichTextBox content and stream input-output to open/save file.

The following predefined commands can be used to implement the various text editing operations on the RichTextBox control.

XML
<CommandBinding x:Name="undobinding" Command="ApplicationCommands.Undo"/>
<CommandBinding x:Name="redobinding" Command="ApplicationCommands.Redo"/>
<CommandBinding x:Name="selectallbinding" Command="ApplicationCommands.SelectAll"/>
<CommandBinding x:Name="cutbinding" Command="ApplicationCommands.Cut"/>
<CommandBinding x:Name="copybinding" Command="ApplicationCommands.Copy"/>
<CommandBinding x:Name="pastebinding" Command="ApplicationCommands.Paste"/>
<CommandBinding x:Name="leftalignbinding" Command="EditingCommands.AlignLeft"/>
<CommandBinding x:Name="centeralignbinding" Command="EditingCommands.AlignCenter"/>
<CommandBinding x:Name="rightalignbinding" Command="EditingCommands.AlignRight"/>
<CommandBinding x:Name="boldbinding" Command="EditingCommands.ToggleBold"/>
<CommandBinding x:Name="italicbinding" Command="EditingCommands.ToggleItalic"/>
<CommandBinding x:Name="underlinebinding" Command="EditingCommands.ToggleUnderline"/>
<CommandBinding x:Name="increasefontbinding" Command="EditingCommands.IncreaseFontSize"/>
<CommandBinding x:Name="decreasefontbinding" Command="EditingCommands.DecreaseFontSize"/>

The following code creates the toolbar buttons to execute the commands:

XML
<ToolBarTray Background="White">
    <ToolBar Band="1" BandIndex="1" Height="50">
        <Button CommandTarget="{Binding richTextBox1}" Command="ApplicationCommands.New" ToolTip="New">
            <Image Source="{StaticResource newicon}"/>
        </Button>
        <Button CommandTarget="{Binding richTextBox1}" Command="ApplicationCommands.Open" ToolTip="Open">
            <Image Source="{StaticResource openicon}"/>
        </Button>
        <Button CommandTarget="{Binding richTextBox1}" Command="ApplicationCommands.Save" ToolTip="Save">
            <Image Source="{StaticResource saveicon}"/>
        </Button>
        <Button CommandTarget="{Binding richTextBox1}" Command="local:CustomCommands.Exit" ToolTip="Exit">
            <Image Source="{StaticResource exiticon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="1" BandIndex="2" Height="50">
        <Button Command="ApplicationCommands.Undo" ToolTip="Undo">
            <Image Source="{StaticResource undoicon}"/>
        </Button>
        <Button Command="ApplicationCommands.Redo" ToolTip="Redo">
            <Image Source="{StaticResource redoicon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="1" BandIndex="3" Height="50">
        <Button Command="ApplicationCommands.SelectAll" ToolTip="Select All">
            <Image Source="{StaticResource selectallicon}"/>
        </Button>
        <Button Command="ApplicationCommands.Cut" ToolTip="Cut">
            <Image Source="{StaticResource cuticon}"/>
        </Button>
        <Button Command="ApplicationCommands.Copy" ToolTip="Copy">
            <Image Source="{StaticResource copyicon}"/>
        </Button>
        <Button Command="ApplicationCommands.Paste" ToolTip="Paste">
            <Image Source="{StaticResource pasteicon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="1" BandIndex="4" Height="50">
        <Button Command="EditingCommands.AlignLeft" ToolTip="Align Left">
            <Image Source="{StaticResource lefticon}"/>
        </Button>
        <Button Command="EditingCommands.AlignCenter" ToolTip="Align Center">
            <Image Source="{StaticResource centericon}"/>
        </Button>
        <Button Command="EditingCommands.AlignRight" ToolTip="Align Right">
            <Image Source="{StaticResource righticon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="2" BandIndex="1" Height="50">
        <Button Command="EditingCommands.ToggleBold" ToolTip="Bold">
            <Image Source="{StaticResource boldicon}"/>
        </Button>
        <Button Command="EditingCommands.ToggleItalic" ToolTip="Italic">
            <Image Source="{StaticResource italicicon}"/>
        </Button>
        <Button Command="EditingCommands.ToggleUnderline" ToolTip="Underline">
            <Image Source="{StaticResource underlineicon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="2" BandIndex="2" Height="50">
        <ComboBox Name="cbFonts" ItemsSource="{x:Static Fonts.SystemFontFamilies}" 
                   SelectedIndex="0" ToolTip="Font">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" FontFamily="{Binding}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
        <Button Command="EditingCommands.IncreaseFontSize" ToolTip="Increase Font Size">
            <Image Source="{StaticResource increasefonticon}"/>
        </Button>
        <Button Command="EditingCommands.DecreaseFontSize" ToolTip="Decrease Font Size">
            <Image Source="{StaticResource decreasefonticon}"/>
        </Button>
    </ToolBar>
    <ToolBar Band="2" BandIndex="3" Height="50">
        <ext:ColorPicker Name="colorChooser" 
           SelectedColorChanged="colorChooser_SelectedColorChanged" ToolTip="Text Color"/>
    </ToolBar>
    <ToolBar Band="2" BandIndex="4" Height="50">
        <Button Name="btnAbout" ToolTip="About RichTextPad" Click="btnAbout_Click">
            <Image Source="{StaticResource abouticon}"/>
        </Button>
    </ToolBar>
</ToolBarTray>

The following code is used to set the font on the selected text:

C#
void cbFonts_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    TextRange range = new TextRange(richTextBox1.Selection.Start, 
        richTextBox1.Selection.End);    // Get RichTextBox content.
    range.ApplyPropertyValue(RichTextBox.FontFamilyProperty, 
        cbFonts.SelectedValue);    // Set the font selected from ComboBox on the RichTextBox
}

Since there is no color selection dialog box in WPF, I have used the one from the Extended WPF Toolkit. A reference to the Extended WPF Toolkit library is added to the project and the following namespace added:

XML
xmlns:ext="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"

The color picker control is added as follows:

XML
<ext:ColorPicker Name="colorChooser" 
   SelectedColorChanged="colorChooser_SelectedColorChanged" ToolTip="Text Color"/>

The following code is used to set color on the selected text:

C#
private void colorChooser_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<color> e)
{
    // Get RichTextBox content.
    TextRange range = new TextRange(richTextBox1.Selection.Start, richTextBox1.Selection.End);
    range.ApplyPropertyValue(RichTextBox.ForegroundProperty, 
          new SolidColorBrush(colorChooser.SelectedColor));
          // Apply the selected color.
}</color>

Points of interest

WPF provides a rich programming model for manipulating the RichTextBox. Once I started working on this project, I realized the many capabilities of the WPF RichTextBox control. I hope that my article will help readers in understanding these concepts in a short time span.

License

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

Share

About the Author

Azim Zahir
Instructor / Trainer NIIT, India
India India
I am a trainer by profession. Currently I am working with iFuture Technologies(India) as a Senior Faculty. I enjoy programming as a hobby. During my career I have seen the growth and decline of many technologies, many of them being my favorites like Flash, WPF, Windows Mobile Development. Few of my current favorites are Android, Xamarin and Python, though I also like traditional and evergreen languages like PHP, C#, Visual Basic and Java.

Apart from computers, my favorite pastime is bicycling.

Comments and Discussions

 
QuestionIgnore Alphanumeric? Pin
fsunole036-Nov-14 5:22
Memberfsunole036-Nov-14 5:22 
AnswerRe: Ignore Alphanumeric? Pin
Azim Zahir7-Nov-14 23:32
MemberAzim Zahir7-Nov-14 23:32 
GeneralRe: Ignore Alphanumeric? Pin
fsunole0325-Jan-15 14:33
Memberfsunole0325-Jan-15 14:33 
QuestionWinforms version Pin
trantrum16-Jun-13 23:23
professionaltrantrum16-Jun-13 23:23 
AnswerRe: Winforms version Pin
Azim Zahir17-Jun-13 16:21
MemberAzim Zahir17-Jun-13 16:21 
QuestionBackgroundProperty SolidColorBrush(Colors.White) Pin
Member 848494116-Jul-12 4:12
MemberMember 848494116-Jul-12 4:12 
AnswerRe: BackgroundProperty SolidColorBrush(Colors.White) Pin
Azim Zahir17-Jul-12 4:31
MemberAzim Zahir17-Jul-12 4:31 
GeneralMy vote of 5 Pin
dilukawittachchi19-Jun-12 5:33
Memberdilukawittachchi19-Jun-12 5:33 
GeneralRe: My vote of 5 Pin
Azim Zahir19-Jun-12 20:42
MemberAzim Zahir19-Jun-12 20:42 
QuestionExcelent Pin
hackerspk2-Jun-12 0:02
Memberhackerspk2-Jun-12 0:02 
AnswerRe: Excelent Pin
Azim Zahir3-Jun-12 5:32
MemberAzim Zahir3-Jun-12 5:32 
GeneralRe: Excelent Pin
hackerspk4-Jun-12 3:25
Memberhackerspk4-Jun-12 3:25 
when any word is misspelled theirs comes a red line under that word to identify the wrong word. I want to know how can i make this. Iam using Nhunspell library for spellcheck. All is well accept of marking red line under the wrong word. Iam doing this in vb.net 2008. I saw that you are doing this line by line. which is a great thing and prevent program from overload or hang. How can i do this for a MS default Richtextbox in Vb.net
GeneralRe: Excelent Pin
Azim Zahir4-Jun-12 3:49
MemberAzim Zahir4-Jun-12 3:49 
GeneralRe: Excelent Pin
hackerspk4-Jun-12 3:57
Memberhackerspk4-Jun-12 3:57 
GeneralRe: Excelent Pin
Azim Zahir4-Jun-12 5:28
MemberAzim Zahir4-Jun-12 5:28 
GeneralRe: Excelent Pin
hackerspk4-Jun-12 5:54
Memberhackerspk4-Jun-12 5:54 

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.