如何检测鼠标滚轮事件在WPF中结束

本文关键字:事件 WPF 结束 鼠标 何检测 检测 | 更新日期: 2023-09-27 17:57:34

当我滚动鼠标滚轮时,会触发几个MouseWheel事件。我用这些事件来缩放一些图像。

我想在一系列MouseWheel事件结束的那一刻调用一个方法。我怎么知道它们什么时候结束?

以下是我到目前为止的实现

private void ModelWindowBorder_MouseWheel(object sender, MouseWheelEventArgs e)
{
  intervaltimer = null;
  // Do stuff like zooming and etc
  CheckEventInterval()
}
private void CheckEventInterval()
{
    intervaltimer = new Stopwatch();
    intervaltimer .Start();
    if (intervaltimer.ElapsedMilliseconds > 50)
    {
        // Do some other stuff
    }
}

如何检测鼠标滚轮事件在WPF中结束

实际上,由于大多数轮子的旋转是无休止的,因此没有特殊事件来通知使用的滚动结束。然而,在您的情况下,您可以测试用户是否在短时间内停止滚动。这可以通过一个简单的计时器来完成:

    //Use dispatcher timer to avoid problems when manipulating UI related objects
    DispatcherTimer timer;
    float someValue = 0;
    public MainWindow()
    {
        InitializeComponent();
        timer = new DispatcherTimer();
        timer.Tick += timer_Tick;
        timer.Interval = TimeSpan.FromMilliseconds(500 /*Adjust the interval*/);

        MouseWheel += MainWindow_MouseWheel;
    }
    void timer_Tick(object sender, EventArgs e)
    {
        //Prevent timer from looping
        (sender as DispatcherTimer).Stop();
        //Perform some action
        Console.WriteLine("Scrolling stopped (" + someValue + ")");
        //Reset for futher scrolling
        someValue = 0;
    }
    void MainWindow_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        //Accumulate some value
        someValue += e.Delta;
        timer.Stop();
        timer.Start();
    }

如您所见,鼠标滚轮事件将启动计时器。如果在计时器启动之前发生了新的鼠标滚轮事件,它将重新启动计时器。这样,只有在特定间隔内没有轮子事件时,计时器才会启动。

这里有一种替代方法,允许您指定要检测鼠标滚轮移动和灵敏度的UI元素(例如画布、窗口、控件等),这是以毫秒为单位的超时,之后滚轮被视为不活动(触发自定义停止事件):

public sealed class MouseWheelMonitor : IDisposable
{
    private AutoResetEvent _resetMonitorEvent;
    private readonly Dispatcher _dispatcher;
    private readonly UIElement _canvas;
    private readonly int _sensitivity;
    private bool _disposed;
    private volatile bool _inactive;
    private volatile bool _stopped;
    public event EventHandler<MouseWheelEventArgs> MouseWheel;
    public event EventHandler<EventArgs> MouseWheelStarted;        
    public event EventHandler<EventArgs> MouseWheelStopped;
    public MouseWheelMonitor(UIElement canvas, int sensitivity)
    {
        _canvas = canvas;
        _canvas.MouseWheel += (s, e) => RaiseMouseWheel(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();
    }
    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 RaiseMouseWheel(MouseWheelEventArgs args)
    {
        if (_stopped)
            RaiseMouseWheelStarted();
        _inactive = false;
        if (MouseWheel != null)
            MouseWheel(_canvas, args);
    }
    private void RaiseMouseWheelStarted()
    {
        _stopped = false;
        if (MouseWheelStarted != null)
            MouseWheelStarted(_canvas, new EventArgs());
    }
    private void RaiseMouseWheelStopped()
    {
        _stopped = true;
        if (MouseWheelStopped != null)
            _dispatcher.Invoke(() => MouseWheelStopped(_canvas, new EventArgs())); // invoked on cached dispatcher for convenience (because fired from non-UI thread)
    }    
    public void Dispose()
    {
        if(!_disposed)
        {
            _disposed = true;
            DetachEventHandlers();
            if (_resetMonitorEvent != null)
            {
                _resetMonitorEvent.Close();
                _resetMonitorEvent = null;
            }
        }
    }
    private void DetachEventHandlers()
    {
        if (MouseWheel != null)
        {
            foreach (var handler in MouseWheel.GetInvocationList().Cast<EventHandler<MouseWheelEventArgs>>())
            {
                MouseWheel -= handler;
            }
        }
        if (MouseWheelStarted != null)
        {
            foreach (var handler in MouseWheelStarted.GetInvocationList().Cast<EventHandler<EventArgs>>())
            {
                MouseWheelStarted -= handler;
            }
        }
        if (MouseWheelStopped != null)
        {
            foreach (var handler in MouseWheelStopped.GetInvocationList().Cast<EventHandler<EventArgs>>())
            {
                MouseWheelStopped -= handler;
            }
        }
    }
}

使用示例:

var monitor = new MouseWheelMonitor(uiElement, 1000);
monitor.MouseWheel += (s, e) => { Debug.WriteLine("mouse wheel turned"); };
monitor.MouseWheelStarted += (s, e) => { Debug.WriteLine("mouse wheel started"); };
monitor.MouseWheelStopped += (s, e) => { Debug.WriteLine("mouse wheel stopped"); };