This is a migrated thread and some comments may be shown as answers.

zoom with scroll

8 Answers 413 Views
PDFViewer
This is a migrated thread and some comments may be shown as answers.
Davide
Top achievements
Rank 1
Davide asked on 06 Apr 2016, 10:19 AM

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

Sort by
0
Davide
Top achievements
Rank 1
answered on 08 Apr 2016, 10:49 AM
http://feedback.telerik.com/Project/143/Feedback/Details/141685-pdfviewer-zoom-to-mouse-cursor-when-using-ctrlmousewheel
0
Deyan
Telerik team
answered on 11 Apr 2016, 08:08 AM
Hi Davide,

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
Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
0
Chris Johnson
Top achievements
Rank 1
answered on 01 Mar 2018, 03:53 PM

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.

0
Deyan
Telerik team
answered on 06 Mar 2018, 03:38 PM
Hello Chris Johnson,

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
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
gy
Top achievements
Rank 1
answered on 06 Jul 2018, 08:11 PM

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!

0
Deyan
Telerik team
answered on 11 Jul 2018, 01:22 PM
Hello,

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
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
gy
Top achievements
Rank 1
answered on 11 Jul 2018, 03:40 PM

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.

0
Deyan
Telerik team
answered on 16 Jul 2018, 07:41 AM
Hello,

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
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
Tags
PDFViewer
Asked by
Davide
Top achievements
Rank 1
Answers by
Davide
Top achievements
Rank 1
Deyan
Telerik team
Chris Johnson
Top achievements
Rank 1
gy
Top achievements
Rank 1
Share this question
or