added SSCLI 1.0
[windows-sources.git] / sdk / samples / WPFSamples / CustomRichTextBox / csharp / myrichtextbox.cs
blob798cf0cf69b9f7c48d4a4de6c164be76ccecd191
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Windows;
5 using System.Windows.Controls;
6 using System.Windows.Controls.Primitives;
7 using System.Windows.Documents;
8 using System.Windows.Input;
9 using System.Windows.Media;
11 namespace CustomRichTextBoxSample
13 /// <summary>
14 /// An extension of RichTextBox that allows only plain text input.
15 /// This class auto-formats words in the document using a dictionary lookup.
16 /// <remarks>
17 /// One of the applications of such a class can be a code editor.
18 /// Syntax highlight for keywords can be implemented using this approach.
19 /// </remarks>
20 /// </summary>
21 public class MyRichTextBox : RichTextBox
23 //------------------------------------------------------
25 // Constructors
27 //------------------------------------------------------
29 // Ctor.
30 static MyRichTextBox()
32 RegisterCommandHandlers();
34 _emailNamesDictionary = new Dictionary<string, string>();
35 _emailNamesDictionary.Add("alias1", "Emailname 1");
36 _emailNamesDictionary.Add("alias2", "Emailname 2");
37 _emailNamesDictionary.Add("alias3", "Emailname 3");
40 static void RegisterCommandHandlers()
42 // Register command handlers for all rich text formatting commands.
43 // We disable all commands by returning false in OnCanExecute event handler,
44 // thus making this control a "plain text only" RichTextBox.
45 foreach (RoutedUICommand command in _formattingCommands)
47 CommandManager.RegisterClassCommandBinding(typeof(MyRichTextBox),
48 new CommandBinding(command, new ExecutedRoutedEventHandler(OnFormattingCommand),
49 new CanExecuteRoutedEventHandler(OnCanExecuteFormattingCommand)));
52 // Command handlers for Cut, Copy and Paste commands.
53 // To enforce that data can be copied or pasted from the clipboard in text format only.
54 CommandManager.RegisterClassCommandBinding(typeof(MyRichTextBox),
55 new CommandBinding(ApplicationCommands.Copy, new ExecutedRoutedEventHandler(OnCopy),
56 new CanExecuteRoutedEventHandler(OnCanExecuteCopy)));
57 CommandManager.RegisterClassCommandBinding(typeof(MyRichTextBox),
58 new CommandBinding(ApplicationCommands.Paste, new ExecutedRoutedEventHandler(OnPaste),
59 new CanExecuteRoutedEventHandler(OnCanExecutePaste)));
60 CommandManager.RegisterClassCommandBinding(typeof(MyRichTextBox),
61 new CommandBinding(ApplicationCommands.Cut, new ExecutedRoutedEventHandler(OnCut),
62 new CanExecuteRoutedEventHandler(OnCanExecuteCut)));
65 // Ctor.
66 public MyRichTextBox() : base()
68 this._words = new List<Word>();
69 this.TextChanged += this.TextChangedEventHandler;
71 MyContextMenu myContextMenu = new MyContextMenu(this);
72 myContextMenu.Placement = PlacementMode.RelativePoint;
73 myContextMenu.PlacementTarget = this;
75 this.ContextMenu = myContextMenu;
78 //------------------------------------------------------
80 // Public Properties
82 //------------------------------------------------------
84 #region Public Properties
86 /// <summary>
87 /// Dictionary of email names which are auto-formatted by this RichTextBox.
88 /// </summary>
89 public Dictionary<string, string> EmailNamesDictionary
91 get
93 return _emailNamesDictionary;
97 #endregion
99 //------------------------------------------------------
101 // Event Handlers
103 //------------------------------------------------------
105 #region Event Handlers
107 /// <summary>
108 /// Event handler for all formatting commands.
109 /// </summary>
110 private static void OnFormattingCommand(object sender, ExecutedRoutedEventArgs e)
112 // Do nothing, and set command handled to true.
113 e.Handled = true;
116 /// <summary>
117 /// Event handler for ApplicationCommands.Copy command.
118 /// <remarks>
119 /// We want to enforce that data can be set on the clipboard
120 /// only in plain text format from this RichTextBox.
121 /// </remarks>
122 /// </summary>
123 private static void OnCopy(object sender, ExecutedRoutedEventArgs e)
125 MyRichTextBox myRichTextBox = (MyRichTextBox)sender;
126 string selectionText = myRichTextBox.Selection.Text;
127 Clipboard.SetText(selectionText);
128 e.Handled = true;
131 /// <summary>
132 /// Event handler for ApplicationCommands.Cut command.
133 /// <remarks>
134 /// We want to enforce that data can be set on the clipboard
135 /// only in plain text format from this RichTextBox.
136 /// </remarks>
137 /// </summary>
138 private static void OnCut(object sender, ExecutedRoutedEventArgs e)
140 MyRichTextBox myRichTextBox = (MyRichTextBox)sender;
141 string selectionText = myRichTextBox.Selection.Text;
142 myRichTextBox.Selection.Text = String.Empty;
143 Clipboard.SetText(selectionText);
144 e.Handled = true;
147 /// <summary>
148 /// Event handler for ApplicationCommands.Paste command.
149 /// <remarks>
150 /// We want to allow paste only in plain text format.
151 /// </remarks>
152 /// </summary>
153 private static void OnPaste(object sender, ExecutedRoutedEventArgs e)
155 MyRichTextBox myRichTextBox = (MyRichTextBox)sender;
157 // Handle paste only if clipboard supports text format.
158 if (Clipboard.ContainsText())
160 myRichTextBox.Selection.Text = Clipboard.GetText();
162 e.Handled = true;
165 /// <summary>
166 /// CanExecute event handler.
167 /// </summary>
168 private static void OnCanExecuteFormattingCommand(object target, CanExecuteRoutedEventArgs args)
170 args.CanExecute = true;
173 /// <summary>
174 /// CanExecute event handler for ApplicationCommands.Copy.
175 /// </summary>
176 private static void OnCanExecuteCopy(object target, CanExecuteRoutedEventArgs args)
178 MyRichTextBox myRichTextBox = (MyRichTextBox)target;
179 args.CanExecute = myRichTextBox.IsEnabled && !myRichTextBox.Selection.IsEmpty;
182 /// <summary>
183 /// CanExecute event handler for ApplicationCommands.Cut.
184 /// </summary>
185 private static void OnCanExecuteCut(object target, CanExecuteRoutedEventArgs args)
187 MyRichTextBox myRichTextBox = (MyRichTextBox)target;
188 args.CanExecute = myRichTextBox.IsEnabled && !myRichTextBox.IsReadOnly && !myRichTextBox.Selection.IsEmpty;
191 /// <summary>
192 /// CanExecute event handler for ApplicationCommand.Paste.
193 /// </summary>
194 private static void OnCanExecutePaste(object target, CanExecuteRoutedEventArgs args)
196 MyRichTextBox myRichTextBox = (MyRichTextBox)target;
197 args.CanExecute = myRichTextBox.IsEnabled && !myRichTextBox.IsReadOnly && Clipboard.ContainsText();
200 /// <summary>
201 /// Event handler for RichTextBox.TextChanged event.
202 /// </summary>
203 private void TextChangedEventHandler(object sender, TextChangedEventArgs e)
205 // Clear all formatting properties in the document.
206 // This is necessary since a paste command could have inserted text inside or at boundaries of a keyword from dictionary.
207 TextRange documentRange = new TextRange(this.Document.ContentStart, this.Document.ContentEnd);
208 documentRange.ClearAllProperties();
210 // Reparse the document to scan for matching words.
211 TextPointer navigator = this.Document.ContentStart;
212 while (navigator.CompareTo(this.Document.ContentEnd) < 0)
214 TextPointerContext context = navigator.GetPointerContext(LogicalDirection.Backward);
215 if (context == TextPointerContext.ElementStart && navigator.Parent is Run)
217 this.AddMatchingWordsInRun((Run)navigator.Parent);
219 navigator = navigator.GetNextContextPosition(LogicalDirection.Forward);
222 // Format words found.
223 this.FormatWords();
226 #endregion
228 //------------------------------------------------------
230 // Private Methods
232 //------------------------------------------------------
234 #region Private Methods
236 /// <summary>
237 /// Helper to apply formatting properties to matching words in the document.
238 /// </summary>
239 private void FormatWords()
241 // Applying formatting properties, triggers another TextChangedEvent. Remove event handler temporarily.
242 this.TextChanged -= this.TextChangedEventHandler;
244 // Add formatting for matching words.
245 foreach (Word word in _words)
247 TextRange range = new TextRange(word.Start, word.End);
248 range.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.Blue));
249 range.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
251 _words.Clear();
253 // Add TextChanged handler back.
254 this.TextChanged += this.TextChangedEventHandler;
257 /// <summary>
258 /// Scans passed Run's text, for any matching words from dictionary.
259 /// </summary>
260 private void AddMatchingWordsInRun(Run run)
262 string runText = run.Text;
264 int wordStartIndex = 0;
265 int wordEndIndex = 0;
266 for (int i = 0; i < runText.Length; i++)
268 if (Char.IsWhiteSpace(runText[i]))
270 if (i > 0 && !Char.IsWhiteSpace(runText[i - 1]))
272 wordEndIndex = i - 1;
273 string wordInRun = runText.Substring(wordStartIndex, wordEndIndex - wordStartIndex + 1);
275 if (_emailNamesDictionary.ContainsKey(wordInRun))
277 TextPointer wordStart = run.ContentStart.GetPositionAtOffset(wordStartIndex, LogicalDirection.Forward);
278 TextPointer wordEnd = run.ContentStart.GetPositionAtOffset(wordEndIndex + 1, LogicalDirection.Backward);
279 _words.Add(new Word(wordStart, wordEnd));
282 wordStartIndex = i + 1;
286 // Check if the last word in the Run is a matching word.
287 string lastWordInRun = runText.Substring(wordStartIndex, runText.Length - wordStartIndex);
288 if (_emailNamesDictionary.ContainsKey(lastWordInRun))
290 TextPointer wordStart = run.ContentStart.GetPositionAtOffset(wordStartIndex, LogicalDirection.Forward);
291 TextPointer wordEnd = run.ContentStart.GetPositionAtOffset(runText.Length, LogicalDirection.Backward);
292 _words.Add(new Word(wordStart, wordEnd));
296 #endregion
298 //------------------------------------------------------
300 // Private Types
302 //------------------------------------------------------
304 #region Private Types
306 /// <summary>
307 /// This class encapsulates a matching word by two TextPointer positions,
308 /// start and end, with forward and backward gravities respectively.
309 /// </summary>
310 private class Word
312 public Word(TextPointer wordStart, TextPointer wordEnd)
314 _wordStart = wordStart.GetPositionAtOffset(0, LogicalDirection.Forward);
315 _wordEnd = wordEnd.GetPositionAtOffset(0, LogicalDirection.Backward);
318 public TextPointer Start
322 return _wordStart;
326 public TextPointer End
330 return _wordEnd;
334 private readonly TextPointer _wordStart;
335 private readonly TextPointer _wordEnd;
338 #endregion
340 //------------------------------------------------------
342 // Private Members
344 //------------------------------------------------------
346 #region Private Members
348 // Static member for email names dictionary.
349 private static readonly Dictionary<string, string> _emailNamesDictionary;
351 // Static list of editing formatting commands. In the ctor we disable all these commands.
352 private static readonly RoutedUICommand[] _formattingCommands = new RoutedUICommand[]
354 EditingCommands.ToggleBold,
355 EditingCommands.ToggleItalic,
356 EditingCommands.ToggleUnderline,
357 EditingCommands.ToggleSubscript,
358 EditingCommands.ToggleSuperscript,
359 EditingCommands.IncreaseFontSize,
360 EditingCommands.DecreaseFontSize,
361 EditingCommands.ToggleBullets,
362 EditingCommands.ToggleNumbering,
365 // List of matching words found in the document.
366 private List<Word> _words;
368 #endregion Private Members