Kamlesh
Kamlesh

Reputation: 386

how to get specific colored words in richtextbox using c#.net

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

Answers (1)

dbc
dbc

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

Related Questions