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

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

首先,MainWindow.xaml:

  

这就是它的样子:

在此处输入图像描述

在代码隐藏中,我有代码在选择更改时检测格式的状态,并相应地更新下划线按钮的状态。 这与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); } } 

然后我有一个方法(和另一个辅助方法),它将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); } 

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

在此处输入图像描述

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

在此处输入图像描述

最初,当您单击一个带下划线的文本时,您将获得一个Count为1的TextDecorationCollection 。但是在保存并重新加载后,您将获得一个零Count ,这就是检测不起作用的原因。

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

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

你可以在我的BitBucket仓库找到SSCCE代码。

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

编辑

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

之前

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

在上图中,请注意段落的内联是Run ,而插入符的父级也是Run ,两者都有TextDecorations

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

后

在上图中,请注意段落的内联现在是Span ,而插入符的父级是Run 。 但奇怪的是, SpanTextDecoration ,但是父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或Span。 其余的仍然相似。

RichTextBox.Selection.GetPropertyValue正在使用GetCharacterValueFromPosition中的错误。 看看这篇文章: 链接

当该方法获取TextDecorationsProperty时,它向上走逻辑树以查找具有该属性的非null值的元素。 当它找到这样的值时,它返回它。 问题是它应该检查空值和空TextDecorationCollection。

如果您使用建议的实现,它将修复切换按钮上的已检查状态问题。 但是仍然存在不能正确设置/取消设置下划线的问题。

尝试将选择事件处理程序更改为以下内容:

 private void RichTextBox_SelectionChanged(object sender, RoutedEventArgs e) { if (this.RichText.Selection != null) { var currentValue = new TextRange(this.RichText.Selection.Start, this.RichText.Selection.End); if (currentValue.GetPropertyValue(Inline.TextDecorationsProperty) == TextDecorations.Underline) { this.UnderlineButton.IsChecked = true; } else { this.UnderlineButton.IsChecked = false; } } 

我知道这是一个老问题,并且已经有了一个已接受的答案,但不是完全正确的答案,并且遇到同样的问题,我为未找到的解决方案的用户写道。 在VB …

  Dim TextRange = New TextRange(richTxtEditor.Selection.Start, richTxtEditor.Selection.End) Dim textDecor As TextDecorationCollection Dim DecorationFound as Boolean = false If TextRange.IsEmpty Then textDecor = txtRange.GetPropertyValue(Inline.TextDecorationsProperty) If textDecor.Equals(TextDecorations.Underline) Then MsgBox("Is Underline, and it only works on a new document, not a document loaded.") DecorationFound = true End If If textDecor.Equals(TextDecorations.Strikethrough) Then MsgBox("Is Strikethrough, and it only works on a new document, not a document loaded.") DecorationFound = true End If ' ### START # From here starts the solution to the problem !!! ### If NOT DecorationFound Then For i = 0 To textDecor.Count - 1 If textDecor.Item(i).Location = TextDecorationLocation.Underline Then MsgBox("Is Underline, and it works on a loaded document.") ElseIf textDecor.Item(i).Location = TextDecorationLocation.Strikethrough Then MsgBox("Is Strikethrough, and it works on a loaded document.") End If Next End If ' ### END SOLUTION ### End If