Hi,
can the pdfviewer automatically scroll in the position where the user is zooming with the fingers?
because if i zoom in a specific position (for example bottom right), the pdfviewer continues to zoom in top left angle of the pdf.
Thanks
Davide
8 Answers, 1 is accepted

Thank you for contacting us.
This functionality is not available in the current version of the product. You may vote for the feature request and follow its implementation progress by following the feedback item.
Meanwhile, you may try to workaround this missing functionality by using Telerik TouchManager, RadPdfViewer's GoToDestination method, HorizontalScrollBar and VerticalScrollBar properties. Currently, RadPdfViewer uses TouchManager Pinch events to zoom, so if you attach to the corresponding events you may try scrolling to the correct position right after RadPdfViewer has performed its default zoom behaviour. Here follow a few hints that may help you to implement this functionality:
- First thing you need to do is to attach to RadPdfViewer's FixedDocumentPresenterChanged, so that you can attach to the new presenter touch event and detach from the same events of the old presenter.
- In order to be notified when the zoom action is performed you should use TouchManager methods AddPinchStartedEventHandler, AddPinchEventHandler and AddPinchFinishedEventHandler. This methods will add your custom event handlers to RadPdfViewer's FixedDocumentPresenter. The last parameter of these methods should be "true" as you need to handle already handled events.
- By using PinchEventArgs GetTouchPoint1 and GetTouchPoint2 methods you may get the pinch points relative to the presenter. Getting the mid point of the above two points you will get the point in the viewport that should preserve its position after zooming.
- By using FixedDocumentPresenter's GetLocationFromViewPoint method you may convert the viewport position of the pinch point to PDF document position. This way you will know exactly which page is zoomed and which point in this page should preserve its viewport position. In your PinchStartedEventHandler you may additionally get the ScaleFactor value at the moment the zoom action starts.
- You may preserve the above information for RadFixedPage, Left and Top page coordinates and the current Zoom value in a Location class instance.
- In PinchEventHandler and PinchFinishedEventHandler you may use RadPdfViewer's GoToDestination method in order to scroll to the previosly preserved Location. You should be aware that GoToDestination method scrolls such that the Location point is positioned at the top left viewport corner. In order to move this document point to the desired position you should perform some additional scrolling using RadPdfViewer's HorizontalScrollBar and VerticalScrollBar properties. In order to calculate how much exactly you need to scroll you should use the current ScaleFactor value and RadFixedPage instances Size property. You should also be aware that if the active presenter is FixedDocumentPagesPresenter then there is additional vertical gap between each two neighbouring pages. This gap has constant value and its value is 10.
- If you follow this link to a forum post you may see a similar example showing how to attach to FixedDocumentPresenter viewport events and how to use GetLocationFromViewPoint and GoToDestination methods in order to scroll to previously clicked position.
I hope this helps. If you have any other questions or concerns please do not hesitate to contact us again.
Regards,
Deyan
the Telerik team

