WPF RichTextbox 从 TextRange 中删除前景信息

发布于 2024-09-27 22:26:25 字数 518 浏览 0 评论 0原文

抱歉我的英语不好... RichTextBox 内容的默认设置是从 RichTextBox 本身继承前景色。这很好,但如果我为文本的某些部分设置特定的前景颜色,那么显然该部分不再继承前景。如何让我的“彩色”文本再次继承 Foreground ?我正在尝试执行类似于 Office Word 中的“自动”颜色的操作,但在为 TextRange 设置特定颜色后,我不知道如何取消设置它:/

TextRange. ClearAllProperties() 可以满足我的需要,但也会删除其他属性,例如 FontSizeFontFamily...

TextRange.ApplyPropertyValue(ForegroundProperty, DependencyProperty.UnsetValue ) 也没有达到目的...

sorry 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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

药祭#氼 2024-10-04 22:26:25

您还可以通过将属性设置为 null 来取消设置(这对我清除背景很有用,例如删除突出显示)

TextRange.ApplyPropertyValue(TextElement.BackgroundProperty, null);

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);

木落 2024-10-04 22:26:25

这似乎几乎不可能实现,因为没有“RemovePropertyValue”方法。我也尝试过使用跨度,并得到了与您相同的异常,所以我创建了一种方法来收集 TextRange 内的所有段落,并为每个段落分别创建一个跨度.. 不太理想,我知道.. 无论如何,它适用于一个小的例如,但对于更复杂的东西来说可能很难使用。

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);
            }
        }
    }
}

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);
            }
        }
    }
}
执笔绘流年 2024-10-04 22:26:25

如果您查看 .NET 参考源中的方法 TextRange.ApplyPropertyValue 的代码,您会发现最终它对 DependencyObject.SetValue 的集合调用 DependencyObject.SetValue 。 code>Inline 和 Block 以及 DependencyObject.SetValue 通过有效清除本地值来特殊对待 DependencyProperty.UnsetValue 值为了财产。

问题是他们在实现 TextRange.ApplyPropertyValue 时没有想到这一点:它根据属性类型检查传递的属性值,并且在引用类型的情况下,它确保传递的值是null 或从同一个类继承,从而阻止我们传递 DependencyProperty.UnsetValue 。

我发现的一个解决方案用于实现一种清除 引用类型 的依赖属性的 TextRange 本地值的方法,如下所示:

// 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);
}

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文