using System; using System.Linq; using System.Threading; using System.Windows; using System.Windows.Input; using System.Windows.Threading; namespace QuickLook.Plugin.PDFViewer { public sealed class PreviewMouseWheelMonitor : IDisposable { private readonly UIElement _canvas; private readonly Dispatcher _dispatcher; private readonly int _sensitivity; private bool _disposed; private volatile bool _inactive; private AutoResetEvent _resetMonitorEvent; private volatile bool _stopped; public PreviewMouseWheelMonitor(UIElement canvas, int sensitivity) { _canvas = canvas; _canvas.PreviewMouseWheel += (s, e) => RaisePreviewMouseWheel(e); _sensitivity = sensitivity; _dispatcher = Dispatcher.CurrentDispatcher; _resetMonitorEvent = new AutoResetEvent(false); _disposed = false; _inactive = true; _stopped = true; var monitor = new Thread(Monitor) {IsBackground = true}; monitor.Start(); } public void Dispose() { if (!_disposed) { _disposed = true; DetachEventHandlers(); if (_resetMonitorEvent != null) { _resetMonitorEvent.Close(); _resetMonitorEvent = null; } } } public event EventHandler PreviewMouseWheel; public event EventHandler PreviewMouseWheelStarted; public event EventHandler PreviewMouseWheelStopped; private void Monitor() { while (!_disposed) { if (_inactive) // if wheel is still inactive... { _resetMonitorEvent.WaitOne(_sensitivity / 10); // ...wait negligibly small quantity of time... continue; // ...and check again } // otherwise, if wheel is active... _inactive = true; // ...purposely change the state to inactive _resetMonitorEvent.WaitOne(_sensitivity); // wait... if (_inactive ) // ...and after specified time check if the state is still not re-activated inside mouse wheel event RaiseMouseWheelStopped(); } } private void RaisePreviewMouseWheel(MouseWheelEventArgs args) { if (_stopped) RaiseMouseWheelStarted(); _inactive = false; if (PreviewMouseWheel != null) PreviewMouseWheel(_canvas, args); } private void RaiseMouseWheelStarted() { _stopped = false; if (PreviewMouseWheelStarted != null) PreviewMouseWheelStarted(_canvas, new EventArgs()); } private void RaiseMouseWheelStopped() { _stopped = true; if (PreviewMouseWheelStopped != null) _dispatcher.Invoke(() => PreviewMouseWheelStopped(_canvas, new EventArgs())); // invoked on cached dispatcher for convenience (because fired from non-UI thread) } private void DetachEventHandlers() { if (PreviewMouseWheel != null) foreach (var handler in PreviewMouseWheel.GetInvocationList().Cast>()) PreviewMouseWheel -= handler; if (PreviewMouseWheelStarted != null) foreach (var handler in PreviewMouseWheelStarted.GetInvocationList().Cast>()) PreviewMouseWheelStarted -= handler; if (PreviewMouseWheelStopped != null) foreach (var handler in PreviewMouseWheelStopped.GetInvocationList().Cast>()) PreviewMouseWheelStopped -= handler; } } }