Hi - I've followed this answer through as far as trying to adjust the scroll bars' positions using the ScaleFactor and RadFixPage.Size to so that the centre point is maintained. Could you give some sample code please.
Also I've called TouchManager's AddPinchStartedEventHandler, AddPinchEventHandler and AddPinchFinishedEventHandler and this works but it seems the built-in handlers are also still called. Is it possible to prevent this?
Thanks.
The following code snippet shows how to override the default "CTRL+Mousewheel" zooming logic by using PreviewMouseWheel event and setting "e.Handled = true". In order to test this code on your side you should call Initialize() method right after InitializeComponent() method call in the Window constructor. As mentioned in the commented lines from this sample code - it handles especially the zooming logic only in the case when the top-left point and the mouse cursor point are over the same PDF page. However, you may try extending this logic to cover the general scenario by measuring the distance between points from neighboring pages. These measuring calculations should take into account the pages size, pages rotation and the gap between pages which has a constant value of 10.
private
IFixedDocumentPresenter presenter =
null
;
private
UIElement PresenterUI
{
get
{
return
this
.presenter
as
UIElement;
}
}
private
void
Initialize()
{
this
.presenter =
this
.pdfViewer.FixedDocumentPresenter;
if
(
this
.PresenterUI !=
null
)
{
this
.PresenterUI.PreviewMouseWheel +=
this
.Presenter_PreviewMouseWheel;
}
this
.pdfViewer.FixedDocumentPresenterChanged +=
this
.FixedDocumentPresenterChanged;
}
private
void
FixedDocumentPresenterChanged(
object
sender, EventArgs e)
{
if
(
this
.PresenterUI !=
null
)
{
this
.PresenterUI.PreviewMouseWheel -=
this
.Presenter_PreviewMouseWheel;
}
this
.presenter =
this
.pdfViewer.FixedDocumentPresenter;
if
(
this
.PresenterUI !=
null
)
{
this
.PresenterUI.PreviewMouseWheel +=
this
.Presenter_PreviewMouseWheel;
}
}
private
void
Presenter_PreviewMouseWheel(
object
sender, MouseWheelEventArgs e)
{
if
((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
IFixedDocumentPresenter presenter = (IFixedDocumentPresenter)sender;
double
scaleFactorChange = e.Delta > 0 ? 0.2 : -0.2;
double
newScaleFactor = Math.Min(5, Math.Max(0.2, presenter.Owner.ScaleFactor + scaleFactorChange));
if
(newScaleFactor != presenter.Owner.ScaleFactor)
{
Point position = e.GetPosition(
this
.PresenterUI);
RadFixedPage page;
Point location;
if
(presenter.GetLocationFromViewPoint(position,
out
page,
out
location))
{
RadFixedPage zeroPointPage;
Point zeroPoint;
bool
isTopLeftPointOnTheSamePage = presenter.GetLocationFromViewPoint(
new
Point(),
out
zeroPointPage,
out
zeroPoint) && zeroPointPage.PageNo == page.PageNo;
// We handle required zoom logic only for the case when the top-left point is on the same page with the mouse pointer location.
// Handling the logic when the points are on different pages would require additional calculations which take into account distance between points in neighbouring pages.
if
(isTopLeftPointOnTheSamePage)
{
Vector distanceFromZeroPoint = location - zeroPoint;
Vector scaledDistance = distanceFromZeroPoint * (newScaleFactor / presenter.Owner.ScaleFactor);
Point newZeroPoint = zeroPoint + (scaledDistance - distanceFromZeroPoint);
presenter.GoToDestination(
new
Location() { Page = page, Left = newZeroPoint.X, Top = newZeroPoint.Y, Zoom = newScaleFactor });
}
else
{
presenter.Owner.ScaleFactor = newScaleFactor;
}
}
}
e.Handled =
true
;
}
}
As for the TouchManager question - current implementation does not allow canceling the build-in Pinch handlers. The only possible solution is to cancel the TouchManager for the presenter UIElement.
TouchManager.SetTouchMode(
this
.PresenterUI, TouchMode.None);
However, this will not allow you to use your custom Pinch handlers with Telerik's TouchManager. Instead, you should handle the pinch logic with the appropriate .Net Framework touch events.
I hope this helps.
Regards,
Deyan
Progress Telerik

Deyan:
I am trying to use RadPdfViewer (WPF edition) to display multi-page PDF documents. I could not find any methods or properties to control the visibility of vertical or horizontal scroll bars. Do you have code examples setting and using horizontal and vertical scrollbar? Thanks!
RadPdfViewer has HorizonalScrollBar and VerticalScrollBar properties. These properties are of type System.Windows.Controls.Primitives.ScrollBar and their visibility may be changed by setting the Visibility property.
Regards,
Deyan
Progress Telerik

Deyan:
Thank you for your reply! The scrollbar still does not work. In fact, it threw an exception when Visibility.Visible is assigned to RadPdfViewer.VerticalScrollBar.Visibility. My code is as follows. pdfViewer is an object of RadPdfViewer in the Window's xaml. The same happens if the assignment of visibility statement is moved after pdfViewer.DocumentSource = new PdfDocumentSource(ms). Without the scrollbar visibility assignment statement, the code can display the PDF file in pdfViewer with no scroll capability.
public MainWindow()
{
InitializeComponent();
DisplayPdf();
}
void DisplayPdf()
{
var fs = new FileStream(@"C:\OutputReports\CurrentReport.pdf", FileMode.Open, FileAccess.Read);
var ms = new MemoryStream();
fs.CopyTo(ms);
pdfViewer.VerticalScrollBar.Visibility = Visibility.Visible; // exception happens here
pdfViewer.DocumentSource = new PdfDocumentSource(ms); // the pdf file can be display in pdfViewer if the visibility assignment is removed.
}
Exception details:
System.Reflection.TargetInvocationException
HResult=0x80131604
Message=Exception has been thrown by the target of an invocation.
Source=mscorlib
StackTrace:
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
at System.Activator.CreateInstance(Type type, Object[] args)
at System.Xaml.Schema.SafeReflectionInvoker.CreateInstanceCritical(Type type, Object[] arguments)
at System.Xaml.Schema.SafeReflectionInvoker.CreateInstance(Type type, Object[] arguments)
at System.Xaml.Schema.XamlTypeInvoker.CreateInstance(Object[] arguments)
at MS.Internal.Xaml.Runtime.ClrObjectRuntime.CreateInstanceWithCtor(XamlType xamlType, Object[] args)
at MS.Internal.Xaml.Runtime.ClrObjectRuntime.CreateInstance(XamlType xamlType, Object[] args)
at System.Xaml.XamlObjectWriter.Logic_CreateAndAssignToParentStart(ObjectWriterContext ctx)
at System.Xaml.XamlObjectWriter.WriteStartMember(XamlMember property)
at System.Xaml.XamlWriter.WriteNode(XamlReader reader)
at System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector)
at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
at System.Windows.Application.LoadBamlStreamWithSyncInfo(Stream stream, ParserContext pc)
at System.Windows.Application.LoadComponent(Uri resourceLocator, Boolean bSkipJournaledProperties)
at System.Windows.Application.DoStartup()
at System.Windows.Application.<.ctor>b__1_0(Object unused)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at InViewPdf.App.Main()
Inner Exception 1:
NullReferenceException: Object reference not set to an instance of an object.
This exception is thrown because the template of RadPdfViewer instance is not applied and the VerticalScrollBar property is null. In order to ensure that the template is applied, you may subscribe for the Loaded event.
However, even when setting the Visibility property successfully, you may notice that this visibility is overridden by the default UI implementation. This happens in the presenters ArrangeOverride implementation where the UpdateScrollBars method is invoked. By default, this implementation hides the vertical scrollbar when the pages are zoomed out enough to fit the viewport and no scrolling is needed to see the whole content. If you need to change this default behavior and always show the vertical scrollbar then you may try implementing custom presenter. The following code snippet shows sample custom presenter implementation which inherits the default FixedDocumentPagesPresenter presenter and makes the scrollbar visible in the ArrangeOverride implementation.
public
class
CustomPagesPresenter : FixedDocumentPagesPresenter
{
public
const
string
PresenterName =
"customPresenter"
;
protected
override
Size ArrangeOverride(Size arrangeBounds)
{
Size size =
base
.ArrangeOverride(arrangeBounds);
this
.Owner.VerticalScrollBar.Visibility = Visibility.Visible;
return
size;
}
}
In order to apply this custom presenter, you should register it in MainWindow constructor and apply it once the RadPdfViewer is loaded.
pdfViewer.RegisterPresenter(CustomPagesPresenter.PresenterName,
new
CustomPagesPresenter());
pdfViewer.Loaded +=
this
.PdfViewer_Loaded;
Sample code for applying the custom presenter may be seen in the snippet below:
private
void
PdfViewer_Loaded(
object
sender, RoutedEventArgs e)
{
RadPdfViewer pdfViewer = (RadPdfViewer)sender;
IFixedDocumentPresenter customPresenter = pdfViewer.GetRegisteredPresenter(CustomPagesPresenter.PresenterName);
pdfViewer.FixedDocumentPresenter = customPresenter;
}
As a result, you will have always visible verticle scrollbar during the time your custom presenter is the active RadPdfViewer's presenter.
Regards,
Deyan
Progress Telerik