Font Selection
Formatting Text
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:
<RichTextBox Margin="11,100,14,9" Name="richTextBox1"/>Colourised in 1ms
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" />Colourised in 3ms
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:
<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>Colourised in 23ms
The following code creates a Grid
and adds CommandBinding
s to its
CommandBindings
collection:
<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"/>Colourised in 11ms
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:
public static class CustomCommands
{
static CustomCommands()
{
exitCommand = new RoutedCommand("Exit", typeof(CustomCommands));
}
public static RoutedCommand Exit
{
get
{
return (exitCommand);
}
}
static RoutedCommand exitCommand;
}Colourised in 7ms
To use our custom exit command, we add the following reference in the window:
xmlns:local="clr-namespace:RichTextPad"Colourised in 0ms
Following is the code for our commands:
private void newbinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; }
private void newbinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
TextRange range = new TextRange(richTextBox1.Document.ContentStart,
richTextBox1.Document.ContentEnd); range.Text = ""; this.Title = "Untitled";
}
private void openbinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void openbinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
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)
{
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)
{
Application.Current.Shutdown();
}Colourised in 59ms
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.
<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"/>Colourised in 17ms
The following code creates the toolbar buttons to execute the commands:
<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>Colourised in 87ms
The following code is used to set the font on the selected text:
void cbFonts_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TextRange range = new TextRange(richTextBox1.Selection.Start,
richTextBox1.Selection.End); range.ApplyPropertyValue(RichTextBox.FontFamilyProperty,
cbFonts.SelectedValue); }Colourised in 2ms
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:
xmlns:ext="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit/extended"Colourised in 0ms
The color picker control is added as follows:
<ext:ColorPicker Name="colorChooser"
SelectedColorChanged="colorChooser_SelectedColorChanged" ToolTip="Text Color"/>Colourised in 1ms
The following code is used to set color on the selected text:
private void colorChooser_SelectedColorChanged(object sender, RoutedPropertyChangedEventArgs<color> e)
{
TextRange range = new TextRange(richTextBox1.Selection.Start, richTextBox1.Selection.End);
range.ApplyPropertyValue(RichTextBox.ForegroundProperty,
new SolidColorBrush(colorChooser.SelectedColor));
}</color>Colourised in 3ms
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.