Reputation: 386
I want to select all the words within richtextbox which has blue color. How can I do this? In richtextbox, there are some keywords of blue color. I want to get all these keywords as a collection.
Upvotes: 0
Views: 219
Reputation: 117293
By "select", I assume you mean "find". I don't believe you can actually select multiple discontinuous ranges of text in a RichTextBox.
Assuming my understanding is correct, here's some moderately-tested code I've been working on recently. Let me know how it works for you.
Be aware that all the textual content in a RichTextBox
is actually stored in a FlowDocument
, accessed via the Document
property. To iterate through the strings you need to walk hierarchy of TextElement
classes in theFlowDocument
. The following does so, returning each string and a stack representing the hierarchy, possibly transformed by a selector method:
public static IEnumerable<KeyValuePair<Stack<T>, string>> WalkTextElements<T>(FlowDocument doc, Func<DependencyObject, Stack<T>, T> selector)
{
// Inspiration: http://www.bryanewert.net/journal/2010/5/26/how-to-explore-the-contents-of-a-flowdocument.html
if (doc != null)
{
var stack = new Stack<T>();
// Start with a TextPointer to FlowDocument.ContentStart
TextPointer t = doc.ContentStart;
// Keep a TextPointer for FlowDocument.ContentEnd handy, so we know when we're done.
TextPointer e = doc.ContentEnd;
// Keep going until the TextPointer is equal to or greater than ContentEnd.
while ((t != null) && (t.CompareTo(e) < 0))
{
// Identify the type of content immediately adjacent to the text pointer.
TextPointerContext context = t.GetPointerContext(LogicalDirection.Forward);
// ElementStart is an "opening tag" which defines the structure of the document, e.g. a paragraph declaration.
if (context == TextPointerContext.ElementStart)
{
stack.Push(selector(t.Parent, stack));
}
// An EmbeddedElement, e.g. a UIContainer.
else if (context == TextPointerContext.EmbeddedElement)
{
; // Do nothing.
}
// The document's text content.
else if (context == TextPointerContext.Text)
{
stack.Push(selector(t.Parent, stack));
yield return new KeyValuePair<Stack<T>, string>(stack, t.GetTextInRun(LogicalDirection.Forward));
stack.Pop();
}
// ElementEnd is a "closing tag".
else if (context == TextPointerContext.ElementEnd)
{
stack.Pop();
}
else
{
throw new System.Exception("Unhandled TextPointerContext " + context.ToString());
}
// Advance to the next ContentElement in the FlowDocument.
t = t.GetNextContextPosition(LogicalDirection.Forward);
}
}
}
With this, we can enumerate the strings with background color explicitly overridden:
/// <summary>
/// Enumerate all the strings in a given flow document that are have an explicit background color.
/// </summary>
/// <param name="doc"></param>
/// <param name="includeFlowDocumentColor">true to consider overrides on the entire FlowDocument itself, else false.</param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<Brush, string>> WalkBackgroundColoredTexts(FlowDocument doc, bool includeFlowDocumentColor)
{
foreach (var pair in WalkTextElements<Brush>(doc, (d, s) => SelectTextBackgroundBrush(d, s, includeFlowDocumentColor)))
{
var brush = pair.Key.Peek();
if (brush != null)
{
yield return new KeyValuePair<Brush, string>(brush, pair.Value);
}
}
}
static Brush SelectTextBackgroundBrush(DependencyObject element, Stack<Brush> brushes, bool includeFlowDocumentColor)
{
//http://blogs.msdn.com/b/prajakta/archive/2006/10/11/flowdocument-content-model.aspx
//http://msdn.microsoft.com/en-us/library/aa970786%28v=vs.110%29.aspx
var textElement = element as TextElement;
if (textElement != null)
{
var brush = textElement.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
var tableColumn = element as TableColumn;
if (tableColumn != null)
{
var brush = tableColumn.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
if (includeFlowDocumentColor)
{
var doc = element as FlowDocument;
if (doc != null)
{
var brush = doc.Background;
if (brush != null)
return brush;
return PeekOrDefault(brushes);
}
}
return null;
}
static T PeekOrDefault<T>(Stack<T> stack)
{
return (stack.Count == 0 ? default(T) : stack.Peek());
}
You probably want to ignore background colors set on the flow document itself and get only specific text runs with background color set, which is why I added the argument.
Given the strings, you may still need to tokenize them into words.
Upvotes: 1