Underscore not detected after RTF reboot

I'm currently trying to get a RichTextBox with basic formatting working for my new beta note software, Lilly Notes . Brian Lagunas' article on this subject set me in the right direction, but I have a problem. If you click the underlined text, the underline button will be pressed, so the status will be recognized. However, if I serialize it to RTF and then deserialize it back to RichTextBox, it is not detected. Since the code in Lilly Notes is not trivial to demonstrate here, I created SSCCE to demonstrate the problem.

Firstly, 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> 

It looks like this:

enter image description here

In the code code, I have code to determine the formatting state when changing the selection and accordingly updating the state of the underline button. This is no different from the Brian Lagunas method.

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

Then I have a method (and another helper method) that saves the RTF to a string and then applies it to the RichTextBox. Again, I do it simply to make it simple - in Lilly Notes I save this line in the database and then load it back when the application starts again.

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

After I click the Save and Reload button and the RTF is serialized into a string and deserialized back to the RichTextBox, the underline definition no longer works, and when I click the underlined text, the button remains as if the underline does not work:

enter image description here

Now that I was debugging this, I noticed this:

enter image description here

Initially, when you click on a piece of underlined text, you get a TextDecorationCollection with a Count of 1. But after saving and reloading, you get a Count from zero, so the detection does not work.

Note that this issue only applies to underline / strikethrough, which are related to TextDecorationCollection in WPF. Bold and italics do not show this problem.

Is this happening because I'm doing something wrong, or is it a bug with the RichTextBox?

In my BitBucket registry, you can find the SSCCE code here .

+6
source share
4 answers

Inline.TextDecorations is a compilation, so perhaps comparing it directly is not a good idea.

Maybe this will work better:

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

EDIT

After going through the code, I discovered a probable reason:

before

This image was taken before saving and reloading as RTF.

In the above image, notice that the lines in the text are Run , and the parent of the caret is also Run and both have TextDecorations in place.

Now save and reload!

after

In the image above, notice that the lines in the paragraph are now Span , and the parent carriage element is Run . but it is strange that Span has TextDecoration in place, but the parent Run does not have TextDecoration in it.

Decision

Here is a possible solution, or a workaround is better said:

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

In the above solution, I tried to get the base Run or Span using the current caret position. The rest remains similar.

+5
source

The error in GetCharacterValueFromPosition seems to be used by RichTextBox.Selection.GetPropertyValue. Look at this entry: link

When this method receives TextDecorationsProperty, it goes up the logical tree to find an element that has a nonzero value for the property. When he finds such a value, he returns it. The problem is that it must check both the null value and the empty TextDecorationCollection.

If you use the proposed implementation, it will fix the problem with the checked state on your toggle button. However, there is still a problem that it does not set / undo underlining properly.

+1
source

Try changing the selection event handler as follows:

 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; } } 
0
source

I know that this is an old question and already has an accepted answer, but not quite the right answer and with the same problem, I am writing a solution for future users that I found. In 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 
0
source

Source: https://habr.com/ru/post/973588/


All Articles