重新加载RTF后未检测到下划线

Gig*_*igi 6 c# wpf serialization rtf richtextbox

我正在尝试使用基本格式的RichTextBox为我的新测试笔记软件Lilly Notes工作.Brian Lagunas关于这个问题的文章让我朝着正确的方向前进,但是我遇到了一些问题.如果单击带下划线的文本,则会按下"下划线"按钮,因此正在识别状态.但是,如果我将其序列化为RTF然后将其反序列化为RichTextBox,则不会检测到它.由于Lilly Notes中的代码在这里展示并不容易,我创建了一个SSCCE来演示这个问题.

首先,MainWindow.xaml:

<Window x:Class="WpfRichTextBoxUnderline.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">
    <DockPanel LastChildFill="True">
        <Button Name="SaveAndReloadButton"
                Content="Save and Reload"
                DockPanel.Dock="Bottom"
                Click="SaveAndReloadButton_Click" />
        <ToggleButton Name="UnderlineButton"
                      DockPanel.Dock="Top"
                      Width="20"
                      Command="{x:Static EditingCommands.ToggleUnderline}"
                      CommandTarget="{Binding ElementName=RichText}">
            <ToggleButton.Content>
                <TextBlock Text="U"
                           TextDecorations="Underline" />
            </ToggleButton.Content>
        </ToggleButton>
        <RichTextBox Name="RichText"
                     SelectionChanged="RichTextBox_SelectionChanged" />
    </DockPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

这就是它的样子:

在此输入图像描述

在代码隐藏中,我有代码来检测选择更改时的格式状态,并相应地更新下划线按钮的状态.这与Brian Lagunas的方法没什么不同.

private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
{
    if (this.RichText.Selection != null)
    {
        object currentValue = this.RichText.Selection.GetPropertyValue(Inline.TextDecorationsProperty);
        this.UnderlineButton.IsChecked = (currentValue == DependencyProperty.UnsetValue) ? false : currentValue != null && currentValue.Equals(TextDecorations.Underline);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个方法(和另一个辅助方法),它将RTF保存到字符串,然后将其应用于RichTextBox.我这样做只是为了保持简单 - 在Lilly Notes中,我将该字符串保存到数据库中,然后在应用程序再次运行时将其加载回来.

public Stream GenerateStreamFromString(string s)
{
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(s);
    writer.Flush();
    stream.Position = 0;
    return stream;
}

private async void SaveAndReloadButton_Click(object sender, RoutedEventArgs e)
{
    string data = null;
    var range = new TextRange(this.RichText.Document.ContentStart, this.RichText.Document.ContentEnd);
    using (var memoryStream = new MemoryStream())
    {
        range.Save(memoryStream, DataFormats.Rtf);
        memoryStream.Position = 0;

        using (StreamReader reader = new StreamReader(memoryStream))
        {
            data = await reader.ReadToEndAsync();
        }
    }

    // load

    var stream = GenerateStreamFromString(data);
    range = new TextRange(this.RichText.Document.ContentStart, this.RichText.Document.ContentEnd);
    range.Load(stream, DataFormats.Rtf);
}
Run Code Online (Sandbox Code Playgroud)

单击"保存并重新加载"按钮后,RTF被序列化为字符串并反序列化为RichTextBox,下划线检测不再起作用,当我单击带下划线的文本时,按钮仍然像下划线不工作一样:

在此输入图像描述

现在,当我调试它时,我注意到了这一点:

在此输入图像描述

最初,当你点击一个带下划线的文本时,你会得到一个1的TextDecorationCollectiona Count.但是在保存和重新加载之后,你会得到Count零,这就是检测不起作用的原因.

请注意,此问题仅适用于属于TextDecorationCollectionWPF中的下划线/删除线.Bold和Italic不会出现此问题.

这是因为我做错了,还是RichTextBox的错误?

你可以找到SSCCE代码在这里,在我的到位桶回购.

pus*_*raj 5

Inline.TextDecorations 是一个集合,所以可能直接比较它不是一个好主意.

也许这会更好:

TextDecorationCollection currentValue = this.RichText.Selection.GetPropertyValue(Inline.TextDecorationsProperty) as TextDecorationCollection;
this.UnderlineButton.IsChecked = (currentValue == DependencyProperty.UnsetValue) ? false : currentValue != null && currentValue.Contains(TextDecorations.Underline);
Run Code Online (Sandbox Code Playgroud)

编辑

在完成提供的代码后,我发现了可能的原因:

之前

此图像在保存并重新加载为RTF之前完成.

在上图中,请注意段落的内联是Run和插入符的父级也是a Run并且两者都已TextDecorations到位.

现在让我们保存并重新加载!

后

在上图中,请注意段落的内联现在Span是插入符号的父级Run.但奇怪的是,它Span已经TextDecoration到位,但是父母Run没有任何TextDecoration内容.

这是一个可能的解决方案,或者更好地说一个解决方法:

    private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e)
    {
        var caret = RichText.CaretPosition;
        Paragraph paragraph = RichText.Document.Blocks.FirstOrDefault(x => x.ContentStart.CompareTo(caret) == -1 && x.ContentEnd.CompareTo(caret) == 1) as Paragraph;

        if (paragraph != null)
        {
            Inline inline = paragraph.Inlines.FirstOrDefault(x => x.ContentStart.CompareTo(caret) == -1 && x.ContentEnd.CompareTo(caret) == 1) as Inline;
            if (inline != null)
            {
                TextDecorationCollection decorations = inline.TextDecorations;
                this.UnderlineButton.IsChecked = (decorations == DependencyProperty.UnsetValue) ? false : decorations != null && decorations.Contains(TextDecorations.Underline[0]);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

在上面的解决方案中,我试图通过使用当前的插入位置来获取底层的Run或Span.其余的仍然相似.