Reputation: 1175
I would like to be able to get the current vertical scroll position if a RichTextBox (line number or TextPointer
).
In my scenario, I have a dissembler and the user selects lines and choices to reformat the assembler line as bytes, words, text etc. That works fine. It also causes so many changes that the FlowDocument
is recreated, and it defaults to scrolling to the top after the text is recreated. I changed my code so it gets the line number of the current selection and scrolls to that line when the text is changed. The problem with that is that it puts the line at the top. I would like for it not scroll at all. I have code that will scroll to any line number, or it can get the line number from a TextPointer
and scroll to that. If I can get the scroll value before the text is changed, I can rescroll to that so the text does not jump.
var line = FindLineNumber(ref RichTextListing.RichText, RichTextListing.RichText.Selection.Start);
View.Flow = doc;
ScrollToLine(ref RichTextListing.RichText, line);
private TextPointer GetTextPosition(ref RichTextBox rtb, int line)
{
var p = rtb.Document.ContentStart;
var findStart = p.GetLineStartPosition(0);
if (findStart == null)
{
return p;
}
var lineNum = 0;
while (lineNum < line)
{
findStart = findStart.GetLineStartPosition(1, out _);
lineNum++;
}
return findStart;
}
private int FindLineNumber(ref RichTextBox rtb, TextPointer p)
{
var findStart = p?.GetLineStartPosition(0);
if (findStart == null) return 0;
var startLinePosition = rtb.Document.ContentStart.GetLineStartPosition(0);
if (startLinePosition == null) return 0;
var foundLineNumber = 0;
while (true)
{
if (startLinePosition.CompareTo(findStart) >= 0)
{
break;
}
startLinePosition = startLinePosition.GetLineStartPosition(1, out var result);
if (result == 0)
{
break;
}
foundLineNumber++;
}
return foundLineNumber;
}
private void ScrollToLine(ref RichTextBox rtb, int line)
{
var pos = GetTextPosition(ref rtb, line);
if (pos == null) return;
if (pos.Parent is FrameworkContentElement e)
{
e.BringIntoView();
}
}
Upvotes: 1
Views: 238
Reputation: 8890
Consider the following code.
Define:
private TextPointer ChangesStartPosition { get; set; }
Before make changes in the document set the ChangesStartPosition
property:
// `rtb` in the code below is the RichTextBox
ChangesStartPosition = rtb.Selection.IsEmpty ? rtb.CaretPosition : rtb.Selection.Start;
After performing changes in the document use the following code to keep the caret centered in the RichTextBox
:
var rect = ChangesStartPosition.GetCharacterRect(LogicalDirection.Forward);
rtb.ScrollToVerticalOffset(rtb.VerticalOffset + rect.Top - rtb.ActualHeight / 2d);
rtb.CaretPosition = ChangesStartPosition;
If a part of the document referenced by the ChangesStartPosition
property is changed and the ChangesStartPosition
become invalid, then a new TextPointer
value should be calculated before calling the ScrollToVerticalOffset()
.
Upvotes: 1