如何以编程方式替换WPF RichTextBox的某些内容而不丢失格式?

jaxagkaj  于 2023-10-22  发布在  其他
关注(0)|答案(2)|浏览(111)

问题:我们如何以编程方式替换WPF RichTextBox中的某些文本而不丢失其格式?在下面的代码中,我显然没有做正确的事情。我的在线搜索给出了一些相关的建议,但他们似乎使用Winform,其中RichTextBox具有rtf属性-如this之一。

下面的代码正确地将WPF RichTexBox中的文本abcd替换为rstu,但它会丢失RichTextBox的格式,如下面两张图所示:

//rtbTest is the name of the RichTextBox
TextRange textRange = new TextRange(rtbTest.Document.ContentStart, rtbTest.Document.ContentEnd);
string oldText = textRange.Text;
string newText = oldText.Replace("abcd", "rstu");
textRange.Text = newText;

将abcd替换为rstu之前的RichTextBox截图

将abcd替换为rstu后的RichTextBox截图

正如我们所看到的格式丢失。下面显示的列表并不是一个真正格式化的编号列表,它可能只是未格式化的文本(如1. Item 1等)。

p8h8hvxi

p8h8hvxi1#

它正在丢失格式,因为您将RTF存储到字符串中,而字符串不保持RTF格式。
您可以按以下方式保存它,

TextRange textRange = new TextRange(rtbTest.Document.ContentStart, rtbTest.Document.ContentEnd);
string rtf;
using (var memoryStream = new MemoryStream())
{
    textRange.Save(memoryStream, DataFormats.Rtf);
    rtf = ASCIIEncoding.Default.GetString(memoryStream.ToArray());
}

rtf = rtf.Replace("abcd", "rstu");

MemoryStream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(rtf));
rtbTest.SelectAll();
rtbTest.Selection.Load(stream, DataFormats.Rtf);

llycmphe

llycmphe2#

修改RichTextBox内容时,分析TextPointer上下文非常重要。如何实现它,请参见下面的示例:

  • MainWindow.xaml*
<Window ...
        Title="MainWindow" Height="350" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>            
        </Grid.RowDefinitions>
        <RichTextBox x:Name="rtb" AllowDrop="True" VerticalScrollBarVisibility="Auto" Padding="2">
            <FlowDocument>
                <Paragraph>
                    <Run Text="Paste a content to the document..."/>                                      
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
        <Button Grid.Row="1" Click="FindAndReplace">Find &amp; Replace </Button>
    </Grid>
</Window>
  • MainWindow.xaml.cs* 的一部分
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

private void FindAndReplace(object sender, RoutedEventArgs e)
{
    var textToFind = "ABC";
    var textReplaceBy = "<A-B-C>";

    TextPointer start = rtb.Document.ContentStart;
    while (true)
    {
        var searchRange = new TextRange(start, rtb.Document.ContentEnd);
        TextRange foundRange = searchRange.FindText(textToFind);
        if (foundRange == null)
            break;

        foundRange.Text = textReplaceBy;             
        start = foundRange.End; // Continue the searching 
    }
    rtb.Focus();
}
  • TextRangeExt.cs*
using System;
using System.Windows.Documents;

namespace WpfApp7
{
    public static class TextRangeExt
    {
        public static TextRange FindText(this TextRange searchRange, string searchText)
        {
            TextRange result = null;
            int offset = searchRange.Text.IndexOf(searchText, StringComparison.OrdinalIgnoreCase);
            if (offset >= 0)
            {
                var start = searchRange.Start.GetTextPositionAtOffset(offset);
                result = new TextRange(start, start.GetTextPositionAtOffset(searchText.Length));
            }
            return result;
        }

        private static TextPointer GetTextPositionAtOffset(this TextPointer position, int offset)
        {
            for (TextPointer current = position; current != null; current = position.GetNextContextPosition(LogicalDirection.Forward))
            {
                position = current;
                var adjacent = position.GetAdjacentElement(LogicalDirection.Forward);
                var context = position.GetPointerContext(LogicalDirection.Forward);
                switch (context)
                {
                    case TextPointerContext.Text:
                        int count = position.GetTextRunLength(LogicalDirection.Forward);
                        if (offset <= count)
                        {
                            return position.GetPositionAtOffset(offset);
                        }
                        offset -= count;
                        break;
                    case TextPointerContext.ElementStart:
                        if (adjacent is InlineUIContainer)
                        {
                            offset--;
                        }
                        else if (adjacent is ListItem lsItem)
                        {
                            var trange = new TextRange(lsItem.ElementStart, lsItem.ElementEnd);
                            var index = trange.Text.IndexOf('\t');
                            if (index >= 0)
                            {
                                offset -= index + 1;
                            }                            
                        }
                        break;
                    case TextPointerContext.ElementEnd:
                        if (adjacent is Paragraph)
                            offset -= 2;                        
                        break;
                }
            }
            return position;
        }

    }
}

相关问题