Word Editor in WPF

Following sample shows interoperability between GemBox.Document component and WPF RichTextBox control via RTF format.

Sample also implements common rich text editor functionalities and shows how to copy and paste between clipboard and GemBox.Document.

Visual Studio solution can be downloaded from: WpfRichTextEditor.zip.

Screenshot

RichTextBox / Clipboard Screenshot

See the full code below.

<Window x:Class="WpfRichTextEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Rich Text 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>
1using System.Diagnostics;
2using System.IO;
3using System.Windows;
4using System.Windows.Documents;
5using System.Windows.Input;
6using GemBox.Document;
7using Microsoft.Win32;
8
9namespace WpfRichTextEditor
10{
11    public partial class MainWindow : Window
12    {
13        public MainWindow()
14        {
15            InitializeComponent();
16
17            ComponentInfo.SetLicense("FREE-LIMITED-KEY");
18        }
19
20        private void Open(object sender, ExecutedRoutedEventArgs e)
21        {
22            var dialog = new OpenFileDialog()
23            {
24                AddExtension = true,
25                Filter =
26                    "All Documents (*.docx;*.docm;*.doc;*.dotx;*.dotm;*.dot;*.htm;*.html;*.rtf;*.txt)|*.docx;*.docm;*.dotx;*.dotm;*.doc;*.dot;*.htm;*.html;*.rtf;*.txt|" +
27                    "Word Documents (*.docx)|*.docx|" +
28                    "Word Macro-Enabled Documents (*.docm)|*.docm|" +
29                    "Word 97-2003 Documents (*.doc)|*.doc|" +
30                    "Word Templates (*.dotx)|*.dotx|" +
31                    "Word Macro-Enabled Templates (*.dotm)|*.dotm|" +
32                    "Word 97-2003 Templates (*.dot)|*.dot|" +
33                    "Web Pages (*.htm;*.html)|*.htm;*.html|" +
34                    "Rich Text Format (*.rtf)|*.rtf|" +
35                    "Text Files (*.txt)|*.txt"
36            };
37
38            if (dialog.ShowDialog() == true)
39                using (var stream = new MemoryStream())
40                {
41                    // Convert input file to RTF stream.
42                    DocumentModel.Load(dialog.FileName).Save(stream, SaveOptions.RtfDefault);
43
44                    stream.Position = 0;
45
46                    // Load RTF stream into RichTextBox.
47                    var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
48                    textRange.Load(stream, DataFormats.Rtf);
49                }
50        }
51
52        private void Save(object sender, ExecutedRoutedEventArgs e)
53        {
54            var dialog = new SaveFileDialog()
55            {
56                AddExtension = true,
57                Filter =
58                    "Word Document (*.docx)|*.docx|" +
59                    "Word Macro-Enabled Document (*.docm)|*.docm|" +
60                    "Word Template (*.dotx)|*.dotx|" +
61                    "Word Macro-Enabled Template (*.dotm)|*.dotm|" +
62                    "PDF (*.pdf)|*.pdf|" +
63                    "XPS Document (*.xps)|*.xps|" +
64                    "Web Page (*.htm;*.html)|*.htm;*.html|" +
65                    "Single File Web Page (*.mht;*.mhtml)|*.mht;*.mhtml|" +
66                    "Rich Text Format (*.rtf)|*.rtf|" +
67                    "Plain Text (*.txt)|*.txt|" +
68                    "Image (*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp)|*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp"
69            };
70
71            if (dialog.ShowDialog(this) == true)
72                using (var stream = new MemoryStream())
73                {
74                    // Save RichTextBox content to RTF stream.
75                    var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
76                    textRange.Save(stream, DataFormats.Rtf);
77
78                    stream.Position = 0;
79
80                    // Convert RTF stream to output format.
81                    DocumentModel.Load(stream, LoadOptions.RtfDefault).Save(dialog.FileName);
82                    Process.Start(dialog.FileName);
83                }
84        }
85
86        private void Cut(object sender, ExecutedRoutedEventArgs e)
87        {
88            this.Copy(sender, e);
89
90            // Clear selection.
91            this.richTextBox.Selection.Text = string.Empty;
92        }
93
94        private void Copy(object sender, ExecutedRoutedEventArgs e)
95        {
96            using (var stream = new MemoryStream())
97            {
98                // Save RichTextBox selection to RTF stream.
99                this.richTextBox.Selection.Save(stream, DataFormats.Rtf);
100
101                stream.Position = 0;
102
103                // Save RTF stream to clipboard.
104                DocumentModel.Load(stream, LoadOptions.RtfDefault).Content.SaveToClipboard();
105            }
106        }
107
108        private void Paste(object sender, ExecutedRoutedEventArgs e)
109        {
110            using (var stream = new MemoryStream())
111            {
112                // Save RichTextBox content to RTF stream.
113                var textRange = new TextRange(this.richTextBox.Document.ContentStart, this.richTextBox.Document.ContentEnd);
114                textRange.Save(stream, DataFormats.Rtf);
115
116                stream.Position = 0;
117
118                // Load document from RTF stream and prepend or append clipboard content to it.
119                var document = DocumentModel.Load(stream, LoadOptions.RtfDefault);
120                var position = (string)e.Parameter == "prepend" ? document.Content.Start : document.Content.End;
121                position.LoadFromClipboard();
122
123                stream.Position = 0;
124
125                // Save document to RTF stream.
126                document.Save(stream, SaveOptions.RtfDefault);
127
128                stream.Position = 0;
129
130                // Load RTF stream into RichTextBox.
131                textRange.Load(stream, DataFormats.Rtf);
132            }
133        }
134
135        private void CanSave(object sender, CanExecuteRoutedEventArgs e)
136        {
137            if (this.richTextBox != null)
138            {
139                var document = this.richTextBox.Document;
140                var startPosition = document.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward);
141                var endPosition = document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward);
142                e.CanExecute = startPosition != null && endPosition != null && startPosition.CompareTo(endPosition) < 0;
143            }
144            else
145                e.CanExecute = false;
146        }
147
148        private void CanCut(object sender, CanExecuteRoutedEventArgs e)
149        {
150            e.CanExecute = this.richTextBox != null && !this.richTextBox.Selection.IsEmpty;
151        }
152
153        private void CanCopy(object sender, CanExecuteRoutedEventArgs e)
154        {
155            e.CanExecute = this.richTextBox != null && !this.richTextBox.Selection.IsEmpty;
156        }
157
158        private void CanPaste(object sender, CanExecuteRoutedEventArgs e)
159        {
160            e.CanExecute = this.richTextBox != null && this.richTextBox.IsKeyboardFocused;
161        }
162    }
163}
1Imports System.Diagnostics
2Imports System.IO
3Imports System.Windows
4Imports System.Windows.Documents
5Imports System.Windows.Input
6Imports GemBox.Document
7Imports Microsoft.Win32
8
9Namespace WpfRichTextEditor
10
11    Partial Public Class MainWindow
12        Inherits Window
13
14        Public Sub New()
15
16            InitializeComponent()
17
18            ComponentInfo.SetLicense("FREE-LIMITED-KEY")
19
20        End Sub
21
22        Private Sub Open(sender As Object, e As ExecutedRoutedEventArgs)
23
24            Dim dialog = New OpenFileDialog() With {
25                .AddExtension = True,
26                .Filter =
27                    "All Documents (*.docx;*.docm;*.doc;*.dotx;*.dotm;*.dot;*.htm;*.html;*.rtf;*.txt)|*.docx;*.docm;*.dotx;*.dotm;*.doc;*.dot;*.htm;*.html;*.rtf;*.txt|" +
28                    "Word Documents (*.docx)|*.docx|" +
29                    "Word Macro-Enabled Documents (*.docm)|*.docm|" +
30                    "Word 97-2003 Documents (*.doc)|*.doc|" +
31                    "Word Templates (*.dotx)|*.dotx|" +
32                    "Word Macro-Enabled Templates (*.dotm)|*.dotm|" +
33                    "Word 97-2003 Templates (*.dot)|*.dot|" +
34                    "Web Pages (*.htm;*.html)|*.htm;*.html|" +
35                    "Rich Text Format (*.rtf)|*.rtf|" +
36                    "Text Files (*.txt)|*.txt"
37            }
38
39            If dialog.ShowDialog() = True Then
40                Using stream = New MemoryStream()
41
42                    ' Convert input file to RTF stream.
43                    DocumentModel.Load(dialog.FileName).Save(stream, SaveOptions.RtfDefault)
44
45                    stream.Position = 0
46
47                    ' Load RTF stream into RichTextBox.
48                    Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
49                    textRange.Load(stream, DataFormats.Rtf)
50
51                End Using
52            End If
53
54        End Sub
55
56        Private Sub Save(sender As Object, e As ExecutedRoutedEventArgs)
57
58            Dim dialog = New SaveFileDialog() With {
59                .AddExtension = True,
60                .Filter =
61                    "Word Document (*.docx)|*.docx|" +
62                    "Word Macro-Enabled Document (*.docm)|*.docm|" +
63                    "Word Template (*.dotx)|*.dotx|" +
64                    "Word Macro-Enabled Template (*.dotm)|*.dotm|" +
65                    "PDF (*.pdf)|*.pdf|" +
66                    "XPS Document (*.xps)|*.xps|" +
67                    "Web Page (*.htm;*.html)|*.htm;*.html|" +
68                    "Single File Web Page (*.mht;*.mhtml)|*.mht;*.mhtml|" +
69                    "Rich Text Format (*.rtf)|*.rtf|" + "Plain Text (*.txt)|*.txt|" +
70                    "Image (*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp)|*.png;*.jpg;*.jpeg;*.gif;*.bmp;*.tif;*.tiff;*.wdp"
71            }
72
73            If dialog.ShowDialog(Me) = True Then
74                Using stream = New MemoryStream()
75
76                    ' Save RichTextBox content to RTF stream.
77                    Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
78                    textRange.Save(stream, DataFormats.Rtf)
79
80                    stream.Position = 0
81
82                    ' Convert RTF stream to output format.
83                    DocumentModel.Load(stream, LoadOptions.RtfDefault).Save(dialog.FileName)
84                    Process.Start(dialog.FileName)
85
86                End Using
87            End If
88
89        End Sub
90
91        Private Sub Cut(sender As Object, e As ExecutedRoutedEventArgs)
92
93            Me.Copy(sender, e)
94
95            ' Clear selection.
96            Me.richTextBox.Selection.Text = String.Empty
97
98        End Sub
99
100        Private Sub Copy(sender As Object, e As ExecutedRoutedEventArgs)
101
102            Using stream = New MemoryStream()
103
104                ' Save RichTextBox selection to RTF stream.
105                Me.richTextBox.Selection.Save(stream, DataFormats.Rtf)
106
107                stream.Position = 0
108
109                ' Save RTF stream to clipboard.
110                DocumentModel.Load(stream, LoadOptions.RtfDefault).Content.SaveToClipboard()
111
112            End Using
113
114        End Sub
115
116        Private Sub Paste(sender As Object, e As ExecutedRoutedEventArgs)
117
118            Using stream = New MemoryStream()
119
120                ' Save RichTextBox content to RTF stream.
121                Dim textRange = New TextRange(Me.richTextBox.Document.ContentStart, Me.richTextBox.Document.ContentEnd)
122                textRange.Save(stream, DataFormats.Rtf)
123
124                stream.Position = 0
125
126                ' Load document from RTF stream and prepend or append clipboard content to it.
127                Dim document = DocumentModel.Load(stream, LoadOptions.RtfDefault)
128                Dim position = If(DirectCast(e.Parameter, String) = "prepend", document.Content.Start, document.Content.End)
129                position.LoadFromClipboard()
130
131                stream.Position = 0
132
133                ' Save document to RTF stream.
134                document.Save(stream, SaveOptions.RtfDefault)
135
136                stream.Position = 0
137
138                ' Load RTF stream into RichTextBox.
139                textRange.Load(stream, DataFormats.Rtf)
140
141            End Using
142
143        End Sub
144
145        Private Sub CanSave(sender As Object, e As CanExecuteRoutedEventArgs)
146
147            If Me.richTextBox IsNot Nothing Then
148                Dim document = Me.richTextBox.Document
149                Dim startPosition = document.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward)
150                Dim endPosition = document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward)
151                e.CanExecute = startPosition IsNot Nothing AndAlso endPosition IsNot Nothing AndAlso startPosition.CompareTo(endPosition) < 0
152            Else
153                e.CanExecute = False
154            End If
155
156        End Sub
157
158        Private Sub CanCut(sender As Object, e As CanExecuteRoutedEventArgs)
159
160            e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Not Me.richTextBox.Selection.IsEmpty
161
162        End Sub
163
164        Private Sub CanCopy(sender As Object, e As CanExecuteRoutedEventArgs)
165
166            e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Not Me.richTextBox.Selection.IsEmpty
167
168        End Sub
169
170        Private Sub CanPaste(sender As Object, e As CanExecuteRoutedEventArgs)
171
172            e.CanExecute = Me.richTextBox IsNot Nothing AndAlso Me.richTextBox.IsKeyboardFocused
173
174        End Sub
175
176    End Class
177
178End Namespace

Check next sample or find out more about GemBox.Document and GemBox Software.