File: System\Windows\InterOp\HwndPanningFeedback.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
//
 
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using MS.Win32;
 
namespace System.Windows.Interop
{
    /// <summary>
    ///     Helper class to provide window feedback that panning has hit an edge.
    /// </summary>
    internal class HwndPanningFeedback
    {
        /// <summary>
        ///     Instantiates a new instance of this class.
        /// </summary>
        /// <param name="hwndSource">The HWND on which to provide feedback.</param>
        public HwndPanningFeedback(HwndSource hwndSource)
        {
            ArgumentNullException.ThrowIfNull(hwndSource);
 
            _hwndSource = hwndSource;
        }
 
        private static bool IsSupported
        {
            get
            {
                return OperatingSystemVersionCheck.IsVersionOrLater(OperatingSystemVersion.Windows7);
            }
        }
 
        /// <summary>
        ///     Returns the handle of the current window.
        /// </summary>
        private HandleRef Handle
        {
            get
            {
                if (_hwndSource != null)
                {
                    IntPtr handle = _hwndSource.CriticalHandle;
                    if (handle != IntPtr.Zero)
                    {
                        return new HandleRef(_hwndSource, handle);
                    }
                }
 
                return new HandleRef();
            }
        }
 
        /// <summary>
        ///     Moves the window by the offset.
        ///     Used to provide feedback that panning has hit an edge.
        /// </summary>
        /// <param name="totalOverpanOffset">The total offset relative to the original location.</param>
        /// <param name="inInertia">Whether the edge was hit due to inertia or the user panning.</param>
        public void UpdatePanningFeedback(Vector totalOverpanOffset, bool inInertia)
        {
            if ((_hwndSource != null) && IsSupported)
            {
                if (!_isProvidingPanningFeedback)
                {
                    _isProvidingPanningFeedback = UnsafeNativeMethods.BeginPanningFeedback(Handle);
                }
 
                if (_isProvidingPanningFeedback)
                {
                    var deviceOffset = _hwndSource.TransformToDevice((Point)totalOverpanOffset);
                    _deviceOffsetX = (int)deviceOffset.X;
                    _deviceOffsetY = (int)deviceOffset.Y;
                    _inInertia = inInertia;
 
                    if (_updatePanningOperation == null)
                    {
                        _updatePanningOperation = _hwndSource.Dispatcher.BeginInvoke(DispatcherPriority.Background,
                            new DispatcherOperationCallback(OnUpdatePanningFeedback), 
                            this);
                    }
                }
            }
        }
 
        private object OnUpdatePanningFeedback(object args)
        {
            HwndPanningFeedback panningFeedback = (HwndPanningFeedback)args;
            _updatePanningOperation = null;
            UnsafeNativeMethods.UpdatePanningFeedback(panningFeedback.Handle, panningFeedback._deviceOffsetX, panningFeedback._deviceOffsetY, panningFeedback._inInertia);
            return null;
        }
 
        private int _deviceOffsetX;
        private int _deviceOffsetY;
        private bool _inInertia;
        private DispatcherOperation _updatePanningOperation;
 
        /// <summary>
        ///     Moves the window back to its original position.
        ///     Used to provide feedback that panning has hit an edge.
        /// </summary>
        /// <param name="animateBack">Whether to animate or snap back to the original position</param>
        public void EndPanningFeedback(bool animateBack)
        {
            if (_hwndSource != null && _isProvidingPanningFeedback)
            {
                _isProvidingPanningFeedback = false;
                if (_updatePanningOperation != null)
                {
                    _updatePanningOperation.Abort();
                    _updatePanningOperation = null;
                }
                UnsafeNativeMethods.EndPanningFeedback(Handle, animateBack);
            }
        }
 
        /// <summary>
        ///     Whether panning feedback is currently in progress.
        /// </summary>
        private bool _isProvidingPanningFeedback;
 
        /// <summary>
        ///     The HwndSource being manipulated.
        /// </summary>
        private HwndSource _hwndSource;
    }
}