开发者

WPF RichTextbox remove Foreground information from TextRange

sorry开发者_Go百科 for my bad english... The default for a RichTextBox content is to inherit the Foreground color from the RichTextBox itself. That's nice, but if I set a specific Foreground color to some part of my text, that part does not inherit the Foreground anymore, obviously. How can I make my "colored" text inherit the Foreground again? I'm trying to do something like the "Automatic" color from Office Word but after I have set a specific color to a TextRange, I do not know how to unset it :/

TextRange.ClearAllProperties() does what I need, but also erases other properties like FontSize and FontFamily...

TextRange.ApplyPropertyValue(ForegroundProperty, DependencyProperty.UnsetValue) also does not do the trick...


You can also unset it by setting the property to null (this worked for me clearing out the background, for example removing highlighting)

TextRange.ApplyPropertyValue(TextElement.BackgroundProperty, null);


This seemed almost impossible to achieve since there is no "RemovePropertyValue" method. I also tried with span and got the same exception as you did so I made a method that collects all the Paragraphs within the TextRange and made a span for each separetly.. less than ideal, I know.. Anyway, it works for a small example but might be pretty hard to work with for something more complex.

private List<Span> m_spanList = new List<Span>();

private void c_setForegroundButton_Click(object sender, RoutedEventArgs e)
{
    TextPointer textPointerStart = c_richTextBox1.Selection.Start;
    TextPointer textPointerEnd = c_richTextBox1.Selection.End;
    TextRange textRange = new TextRange(textPointerStart, textPointerEnd);
    SetForeground(textRange);
}

private void c_clearForegroundButton_Click(object sender, RoutedEventArgs e)
{
    foreach (Span span in m_spanList)
    {
        span.ClearValue(Span.ForegroundProperty);
    }
}

public void SetForeground(TextRange textRange)
{
    List<Paragraph> spannedParagraphs = new List<Paragraph>();
    if (textRange.Start.Paragraph != null)
    {
        TextRange curRange = null;
        Block cur = textRange.Start.Paragraph;
        do
        {
            spannedParagraphs.Add(cur as Paragraph);
            // Get next range
            curRange = new TextRange(cur.ContentStart, cur.ContentEnd);
        } while ((textRange.End.Paragraph == null || !curRange.Contains(textRange.End.Paragraph.ContentEnd)) && (cur = cur.NextBlock) != null);
    }

    if (spannedParagraphs.Count == 1)
    {
        Span span = new Span(c_richTextBox1.Selection.Start, c_richTextBox1.Selection.End);
        span.Foreground = Brushes.Red;
        m_spanList.Add(span);
    }
    else
    {
        for (int i = 0; i < spannedParagraphs.Count; i++)
        {
            if (i == spannedParagraphs.Count - 1)
            {
                Paragraph paragraph = spannedParagraphs[i];
                // For some reason I get an exception here when I try this..
                //m_span = new Span(paragraph.ElementStart, c_richTextBox1.Selection.End);
                c_richTextBox1.Selection.Select(paragraph.ElementStart, c_richTextBox1.Selection.End);
                Span span = new Span(c_richTextBox1.Selection.Start, c_richTextBox1.Selection.End);
                span.Foreground = Brushes.Red;
                m_spanList.Add(span);
            }
            else if (i == 0)
            {
                Paragraph paragraph = spannedParagraphs[i];
                Span span = new Span(c_richTextBox1.Selection.Start, paragraph.ElementEnd);
                span.Foreground = Brushes.Red;
                m_spanList.Add(span);
            }
            else
            {
                Paragraph paragraph = spannedParagraphs[i];
                Span span = new Span(paragraph.ElementStart, paragraph.ElementEnd);
                span.Foreground = Brushes.Red;
                m_spanList.Add(span);
            }
        }
    }
}


If you look at the code of method TextRange.ApplyPropertyValue in the .NET Reference Source, you'll see that in the end it calls DependencyObject.SetValue on a collection of Inlines and Blocks, and DependencyObject.SetValue treats the value DependencyProperty.UnsetValue specially by effectively clearing the local value for the property.

The problem is that they didn't think of that when implementing TextRange.ApplyPropertyValue: it checks the passed property value against the property type, and in case of a reference type, it makes sure the passed value is either null or inherits from the same class, thus preventing us from passing DependencyProperty.UnsetValue.

One solution I found to implement a way of clearing local values of a TextRange for dependency properties of a reference type is the following:

// We declare a marker brush to be detected later in the TextRange.
var markerBrush = new SolidColorBrush();

// First we ask the TextRange implementation to set our marker brush on its content.
// Using ApplyPropertyValue here takes care of splitting inlines when necessary to make
// sure that only the selected text gets affected.
range.ApplyPropertyValue(TextElement.ForegroundProperty, markerBrush);

// Now, we search the text range for every Inline that has our brush set as the foreground
// brush, and we clear the Foreground dependency property.
var position = range.Start;
while (position != null && range.Contains(position))
{
    if (position.GetPointerContext(LogicalDirection.Backward) == TextPointerContext.ElementStart &&
        position.Parent is Inline inline &&
        inline.ReadLocalValue(TextElement.ForegroundProperty) == _foregroundClearBrush)
        inline.ClearValue(TextElement.ForegroundProperty);
    position = position.GetNextContextPosition(LogicalDirection.Forward);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