Why does text of TextPointer vary depending on whether BackgroundProperty is set or not?

Answered Why does text of TextPointer vary depending on whether BackgroundProperty is set or not?

  • Monday, August 20, 2012 7:35 PM
     
      Has Code

    After more than a little experimentation, I am kind of seeing double when trying to reliably get the position and text of a FlowDocument TextPointer. Problem is the text I get from the TextPointer varies depending on whether the BackgroundProperty of the text has been set or not.

    Is this correct? If so, how do I return the same text at the same offset in the line whether the text has a BackgroundProperty applied to it or not?

    In the code sample posted I fill a RichTextBox with 50 lines of text. Then I use GetLineStartPosition and GetPositionAtOffset to set a TextPointer to the start of line 3. Use GetTextInRun to get the text and the return value is an empty string.  Then I set a TextRange to the 6 characters of the line and set the BackgroundProperty of that range. The result is only the first 4 chars of the line are highlighted. And when I use GetPositionAtOffset again to get a TextPointer to the first char of the line, GetTextInRun now returns the text of the first 4 chars of the line.

    It is all pretty confusing. How do I reliably get the text of a FlowDocument. And how to reliably be able to set the background property of all the characters on the line?

    thanks,

    <Window x:Class="ColumnSelect.Windows.Window2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window2" Height="300" Width="400">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
                <RowDefinition Height="auto"></RowDefinition>
            </Grid.RowDefinitions>
            <RichTextBox x:Name="rbText"
                         AcceptsReturn="True"
                         VerticalScrollBarVisibility="Auto"
                         />
            <StackPanel Orientation="Horizontal" Grid.Row="1">
                <Button x:Name="but1" Margin="10" Click="but1_Click" >Get Text, then Highlight</Button>
                <Button x:Name="but2" Margin="10" Click="but2_Click">Get Text, then Highlight, then clear highlighting</Button>
            </StackPanel>
        </Grid>
    </Window>
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    
    namespace ColumnSelect.Windows
    {
      /// <summary>
      /// Interaction logic for Window2.xaml
      /// </summary>
      public partial class Window2 : Window
      {
        public Window2()
        {
          InitializeComponent();
          Loaded += Window2_Loaded;
        }
    
        void Window2_Loaded(object sender, RoutedEventArgs e)
        {
          FlowDocument flowDoc = new FlowDocument();
    
          var para = new Paragraph();
          para.FontSize = 28.0;
          para.FontFamily = new FontFamily("Lucida console");
    
          for (int cx = 0; cx < 50; ++cx)
          {
            Run run = new Run("line:" + cx.ToString());
            para.Inlines.Add(run);
            para.Inlines.Add(new LineBreak());
    
          }
          flowDoc.Blocks.Add(para);
          rbText.Document = flowDoc;
        }
    
        private void but1_Click(object sender, RoutedEventArgs e)
        {
          var flowDoc = rbText.Document;
    
          // position pointer to line 3.
          var lp1 = flowDoc.ContentStart.GetLineStartPosition(0);
          lp1 = lp1.GetLineStartPosition(3);
          TextRange trSave = null;
    
          // position pointer at column 0 of line 3. Text of pointer returns blanks. Why?
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var s1 = tp1.GetTextInRun(LogicalDirection.Forward);
            Debug.WriteLine("Before setting background property. Text at offset 0 of line 3:" + s1 +
              " Length:" + s1.Length);
          }
    
          // create textRange from offset 0 to offset 5 of line 3. Then apply a background
          // property value to the range. Only the first 4 characters are highlighted.
          // Expecting 6 chars to be highlighted. From offset 0 to offset 5 of the line.
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var tp2 = lp1.GetPositionAtOffset(5);
            var tr = new TextRange(tp1, tp2);
            tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.DarkRed);
    
            // save the textRange so the backround property can be cleared later.
            trSave = tr;
          }
    
          // now that the text is highlighted, get the Text of the TextPointer starting
          // at offset 0. The text of the text pointer now shows a value. But only the first 
          // 4 chars. In the code just run before setting the background property, the text
          // was blank.
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var s1 = tp1.GetTextInRun(LogicalDirection.Forward);
            Debug.WriteLine("After setting background property. Text at offset 0 of line 3:" + s1);
          }
    
          Debug.WriteLine("---------------------");
        }
    
        private void but2_Click(object sender, RoutedEventArgs e)
        {
          var flowDoc = rbText.Document;
    
          // position pointer to line 3.
          var lp1 = flowDoc.ContentStart.GetLineStartPosition(0);
          lp1 = lp1.GetLineStartPosition(3);
          TextRange trSave = null;
    
          // position pointer at column 0 of line 3. Text of pointer returns blanks. Why?
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var s1 = tp1.GetTextInRun(LogicalDirection.Forward);
            Debug.WriteLine("Before setting background property. Text at offset 0 of line 3:" + s1);
          }
    
          // create textRange from offset 0 to offset 5 of line 3. Then apply a background
          // property value to the range. Only the first 4 characters are highlighted.
          // Expecting 6 chars to be highlighted. From offset 0 to offset 5 of the line.
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var tp2 = lp1.GetPositionAtOffset(5);
            var tr = new TextRange(tp1, tp2);
            tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.DarkRed);
    
            // save the textRange so the backround property can be cleared later.
            trSave = tr;
          }
    
          // now that the text is highlighted, get the Text of the TextPointer starting
          // at offset 0. The text of the text pointer now shows a value. But only the first 
          // 4 chars. In the code just run before setting the background property, the text
          // was blank.
          {
            var tp1 = lp1.GetPositionAtOffset(0);
            var s1 = tp1.GetTextInRun(LogicalDirection.Forward);
            Debug.WriteLine("After setting background property. Text at offset 0 of line 3:" + s1);
          }
    
          // clear the background property. Get the text at offset 0 of the line again. The
          // text now shows as the full 6 characters of the line.
          {
            trSave.ApplyPropertyValue(TextElement.BackgroundProperty, null);
            var tp1 = lp1.GetPositionAtOffset(0);
            var s1 = tp1.GetTextInRun(LogicalDirection.Forward);
            Debug.WriteLine("After background is cleared. Text at offset 0 of line 3:" + s1);
          }
    
          Debug.WriteLine("---------------------");
        }
      }
    }
    

All Replies

  • Tuesday, August 21, 2012 7:47 AM
    Moderator
     
     Answered Has Code

    Hi Steve Richter,

    The cause of your issue it "GetLineStartPosition" give the position which is not surrounded by any text, it is not a insert position, so you could not get any text by

    "GetTextInRun(LogicalDirection.Forward);"

    and

    " var tp1 = lp1.GetPositionAtOffset(0);
       var tp2 = lp1.GetPositionAtOffset(5);"

    will be first "4" insert text.

    You could use "GetInsertionPosition" method to fix your issue, refer to below code snippet:

    lp1 = lp1.GetLineStartPosition(3).GetInsertionPosition(LogicalDirection.Backward);

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked As Answer by Steve Richter Tuesday, August 21, 2012 8:45 PM
    •