Saving the vertical FlowDocument cursor in a RichTextBox

I am having trouble with this, and hopefully someone can help.

I have a WPF project with RichTextBox.

When I edit the text, I would like the cursor in the document to always remain centered vertically.

For example, if you press up or down during editing, and not the cursor up, I would like the text to go down. This should cause the cursor to remain stationary.

Many thanks.

+7
source share
2 answers

Not sure if this is what you had in mind, but there is a proof of concept for RichTextBox, which stores the cursor in the center where the user places it (clicks in the field).

Although, since Omkar said you would need to add spaces if the document was scrolled to the beginning or the end, you need to add white color to scroll through the text.

<RichTextBox HorizontalAlignment="Left" Height="311" VerticalAlignment="Top" Width="509" PreviewKeyDown="HandleKeyDownEvent"> <FlowDocument> <Paragraph Margin="0"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla turpis sem, tincidunt id vestibulum venenatis, fermentum eget orci. Donec mollis neque ac leo tincidunt tempus. Pellentesque mollis, nunc sit amet fermentum rutrum, lectus augue ultrices nibh, at lacinia est est ut justo. Cras non quam eu enim vulputate porttitor eu sit amet lectus. Suspendisse potenti. Maecenas metus nunc, dapibus id dapibus rhoncus, semper quis leo. Pellentesque eget risus magna, dignissim aliquam diam. Morbi. </Paragraph> </FlowDocument> </RichTextBox> 

In the code behind:

 private void HandleKeyDownEvent(object sender, KeyEventArgs e) { RichTextBox rtb = sender as RichTextBox; if (rtb != null) { //text to scroll up relative to caret if (e.Key == Key.Down) { Block paragraph; //get the whitespace paragraph at end of documnent paragraph = rtb.Document.Blocks .Where(x => x.Name == "lastParagraph") .FirstOrDefault(); // if there is no white space paragraph create it if (paragraph == null) { paragraph = new Paragraph { Name = "lastParagraph", Margin = new Thickness(0) }; //add to the end of the document rtb.Document.Blocks.InsertAfter(rtb.Document.Blocks.LastBlock, paragraph); } // if viewport larger than document, add whitespace content to fill view port if (rtb.ExtentHeight < rtb.ViewportHeight) { Thickness margin = new Thickness() { Top = rtb.ViewportHeight - rtb.ExtentHeight }; margin.Bottom = rtb.ViewportHeight - rtb.ExtentHeight; paragraph.Margin = margin; } // if the document has been scrolled to the end or doesn't fill the view port if (rtb.VerticalOffset + rtb.ViewportHeight == rtb.ExtentHeight) { // and a line to the white paragraph paragraph.ContentEnd.InsertLineBreak(); } //move the text up relative to caret rtb.LineDown(); } // text is to scroll download relative to caret if (e.Key == Key.Up) { // get whitespace at start of document Block paragraph; paragraph = rtb.Document.Blocks .Where(x => x.Name == "firstParagraph") .FirstOrDefault(); //if whitespace paragraph is null append a new one if (paragraph == null) { paragraph = new Paragraph { Name = "firstParagraph", Margin = new Thickness(0) }; rtb.Document.Blocks.InsertBefore(rtb.Document.Blocks.FirstBlock, paragraph); } // up document is at top add white space if (rtb.VerticalOffset == 0.0) { paragraph.ContentStart.InsertLineBreak(); } //move text one line down relative to caret rtb.LineUp(); } } } 

EDIT: This approach seems to work. The line height is determined using the difference between the top of one line and the next, avoiding the problem of line breaking performing offset.

  <RichTextBox PreviewKeyDown="PreviewKeyDownHandler"> <FlowDocument> <!-- Place content here --> </FlowDocument> </RichTextBox> 

In the code behind:

  private void PreviewKeyDownHandler(object sender, KeyEventArgs e) { RichTextBox rtb = sender as RichTextBox; if (rtb != null) { if (e.Key == Key.Down) { // if there is another line below current if (rtb.CaretPosition.GetLineStartPosition(0) != rtb.CaretPosition.GetLineStartPosition(1)) { // find the FlowDocumentView through reflection FrameworkElement flowDocumentView = GetFlowDocument(rtb); // get the content bounds of the current line Rect currentLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); // move the caret down to next line EditingCommands.MoveDownByLine.Execute(null, rtb); // get the content bounds of the new line Rect nextLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); // get the offset the document double currentDocumentOffset = flowDocumentView.Margin.Top; // add the height of the previous line to the offset // the character rect of a line doesn't include the baseline offset so the actual height of line has to be determined // from the difference in the offset between the tops of the character rects of the consecutive lines flowDocumentView.Margin = new Thickness { Top = currentDocumentOffset + currentLineBounds.Top - nextLineBounds.Top }; } // prevent default behavior e.Handled = true; } if (e.Key == Key.Up) { if (rtb.CaretPosition.GetLineStartPosition(0) != rtb.CaretPosition.GetLineStartPosition(-1)) { FrameworkElement flowDocumentView = GetFlowDocument(rtb); Rect currentLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); EditingCommands.MoveUpByLine.Execute(null, rtb); Rect nextLineBounds = rtb.CaretPosition.GetCharacterRect(LogicalDirection.Forward); double currentDocumentOffset = flowDocumentView.Margin.Top; flowDocumentView.Margin = new Thickness { Top = currentDocumentOffset + currentLineBounds.Top - nextLineBounds.Top }; } e.Handled = true; } } } protected FrameworkElement GetFlowDocument(RichTextBox textBox) { FrameworkElement flowDocumentVisual = GetChildByTypeName(textBox, "FlowDocumentView") as FrameworkElement; return flowDocumentVisual; } protected DependencyObject GetChildByTypeName(DependencyObject dependencyObject, string name) { if (dependencyObject.GetType().Name == name) { return dependencyObject; } else { if (VisualTreeHelper.GetChildrenCount(dependencyObject) > 0) { int childCount = VisualTreeHelper.GetChildrenCount(dependencyObject); for (int idx = 0; idx < childCount; idx++) { var dp = GetChildByTypeName(VisualTreeHelper.GetChild(dependencyObject, idx), name); if (dp != null) return dp; } return null; } else { return null; } } } 
+3
source

So, I am trying something like that, but have not yet worked, just busy with something else:

 TextPointer start = flowDocument.ContentStart; TextPointer caretPosition = RichTextBox1.CaretPosition; var offset = start.GetOffsetToPosition(caretPosition); RichTextBox1.ScrollToVerticalOffset(offset); 
0
source

All Articles