Word Editor in WPF
Often rich editing controls for desktop applications use the RTF format as their internal object model or provide support for importing and exporting RTF content.
GemBox.Document supports both reading and writing RTF, which enables you to combine it with such controls to create an advanced Word editor control, one that's capable of saving to PDF, printing, mail merging and a lot more.
The following example shows interoperability between GemBox.Document library and WPF's RichTextBox
control via RTF. The example also implements some common Word functionalities for text editing like modifying, styling, copying and pasting.
<Window x:Class="WpfRichTextEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Word Editor"
WindowState="Maximized">
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBarTray.Resources>
<Style TargetType="Image">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Width" Value="30"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="Palatino Linotype"/>
<Setter Property="FontSize" Value ="14"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</ToolBarTray.Resources>
<ToolBar Header="GemBox ToolBar" ToolTip="Commands in this group use GemBox.Document component">
<ToolBar.CommandBindings>
<CommandBinding Command="Open" Executed="Open"/>
<CommandBinding Command="Save" Executed="Save" CanExecute="CanSave" />
<CommandBinding Command="SaveAs" Executed="Save" CanExecute="CanSave" />
<CommandBinding Command="Cut" Executed="Cut" CanExecute="CanCut" />
<CommandBinding Command="Copy" Executed="Copy" CanExecute="CanCopy" />
<CommandBinding Command="Paste" Executed="Paste" CanExecute="CanPaste"/>
</ToolBar.CommandBindings>
<Button Command="Open" ToolTip="Open">
<Image Source="Icons/Open.png"/>
</Button>
<Button Command="Save" ToolTip="Save">
<Image Source="Icons/Save.png"/>
</Button>
<Separator/>
<Button Command="Cut" ToolTip="Cut with GemBox.Document">
<Image Source="Icons/Cut.png"/>
</Button>
<Button Command="Copy" ToolTip="Copy with GemBox.document">
<Image Source="Icons/Copy.png"/>
</Button>
<Button Command="Paste" CommandParameter="prepend" ToolTip="Paste prepend with GemBox.Document">
<Image Source="Icons/Paste.png"/>
</Button>
<Button Command="Paste" CommandParameter="append" ToolTip="Paste append with GemBox.Document">
<Image Source="Icons/Paste.png"/>
</Button>
</ToolBar>
<ToolBar Header="WPF ToolBar" ToolTip="Commands in this group use only WPF">
<Button Command="Undo" ToolTip="Undo">
<Image Source="Icons/Undo.png"/>
</Button>
<Button Command="Redo" ToolTip="Redo">
<Image Source="Icons/Redo.png"/>
</Button>
<Separator/>
<Button Command="Cut" ToolTip="Cut">
<Image Source="Icons/Cut.png"/>
</Button>
<Button Command="Copy" ToolTip="Copy">
<Image Source="Icons/Copy.png"/>
</Button>
<Button Command="Paste" ToolTip="Paste">
<Image Source="Icons/Paste.png"/>
</Button>
<Separator/>
<Button Command="ToggleBold" ToolTip="Bold">
<TextBlock Text="B" FontWeight="Bold"/>
</Button>
<Button Command="ToggleItalic" ToolTip="Italic">
<TextBlock Text="I" FontStyle="Italic"/>
</Button>
<Button Command="ToggleUnderline" ToolTip="Underline">
<TextBlock Text="U" TextDecorations="Underline"/>
</Button>
<Separator/>
<Button Command="IncreaseFontSize" ToolTip="Increase Font Size">
<Image Source="Icons/IncreaseFontSize.png"/>
</Button>
<Button Command="DecreaseFontSize" ToolTip="Decrease Font Size">
<Image Source="Icons/DecreaseFontSize.png"/>
</Button>
<Separator/>
<Button Command="ToggleBullets" ToolTip="Bullets">
<Image Source="Icons/ToggleBullets.png"/>
</Button>
<Button Command="ToggleNumbering" ToolTip="Numbering">
<Image Source="Icons/ToggleNumbering.png"/>
</Button>
<Separator/>
<Button Command="DecreaseIndentation" ToolTip="Decrease Indentation">
<Image Source="Icons/DecreaseIndentation.png"/>
</Button>
<Button Command="IncreaseIndentation" ToolTip="Increase Indentation">
<Image Source="Icons/IncreaseIndentation.png"/>
</Button>
<Separator/>
<Button Command="AlignLeft" ToolTip="Align Left">
<Image Source="Icons/AlignLeft.png"/>
</Button>
<Button Command="AlignCenter" ToolTip="Align Center">
<Image Source="Icons/AlignCenter.png"/>
</Button>
<Button Command="AlignRight" ToolTip="Align Right">
<Image Source="Icons/AlignRight.png"/>
</Button>
<Button Command="AlignJustify" ToolTip="Align Justify">
<Image Source="Icons/AlignJustify.png"/>
</Button>
</ToolBar>
</ToolBarTray>
<RichTextBox x:Name="richTextBox" AcceptsTab="True" VerticalScrollBarVisibility="Auto"/>
</DockPanel>
</Window>
using GemBox.Document;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
namespace WpfRichTextEditor
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// If using the Professional version, put your serial key below.
ComponentInfo.SetLicense("FREE-LIMITED-KEY");
}
private void Open(object sender, ExecutedRoutedEventArgs e)
{
var dialog = new OpenFileDialog()
{
AddExtension = true,
Filter =
"All Documents (*.docx;*.docm;*.doc;*.dotx;*.dotm;*.dot;*.htm;*.html;*.rtf;*.xml;*.txt)|*.docx;*.docm;*.dotx;*.dotm;*.doc;*.dot;*.htm;*.html;*.rtf;*.xml;*.txt|" +
"Word Documents (*.docx)|*.docx|" +
"Word Macro-Enabled Documents (*.docm)|*.docm|" +
"Word 97-2003 Documents (*.doc)|*.doc|" +
"Word Templates (*.dotx)|*.dotx|" +
"Word Macro-Enabled Templates (*.dotm)|*.dotm|" +
"Word 97-2003 Templates (*.dot)|*.dot|" +
"Web Pages (*.htm;*.html)|*.htm;*.html|" +
"Rich Text Format (*.rtf)|*.rtf|" +
"Flat OPC (*.xml)|*.xml|" +
"Plain Text (*.txt)|*.txt"
};
if (dialog.ShowDialog() == true)
using (var stream = new MemoryStream())
{
// Convert input file to RTF stream.
DocumentModel.Load(dialog.FileName).Save(stream, SaveOptions.RtfDefault);
stream.Position = 0;
// Load RTF stream into RichTextBox.
var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
textRange.Load(stream, DataFormats.Rtf);
}
}
private void Save(object sender, ExecutedRoutedEventArgs e)
{
var dialog = new SaveFileDialog()
{
AddExtension = true,
Filter =
"Word Document (*.docx)|*.docx|" +
"Word Macro-Enabled Document (*.docm)|*.docm|" +
"Word Template (*.dotx)|*.dotx|" +
"Word Macro-Enabled Template (*.dotm)|*.dotm|" +
"PDF (*.pdf)|*.pdf|" +
"XPS Document (*.xps)|*.xps|" +
"Web Page (*.htm;*.html)|*.htm;*.html|" +
"Single File Web Page (*.mht;*.mhtml)|*.mht;*.mhtml|" +
"Rich Text Format (*.rtf)|*.rtf|" +
"Flat OPC (*.xml)|*.xml|" +
"Plain Text (*.txt)|*.txt|" +
"Image (*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp,*.svg)|*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp;*.svg"
};
if (dialog.ShowDialog(this) == true)
using (var stream = new MemoryStream())
{
// Save RichTextBox content to RTF stream.
var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
textRange.Save(stream, DataFormats.Rtf);
stream.Position = 0;
// Convert RTF stream to output format.
DocumentModel.Load(stream, LoadOptions.RtfDefault).Save(dialog.FileName);
Process.Start(dialog.FileName);
}
}
private void Cut(object sender, ExecutedRoutedEventArgs e)
{
this.Copy(sender, e);
// Clear selection.
this.richTextBox.Selection.Text = string.Empty;
}
private void Copy(object sender, ExecutedRoutedEventArgs e)
{
using (var stream = new MemoryStream())
{
// Save RichTextBox selection to RTF stream.
this.richTextBox.Selection.Save(stream, DataFormats.Rtf);
stream.Position = 0;
// Save RTF stream to clipboard.
DocumentModel.Load(stream, LoadOptions.RtfDefault).Content.SaveToClipboard();
}
}
private void Paste(object sender, ExecutedRoutedEventArgs e)
{
using (var stream = new MemoryStream())
{
// Save RichTextBox content to RTF stream.
var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
textRange.Save(stream, DataFormats.Rtf);
stream.Position = 0;
// Load document from RTF stream and prepend or append clipboard content to it.
var document = DocumentModel.Load(stream, LoadOptions.RtfDefault);
var position = (string)e.Parameter == "prepend" ? document.Content.Start : document.Content.End;
position.LoadFromClipboard();
stream.Position = 0;
// Save document to RTF stream.
document.Save(stream, SaveOptions.RtfDefault);
stream.Position = 0;
// Load RTF stream into RichTextBox.
textRange.Load(stream, DataFormats.Rtf);
}
}
private void CanSave(object sender, CanExecuteRoutedEventArgs e)
{
if (this.richTextBox != null)
{
var document = this.richTextBox.Document;
var startPosition = document.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward);
var endPosition = document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward);
e.CanExecute = startPosition != null && endPosition != null && startPosition.CompareTo(endPosition) < 0;
}
else
e.CanExecute = false;
}
private void CanCut(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.richTextBox != null && !this.richTextBox.Selection.IsEmpty;
}
private void CanCopy(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.richTextBox != null && !this.richTextBox.Selection.IsEmpty;
}
private void CanPaste(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.richTextBox != null && this.richTextBox.IsKeyboardFocused;
}
}
}
Imports GemBox.Document
Imports Microsoft.Win32
Imports System.Diagnostics
Imports System.IO
Imports System.Windows
Imports System.Windows.Documents
Imports System.Windows.Input
Namespace WpfRichTextEditor
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
' If using the Professional version, put your serial key below.
ComponentInfo.SetLicense("FREE-LIMITED-KEY")
End Sub
Private Sub Open(sender As Object, e As ExecutedRoutedEventArgs)
Dim dialog = New OpenFileDialog() With {
.AddExtension = True,
.Filter =
"All Documents (*.docx;*.docm;*.doc;*.dotx;*.dotm;*.dot;*.htm;*.html;*.rtf;*.xml;*.txt)|*.docx;*.docm;*.dotx;*.dotm;*.doc;*.dot;*.htm;*.html;*.rtf;*.xml;*.txt|" &
"Word Documents (*.docx)|*.docx|" &
"Word Macro-Enabled Documents (*.docm)|*.docm|" &
"Word 97-2003 Documents (*.doc)|*.doc|" &
"Word Templates (*.dotx)|*.dotx|" &
"Word Macro-Enabled Templates (*.dotm)|*.dotm|" &
"Word 97-2003 Templates (*.dot)|*.dot|" &
"Web Pages (*.htm;*.html)|*.htm;*.html|" &
"Rich Text Format (*.rtf)|*.rtf|" &
"Flat OPC (*.xml)|*.xml|" &
"Plain Text (*.txt)|*.txt"
}
If dialog.ShowDialog() = True Then
Using stream = New MemoryStream()
' Convert input file to RTF stream.
DocumentModel.Load(dialog.FileName).Save(stream, SaveOptions.RtfDefault)
stream.Position = 0
' Load RTF stream into RichTextBox.
Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
textRange.Load(stream, DataFormats.Rtf)
End Using
End If
End Sub
Private Sub Save(sender As Object, e As ExecutedRoutedEventArgs)
Dim dialog = New SaveFileDialog() With {
.AddExtension = True,
.Filter =
"Word Document (*.docx)|*.docx|" &
"Word Macro-Enabled Document (*.docm)|*.docm|" &
"Word Template (*.dotx)|*.dotx|" +
"Word Macro-Enabled Template (*.dotm)|*.dotm|" &
"PDF (*.pdf)|*.pdf|" &
"XPS Document (*.xps)|*.xps|" &
"Web Page (*.htm;*.html)|*.htm;*.html|" &
"Single File Web Page (*.mht;*.mhtml)|*.mht;*.mhtml|" &
"Rich Text Format (*.rtf)|*.rtf|" &
"Flat OPC (*.xml)|*.xml|" &
"Plain Text (*.txt)|*.txt|" &
"Image (*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp,*.svg)|*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp;*.svg"
}
If dialog.ShowDialog(Me) = True Then
Using stream = New MemoryStream()
' Save RichTextBox content to RTF stream.
Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
textRange.Save(stream, DataFormats.Rtf)
stream.Position = 0
' Convert RTF stream to output format.
DocumentModel.Load(stream, LoadOptions.RtfDefault).Save(dialog.FileName)
Process.Start(dialog.FileName)
End Using
End If
End Sub
Private Sub Cut(sender As Object, e As ExecutedRoutedEventArgs)
Me.Copy(sender, e)
' Clear selection.
Me.richTextBox.Selection.Text = String.Empty
End Sub
Private Sub Copy(sender As Object, e As ExecutedRoutedEventArgs)
Using stream = New MemoryStream()
' Save RichTextBox selection to RTF stream.
Me.richTextBox.Selection.Save(stream, DataFormats.Rtf)
stream.Position = 0
' Save RTF stream to clipboard.
DocumentModel.Load(stream, LoadOptions.RtfDefault).Content.SaveToClipboard()
End Using
End Sub
Private Sub Paste(sender As Object, e As ExecutedRoutedEventArgs)
Using stream = New MemoryStream()
' Save RichTextBox content to RTF stream.
Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
textRange.Save(stream, DataFormats.Rtf)
stream.Position = 0
' Load document from RTF stream and prepend or append clipboard content to it.
Dim document = DocumentModel.Load(stream, LoadOptions.RtfDefault)
Dim position = If(DirectCast(e.Parameter, String) = "prepend", document.Content.Start, document.Content.End)
position.LoadFromClipboard()
stream.Position = 0
' Save document to RTF stream.
document.Save(stream, SaveOptions.RtfDefault)
stream.Position = 0
' Load RTF stream into RichTextBox.
textRange.Load(stream, DataFormats.Rtf)
End Using
End Sub
Private Sub CanSave(sender As Object, e As CanExecuteRoutedEventArgs)
If Me.richTextBox IsNot Nothing Then
Dim document = Me.richTextBox.Document
Dim startPosition = document.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward)
Dim endPosition = document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward)
e.CanExecute = startPosition IsNot Nothing AndAlso endPosition IsNot Nothing AndAlso startPosition.CompareTo(endPosition) < 0
Else
e.CanExecute = False
End If
End Sub
Private Sub CanCut(sender As Object, e As CanExecuteRoutedEventArgs)
e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Not Me.richTextBox.Selection.IsEmpty
End Sub
Private Sub CanCopy(sender As Object, e As CanExecuteRoutedEventArgs)
e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Not Me.richTextBox.Selection.IsEmpty
End Sub
Private Sub CanPaste(sender As Object, e As CanExecuteRoutedEventArgs)
e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Me.richTextBox.IsKeyboardFocused
End Sub
End Class
End Namespace