使用 Observable / Reactive Extensions 检测鼠标移动开始和停止?

Detecting mouse movement starting and stopping with an Observable / Reactive Extensions?

让我开始:

  1. 我对 Rx 完全陌生
  2. 我没有使用 C#/Linq
  3. 我是 experimenting/learning,所以没有 应用 这个问题

我已经阅读了一些介绍性内容,包括 Matthew PodwysockiIntroduction to the Reactive Framework

所以我从他的一个示例开始,并编写了一些 mouse-drag/line-drawing 代码,如下所示:

var leftMouseDown = Observable.FromEventPattern<MouseEventArgs>(mainCanvas, "MouseLeftButtonDown");
var leftMouseUp = Observable.FromEventPattern<MouseButtonEventArgs>(mainCanvas, "MouseLeftButtonUp");
var mouseMove = Observable.FromEventPattern<MouseEventArgs>(mainCanvas, "MouseMove");

var mouseMoves = from mm in mouseMove
                 let location = mm.EventArgs.GetPosition(mainCanvas)
                 select new { location.X, location.Y };

var mouseDiffs = mouseMoves.Skip(1).Zip(mouseMoves, (l, r) => new { X1 = l.X, Y1 = l.Y, X2 = r.X, Y2 = r.Y });

var mouseDrag = from _ in leftMouseDown
                from md in mouseDiffs.TakeUntil(leftMouseUp)
                select md;

var mouseSub = mouseDrag.Subscribe(item =>
{
     var line = new Line
     {
         Stroke = Brushes.LightSteelBlue,
         X1 = item.X1,
         X2 = item.X2,
         Y1 = item.Y1,
         Y2 = item.Y2,
         StrokeThickness = 5
     };
     mainCanvas.Children.Add(line);
 });

我的问题

基于这个例子,我想尝试对鼠标移动做出反应,例如:

鼠标左键,只涉及鼠标移动。

这可能吗?

我觉得这里的关键是鼠标什么时候不动?什么时候空闲了 1 秒、10 秒、250 毫秒?以下应该做你想做的。它是 bool 类型的可观察对象。它在鼠标移动时产生 true,如果鼠标空闲时间超过指定的空闲时间则产生 false

int idleTime = 1000;

var mouseMoving =
    mouseMove
    .Buffer(TimeSpan.FromMilliseconds(idleTime), 1) // Buffer the mouse move events for the duration of the idle time.
    .Select(x => x.Any())                           // Return true if the buffer is not empty, false otherwise.
    .DistinctUntilChanged();                        // Only notify when the mouse moving state changes.

我会第一个告诉你我怀疑这不是最好的解决方案。缓冲鼠标移动事件似乎是不必要的浪费。我试图使用 Sample 找到解决方案,但我无法做到这一点。?

感谢 Jerry 的评论,我对更新后的解决方案更加满意。

首先这里有个问题:

var mouseDiffs = mouseMoves.Skip(1)
    .Zip(mouseMoves, (l, r) => new { X1 = l.X, Y1 = l.Y, X2 = r.X, Y2 = r.Y });

当观察者订阅 mouseDiffs 时, 两个 订阅 mouseMoves。您应该改用 Publish 以确保每次订阅 mouseDiffs:

仅订阅一次 mouseMoves
var mouseDiffs = mouseMoves.Publish(obs => obs.Skip(1)
    .Zip(obs, (l, r) => new { X1 = l.X, Y1 = l.Y, X2 = r.X, Y2 = r.Y }));

现在让我们为您的 canvas 定义一个可观察的颜色。请注意,您实际上并不需要 mouseDiffs observable - mouseMove 可以正常工作。

var colors = mouseMove
    .Select(_ => Observable.Concat(
        Observable.Return(true),
        Observable.Return(false).Delay(TimeSpan.FromSeconds(1))))
    .Switch()
    .StartWith(false)
    .DistinctUntilChanged()
    .Select(isActive => isActive ? Color.Green : Color.Red);

您可以 Subscribecolors 并在每次发出 Color.

时设置您的 canvas 颜色