Create A Rich Text Editor Using WPF

Here are the steps to create a Rich Text Editor using WPF

Software Requirements:
  • .NET Framework 3.5 or higher
  • Visual studio 2010 or higher

Control Requirements:

  • One Rich Textbox
  • Ribbon Controls to add menu like Microsoft Word
  1. Open Visual Studio and create a new WPF application with C# code as shown below,

    WPF

  2. Download and add RibbonControlsLibrary reference and other existing References as shown below,

    • Right click on References and goto Add Reference, Assemblies, then Framework and add two references from this:

    a. System.Drawing
    b. System.Windows.Forms

    • Add downloaded Reference from Add Reference, Browse, then RibbonControlsLibrary.dll file and click Ok (if you downloaded from NuGet packages from VS then it will be added automatically to your project, so no need to browse this reference),

    a. Figure is showing the example for adding references.

    DRAWING

  3. Now the time for the design as shown below,

    • Add required image files
    • Add controls to the window in VS

    a. Add Ribbon control and Rich textbox
    b. To add Ribbon control add xaml class code in the beginning of window XAML code as

    xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"

    XML

    Xaml Source
    1. <Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon" Title="Editor" Width="1024" Height="700" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">  
    2.     <Grid>  
    3.         <Frame Background="White" />  
    4.         <my:Ribbon Name="_ribbon" Margin="0,1,0,361">  
    5.             <my:Ribbon.ApplicationMenu>  
    6.                 <my:RibbonApplicationMenu Visibility="Collapsed">  
    7.                 </my:RibbonApplicationMenu>  
    8.             </my:Ribbon.ApplicationMenu>  
    9.             <my:RibbonTab Header="Home">  
    10.                 <my:RibbonGroup Header="Edit">  
    11.                     <my:RibbonButton x:Name="_btnPaste" Label="Paste" LargeImageSource="/Images/Paste32.png" ToolTip="Paste" Command="{x:StaticApplicationCommands.Paste}" CommandTarget="{Binding ElementName=_richTextBox}">  
    12.                         <my:RibbonButton.ControlSizeDefinition>  
    13.                             <my:RibbonControlSizeDefinitionImageSize="Large" />  
    14.                         </my:RibbonButton.ControlSizeDefinition>  
    15.                     </my:RibbonButton>  
    16.                     <my:RibbonButton x:Name="_btnCut" Label="Cut" SmallImageSource="/Images/Cut16.png" ToolTip="Cut" Command="{x:StaticApplicationCommands.Cut}" CommandTarget="{Binding ElementName=_richTextBox}">  
    17.                         <my:RibbonButton.ControlSizeDefinition>  
    18.                             <my:RibbonControlSizeDefinitionImageSize="Small" />  
    19.                         </my:RibbonButton.ControlSizeDefinition>  
    20.                     </my:RibbonButton>  
    21.                     <my:RibbonButton x:Name="_btnCopy" Label="Copy" SmallImageSource="/Images/Copy16.png" ToolTip="Copy" Command="{x:StaticApplicationCommands.Copy}" CommandTarget="{Binding ElementName=_richTextBox}">  
    22.                         <my:RibbonButton.ControlSizeDefinition>  
    23.                             <my:RibbonControlSizeDefinitionImageSize="Small" />  
    24.                         </my:RibbonButton.ControlSizeDefinition>  
    25.                     </my:RibbonButton>  
    26.                     <my:RibbonButton x:Name="_btnClear" Label="Clear" SmallImageSource="/Images/Delete16.png" ToolTip="Clear" Command="{x:StaticEditingCommands.Delete}" CommandTarget="{Binding ElementName=_richTextBox}">  
    27.                         <my:RibbonButton.ControlSizeDefinition>  
    28.                             <my:RibbonControlSizeDefinitionImageSize="Small" />  
    29.                         </my:RibbonButton.ControlSizeDefinition>  
    30.                     </my:RibbonButton>  
    31.                     <my:RibbonButton x:Name="_btnUndo" Label="Undo" SmallImageSource="/Images/Undo16.png " ToolTip="Undo" Command="{x:StaticApplicationCommands.Undo}" CommandTarget="{Binding ElementName=_richTextBox}">  
    32.                         <my:RibbonButton.ControlSizeDefinition>  
    33.                             <my:RibbonControlSizeDefinitionImageSize="Small" />  
    34.                         </my:RibbonButton.ControlSizeDefinition>  
    35.                     </my:RibbonButton>  
    36.                     <my:RibbonButton x:Name="_bntRedo" Label="Redo" SmallImageSource="/Images/Redo16.png" ToolTip="Redo" Command="{x:StaticApplicationCommands.Redo}" CommandTarget="{Binding ElementName=_richTextBox}">  
    37.                         <my:RibbonButton.ControlSizeDefinition>  
    38.                             <my:RibbonControlSizeDefinitionImageSize="Small" />  
    39.                         </my:RibbonButton.ControlSizeDefinition>  
    40.                     </my:RibbonButton>  
    41.                     <my:RibbonButton x:Name="_btnSelectAll" Label="Select All" ToolTip="Select All" Command="{x:StaticApplicationCommands.SelectAll}" CommandTarget="{Binding ElementName=_richTextBox}" />  
    42.                 </my:RibbonGroup>  
    43.                 <my:RibbonGroup Header="Font">  
    44.                     <my:RibbonControlGroup>  
    45.                         <ComboBox x:Name="_fontFamily" IsEditable="True" Width="110" ToolTip="Font" SelectionChanged="FontFamily_SelectionChanged" />  
    46.                         <ComboBox x:Name="_fontSize" IsEditable="True" Width="45" ToolTip="FontSize" SelectionChanged="FontSize_SelectionChanged" />  
    47.                     </my:RibbonControlGroup>  
    48.                     <my:RibbonControlGroup>  
    49.                         <my:RibbonToggleButton x:Name="_btnBold" ToolTip="Bold" SmallImageSource="/Images/Bold16.png" Command="{x:StaticEditingCommands.ToggleBold}" CommandTarget="{Binding ElementName=_richTextBox}">  
    50.                             <my:RibbonToggleButton.ControlSizeDefinition>  
    51.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    52.                             </my:RibbonToggleButton.ControlSizeDefinition>  
    53.                         </my:RibbonToggleButton>  
    54.                         <my:RibbonToggleButton x:Name="_btnItalic" SmallImageSource="/Images/Italic16.png" ToolTip="Italic" Command="{x:StaticEditingCommands.ToggleItalic}" CommandTarget="{Binding ElementName=_richTextBox}">  
    55.                             <my:RibbonToggleButton.ControlSizeDefinition>  
    56.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    57.                             </my:RibbonToggleButton.ControlSizeDefinition>  
    58.                         </my:RibbonToggleButton>  
    59.                         <my:RibbonToggleButton x:Name="_btnUnderline" SmallImageSource="/Images/Underline16.png" ToolTip="Underline" Command="{x:StaticEditingCommands.ToggleUnderline}" CommandTarget="{Binding ElementName=_richTextBox}">  
    60.                             <my:RibbonToggleButton.ControlSizeDefinition>  
    61.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    62.                             </my:RibbonToggleButton.ControlSizeDefinition>  
    63.                         </my:RibbonToggleButton>  
    64.                     </my:RibbonControlGroup>  
    65.                 </my:RibbonGroup>  
    66.                 <my:RibbonGroup Header="Paragraph">  
    67.                     <my:RibbonControlGroup>  
    68.                         <my:RibbonRadioButton x:Name="_btnAlignLeft" Label="" SmallImageSource="/Images/LeftAlign16.png" ToolTip="Align Text Left" Command="{x:StaticEditingCommands.AlignLeft}" CommandTarget="{Binding ElementName=_richTextBox}">  
    69.                             <my:RibbonRadioButton.ControlSizeDefinition>  
    70.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" /></my:RibbonRadioButton.ControlSizeDefinition>  
    71.                         </my:RibbonRadioButton>  
    72.                         <my:RibbonRadioButton x:Name="_btnAlignCenter" Label="" SmallImageSource="/Images/CenterAlign16.png" ToolTip="Center" Command="{x:StaticEditingCommands.AlignCenter}" CommandTarget="{Binding ElementName=_richTextBox}">  
    73.                             <my:RibbonRadioButton.ControlSizeDefinition>  
    74.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    75.                             </my:RibbonRadioButton.ControlSizeDefinition>  
    76.                         </my:RibbonRadioButton>  
    77.                         <my:RibbonRadioButton x:Name="_btnAlignRight" Label="" SmallImageSource="/Images/RightAlign16.png" ToolTip="Align Text Right" Command="{x:StaticEditingCommands.AlignRight}" CommandTarget="{Binding ElementName=_richTextBox}" />  
    78.                         <my:RibbonRadioButton x:Name="_btnAlignJustify" Label="" SmallImageSource="/Images/JustifyAlign16.png" ToolTip="Justify" Command="{x:StaticEditingCommands.AlignJustify}" CommandTarget="{Binding ElementName=_richTextBox}">  
    79.                             <my:RibbonRadioButton.ControlSizeDefinition>  
    80.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    81.                             </my:RibbonRadioButton.ControlSizeDefinition>  
    82.                         </my:RibbonRadioButton>  
    83.                     </my:RibbonControlGroup>  
    84.                     <my:RibbonControlGroup>  
    85.                         <my:RibbonRadioButton x:Name="_btnBullets" Label="" mallImageSource="/Images/Bullets16.png" ToolTip="Bullets" Command="{x:StaticEditingCommands.ToggleBullets}" CommandTarget="{Binding ElementName=_richTextBox}">  
    86.                             <my:RibbonRadioButton.ControlSizeDefinition>  
    87.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    88.                             </my:RibbonRadioButton.ControlSizeDefinition>  
    89.                         </my:RibbonRadioButton>  
    90.                         <my:RibbonRadioButton x:Name="_btnNumbers" Label="" SmallImageSource="/Images/Numbering16.png" ToolTip="Numbering" Command="{x:StaticEditingCommands.ToggleNumbering}" CommandTarget="{Binding ElementName=_richTextBox}">  
    91.                             <my:RibbonRadioButton.ControlSizeDefinition>  
    92.                                 <my:RibbonControlSizeDefinitionImageSize="Small" IsLabelVisible="False" />  
    93.                             </my:RibbonRadioButton.ControlSizeDefinition>  
    94.                         </my:RibbonRadioButton>  
    95.                     </my:RibbonControlGroup>  
    96.                 </my:RibbonGroup>  
    97.                 <my:RibbonGroup Header="Image">  
    98.                     <my:RibbonButton Width="53" Height="39" LargeImageSource="/Images/Images.png" ToolTip="Insert Image" Click="btn_importimg_Click" Margin="5,15,5,5">  
    99.                         <my:RibbonButton.ControlSizeDefinition>  
    100.                             <my:RibbonControlSizeDefinitionImageSize="Large" />  
    101.                         </my:RibbonButton.ControlSizeDefinition>  
    102.                     </my:RibbonButton>  
    103.                 </my:RibbonGroup>  
    104.                 <my:RibbonGroup Header="Font Color">  
    105.                     <my:RibbonButton Width="53" Height="Auto" LargeImageSource="/Images/font_color.png" ToolTip="Change font color" Click="btn_Font_Click" Margin="5,20,5,5">  
    106.                         <my:RibbonButton.ControlSizeDefinition>  
    107.                             <my:RibbonControlSizeDefinitionImageSize="Large" />  
    108.                         </my:RibbonButton.ControlSizeDefinition>  
    109.                     </my:RibbonButton>  
    110.                 </my:RibbonGroup>  
    111.                 <my:RibbonGroup Header="File">  
    112.                     <my:RibbonButtonLargeImageSource="/Images/Open.png" ToolTip="Open Document File(.doc)" Click="btn_OpenDoc_Click" Margin="5,20,5,5" Height="38" Width="54">  
    113.                         <my:RibbonButton.ControlSizeDefinition>  
    114.                             <my:RibbonControlSizeDefinitionImageSize="Large" />  
    115.                         </my:RibbonButton.ControlSizeDefinition>  
    116.                         </my:RibbonButton>  
    117.                         <my:RibbonButtonLargeImageSource="/Images/Save.ico" ToolTip="Save As Doc" Click="btn_SaveDoc_Click" Margin="5,20,5,5" Height="41">  
    118.                             <my:RibbonButton.ControlSizeDefinition>  
    119.                                 <my:RibbonControlSizeDefinitionImageSize="Large" />  
    120.                             </my:RibbonButton.ControlSizeDefinition>  
    121.                             </my:RibbonButton>  
    122.                 </my:RibbonGroup>  
    123.             </my:RibbonTab>  
    124.         </my:Ribbon>  
    125.         <RichTextBoxAcceptsTab="True" VerticalScrollBarVisibility="Auto" TabIndex="3" SpellCheck.IsEnabled="True" Margin="0,138,0,0" x:Name="Workspace" BorderBrush="Black" BorderThickness="0.7,0.7,0.7,0.7" Height="532" VerticalAlignment="Top" SelectionChanged="Workspace_SelectionChanged" />  
    126.     </Grid>  
    127. </Window>  
  4. Now is the time to add some functionality to this editor. To do that click F7 or go to C# code and write as given below,

    • Import some references to it as below,
    1. using System.Windows.Navigation;  
    2. using System.Windows.Controls.Primitives;  
    3. using System.IO;  
    4. using Microsoft.Win32;  
    • To add fonts and font size after page initializes write code in MainWindow() as below,
    1. public MainWindow()  
    2. {  
    3. InitializeComponent();  
    4. _fontFamily.ItemsSource = System.Windows.Media.Fonts.SystemFontFamilies;  
    5. _fontSize.ItemsSource = FontSizes;  
    6. }  
    7. public double[] FontSizes  
    8. {  
    9. get  
    10. {  
    11. return new double[] { 3.0, 4.0, 5.0, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5, 11.0, 11.5, 12.0, 12.5,13.0,13.5,14.0, 15.0,16.0, 17.0, 18.0, 19.0, 20.0, 22.0, 24.0, 26.0, 28.0, 30.0,32.0, 34.0, 36.0, 38.0, 40.0, 44.0, 48.0, 52.0, 56.0, 60.0, 64.0, 68.0, 72.0, 76.0,80.0, 88.0, 96.0, 104.0, 112.0, 120.0, 128.0, 136.0, 144.0  
    12. };  
    13. }  
    14. }  
    • Selection change event as below,
    1. void ApplyPropertyValueToSelectedText(DependencyPropertyformattingProperty, object value)  
    2. {  
    3.     if (value == null)  
    4.         return;  
    5.     Workspace.Selection.ApplyPropertyValue(formattingProperty, value);  
    6. }  
    7. private void FontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e)   
    8. {  
    9.     try  
    10.     {  
    11.         FontFamilyeditValue = (FontFamily) e.AddedItems[0];  
    12.         ApplyPropertyValueToSelectedText(TextElement.FontFamilyProperty, editValue);  
    13.     } catch (Exception) {}  
    14. }  
    15. private void FontSize_SelectionChanged(object sender, SelectionChangedEventArgs e)   
    16. {  
    17.     try  
    18.     {  
    19.         ApplyPropertyValueToSelectedText(TextElement.FontSizeProperty, e.AddedItems[0]);  
    20.     } catch (Exception) {}  
    21. }  
    • Add other functionalities during selection change event of RichText box as given below,
    1. void UpdateItemCheckedState(ToggleButton button, DependencyPropertyformattingProperty, object expectedValue)   
    2. {  
    3.     object currentValue = Workspace.Selection.GetPropertyValue(formattingProperty);  
    4.     button.IsChecked = (currentValue == DependencyProperty.UnsetValue) ? false : currentValue != null && currentValue.Equals(expectedValue);  
    5. }  
    6. private void UpdateToggleButtonState()  
    7. {  
    8.     UpdateItemCheckedState(_btnBold, TextElement.FontWeightProperty, FontWeights.Bold);  
    9.     UpdateItemCheckedState(_btnItalic, TextElement.FontStyleProperty, FontStyles.Italic);  
    10.     UpdateItemCheckedState(_btnUnderline, Inline.TextDecorationsProperty, TextDecorations.Underline);  
    11.     UpdateItemCheckedState(_btnAlignLeft, Paragraph.TextAlignmentProperty, TextAlignment.Left);  
    12.     UpdateItemCheckedState(_btnAlignCenter, Paragraph.TextAlignmentProperty, TextAlignment.Center);  
    13.     UpdateItemCheckedState(_btnAlignRight, Paragraph.TextAlignmentProperty, TextAlignment.Right);  
    14.     UpdateItemCheckedState(_btnAlignJustify, Paragraph.TextAlignmentProperty, TextAlignment.Right);  
    15. }  
    16. private void UpdateSelectionListType()   
    17. {  
    18.     Paragraph startParagraph = Workspace.Selection.Start.Paragraph;  
    19.     Paragraph endParagraph = Workspace.Selection.End.Paragraph;  
    20.     if (startParagraph != null && endParagraph != null && (startParagraph.Parent is ListItem) && (endParagraph.Parent is ListItem) && object.ReferenceEquals(((ListItem) startParagraph.Parent).List, ((ListItem) endParagraph.Parent).List)) {  
    21.         TextMarkerStylemarkerStyle = ((ListItem) startParagraph.Parent).List.MarkerStyle;  
    22.         if (markerStyle == TextMarkerStyle.Disc) //bullets  
    23.         {  
    24.             _btnBullets.IsChecked = true;  
    25.         } else if (markerStyle == TextMarkerStyle.Decimal) //number  
    26.         {  
    27.             _btnNumbers.IsChecked = true;  
    28.         }  
    29.     } else {  
    30.         _btnBullets.IsChecked = false;  
    31.         _btnNumbers.IsChecked = false;  
    32.     }  
    33. }  
    34. private void UpdateSelectedFontFamily()  
    35. {  
    36.     object value = Workspace.Selection.GetPropertyValue(TextElement.FontFamilyProperty);  
    37.     FontFamilycurrentFontFamily = (FontFamily)((value == DependencyProperty.UnsetValue) ? null : value);  
    38.     if (currentFontFamily != null)   
    39.     {  
    40.         _fontFamily.SelectedItem = currentFontFamily;  
    41.     }  
    42. }  
    43. private void UpdateSelectedFontSize()  
    44. {  
    45.     object value = Workspace.Selection.GetPropertyValue(TextElement.FontSizeProperty);  
    46.     _fontSize.SelectedValue = (value == DependencyProperty.UnsetValue) ? null : value;  
    47. }  
    48. private void UpdateVisualState()  
    49. {  
    50.     UpdateToggleButtonState();  
    51.     UpdateSelectionListType();  
    52.     UpdateSelectedFontFamily();  
    53.     UpdateSelectedFontSize();  
    54. }  
    55. private void Workspace_SelectionChanged(object sender, RoutedEventArgs e) {  
    56.     UpdateVisualState();  
    57. }  
    • Add function for inserting image and for font color selection,

    For image insertion
    1. public void selectImg(RichTextBoxrc)   
    2. {  
    3.     OpenFileDialogdlg = new OpenFileDialog();  
    4.     dlg.Filter = "Image files (*.jpg, *.jpeg,*.gif, *.png) | *.jpg; *.jpeg; *.gif; *.png";  
    5.     var result = dlg.ShowDialog();  
    6.     if (result.Value)  
    7.     {  
    8.         Uri uri = new Uri(dlg.FileName, UriKind.Relative);  
    9.         BitmapImagebitmapImg = new BitmapImage(uri);  
    10.         Image image = new Image();  
    11.         image.Stretch = Stretch.Fill;  
    12.         image.Width = 250;  
    13.         image.Height = 200;  
    14.         image.Source = bitmapImg;  
    15.         vartp = rc.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);  
    16.         new InlineUIContainer(image, tp);  
    17.     }  
    18. }  
    19. private void btn_importimg_Click(object sender, RoutedEventArgs e)  
    20. {  
    21.     selectImg(Workspace);  
    22. }  
    For Font color change
    1. private void fontcolor(RichTextBoxrc)  
    2. {  
    3.     var colorDialog = new System.Windows.Forms.ColorDialog();  
    4.     if (colorDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)   
    5.     {  
    6.         var wpfcolor = Color.FromArgb(colorDialog.Color.A, colorDialog.Color.R, colorDialog.Color.G, colorDialog.Color.B);  
    7.         TextRange range = new TextRange(rc.Selection.Start, rc.Selection.End);  
    8.         range.ApplyPropertyValue(FlowDocument.ForegroundProperty, new SolidColorBrush(wpfcolor));  
    9.     }  
    10. }  
    11. private void btn_Font_Click(object sender, RoutedEventArgs e)  
    12. {  
    13.     fontcolor(Workspace);  
    14.  
    • Write the code for saving as Doc File and open Doc file,

    To save 
    1. private void btn_SaveDoc_Click(object sender, RoutedEventArgs e)  
    2. {  
    3.     SaveFileDialogsavefile = new SaveFileDialog();  
    4.     // set a default file name  
    5.     savefile.FileName = "unknown.doc";  
    6.     // set filters - this can be done in properties as well  
    7.     savefile.Filter = "Document files (*.doc)|*.doc";  
    8.     if (savefile.ShowDialog() == true)   
    9.     {  
    10.         TextRange t = new TextRange(Workspace.Document.ContentStart, Workspace.Document.ContentEnd);  
    11.         FileStream file = new FileStream(savefile.FileName, FileMode.Create);  
    12.         t.Save(file, System.Windows.DataFormats.Rtf);  
    13.         file.Close();  
    14.     }  
    15. }  
    To open
    1. private void btn_OpenDoc_Click(object sender, RoutedEventArgs e)  
    2. {  
    3.     OpenFileDialogdlg = new OpenFileDialog();  
    4.     dlg.Filter = "Document files (*.doc)|*.doc";  
    5.     var result = dlg.ShowDialog();  
    6.     if (result.Value)  
    7.     {  
    8.         TextRange t = new TextRange(Workspace.Document.ContentStart, Workspace.Document.ContentEnd);  
    9.         FileStream file = new FileStream(dlg.FileName, FileMode.Open);  
    10.         t.Load(file, System.Windows.DataFormats.Rtf);  
    11.     }  
    12. }  

Read more articles on WPF: