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代码在这里,在我的到位桶回购.
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.其余的仍然相似.