Reputation: 2988
Is there anyway to get the horizontal position(pixel) and vertical position(pixel) of a Run element in a FlowDocument?
Edit: All i need to do is scroll to that position and make it the top line of the FlowDocument.
Upvotes: 0
Views: 1256
Reputation: 597
It may be late but i still want to share the way i DID it in WPF. You need an offset to do so.
As the above said: Flow gave you:
flow.ScrollToHome(); // Bottom
But also gave: ScrollToVerticalOffset (get from Rect)
if you have index (offset of the char/line) - you can find it in you saved data or get the TextPointer with flow.Selection.Start/End
TextPointer t_st = flow.Selection.Start;
double offset = flow.Document.ContentStart.GetOffsetToPosition(t_st);
private void gotoOffset(double offset)
{
TextPointer myTextPointer1 = flow.Document.ContentStart.GetPositionAtOffset((int)offset);
flow.Selection.Select(myTextPointer1, myTextPointer1);
flow.Focus();
Rect screenPos2 = myTextPointer1.GetCharacterRect(LogicalDirection.Forward);
double offset2 = screenPos2.Top;
Thread.Sleep(100);
flow.ScrollToVerticalOffset(offset2);
flow.Focus();
}
As the code above, We get the Rect from TextPointer, the Textpointer and get from Offset. The focus just to make sure to place the cursor in right place.
Sometime the issue happen when you jump to many offset.
I recomment to trigger flow.ScrollToHome();
Before jump (because this ScrollToVerticalOffset true from the start, not any line)
Upvotes: 0
Reputation: 3404
To Answer Your Question
The code needed to get the position of a content element in a document is all internal to .NET and not publically exposed. You would need access to an IContentHost implementation, which the built-in document viewers do not publically expose. So, there is no supported way to do what you are asking.
To Solve Your Actual Problem
There is a way to achieve your desired result of scrolling the element to the top of the view. What you want to do is scroll to the end of the document, then call BringIntoView on the element you want to have at the top.
There are multiple ways a FlowDocument can be displayed in an application. How you handle the scrolling depends on which control you are using to present the FlowDocument
.
RichTextBox
, use the ScrollToEnd method.In a FlowDocumentScrollViewer
, you will need to get its internal ScrollViewer and call ScrollToBottom on it. (You have to wait until the control is loaded before you can get a template part from it.)
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
mScrollViewer = mViewer.Template.FindName("PART_ContentHost", mViewer) as ScrollViewer;
}
In a FlowDocumentReader
, the process is a bit more complex.
When the control is loaded, register for changes to the ViewingMode property and run the handler once to account for the starting value:
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
var descriptor = DependencyPropertyDescriptor.FromProperty(FlowDocumentReader.ViewingModeProperty, typeof(FlowDocumentReader));
descriptor.AddValueChanged(mReader, (s, a) => Reader_ViewModeChanged());
Reader_ViewModeChanged();
}
In the handler, dig in to find the ScrollViewer
. It will only be present when the ViewingMode
is set to Scroll
:
private void Reader_ViewModeChanged()
{
mScrollViewer = null;
if (mReader.ViewingMode == FlowDocumentReaderViewingMode.Scroll)
{
var contentHost = mReader.Template.FindName("PART_ContentHost", mReader) as DependencyObject;
if (contentHost != null && VisualTreeHelper.GetChildrenCount(contentHost) > 0)
{
var documentScrollViewer = VisualTreeHelper.GetChild(contentHost, 0) as FlowDocumentScrollViewer;
if (documentScrollViewer != null)
{
documentScrollViewer.ApplyTemplate();
mScrollViewer = documentScrollViewer.Template.FindName("PART_ContentHost", documentScrollViewer) as ScrollViewer;
}
}
}
}
Once you have the ScrollViewer
, you can call ScrollToBottom on it when desired.
Now, scroll to the bottom of the document, then call BringIntoView on your Run
, and it should be at the top of the view.
Upvotes: 2
Reputation: 45096
Does not bring it to the top but just call BringIntoView on the Run. Save a reference to the Run.
Upvotes: 1