比较鼠标移动事件流,鼠标位置不会减少

本文关键字:鼠标 位置 移动 事件 比较 | 更新日期: 2023-09-27 18:30:30

我有一个Windows窗体应用程序,我正在使用Rx查询MouseMove事件流,并根据鼠标的位置(当前和以前)生成结果。我的代码如下所示:(标签 1 显示结果,标签 2 显示包含当前和上一个位置的"日志"。

        var observable = Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove");
        observable                
            .PairWithPrevious()
            .Select(tuple =>
                    {
                        if (tuple.Item2 == null || tuple.Item1 == null) return Tuple.Create(0, 0);
                        var currPosition = tuple.Item2.EventArgs.Location;
                        var prevPosition = tuple.Item1.EventArgs.Location;
                        var x = 0;
                        if (currPosition.X > prevPosition.X) x = 1;
                        else if (currPosition.X < prevPosition.X) x = -1;
                        var y = 0;
                        if (currPosition.Y > prevPosition.Y) y = 1;
                        else if (currPosition.Y < prevPosition.Y) y = -1;
                        label2.Text = string.Format("Curr X: {0} * Y: {1} |||| Prev X: {2} * Prev Y: {3}", currPosition.X, currPosition.Y, prevPosition.X, prevPosition.Y);
                        return Tuple.Create(x, y);
                    })
            .Subscribe(x =>
                       {
                           label1.Text = string.Format("X: {0} ** Y: {1}", x.Item1, x.Item2);
                       });

PairWithPrevious 函数:

    public static IObservable<Tuple<TSource, TSource>> PairWithPrevious<TSource>(this IObservable<TSource> source)
    {
        return source.Scan(
            Tuple.Create(default(TSource), default(TSource)),
            (acc, current) => Tuple.Create(acc.Item2, current));
    }

问题是:当您向左或向上移动鼠标时,前一个和当前 (x, y) 是相同的值,结果 (label1) 永远不会显示"-1"值。

我尝试使用Zip函数来比较以前的值和当前值,但结果是相同的:

      Zip(observable.Skip(1), Tuple.Create)

那么,为什么会出现这种情况?以及如何解决它?

比较鼠标移动事件流,鼠标位置不会减少

如果您观察 MouseMove 事件触发的频率,您将看到导致问题的原因。我已经更新了您的代码以包含计数。 我还添加了一个包含三列的简单列表视图,并将列表视图的视图设置为详细信息,使其非常明显。

我的一些代码与你的不同。 这主要是一个风格问题。 但是我将PairWithBefore更改为WithPrior并隐藏了元组

//http://www.zerobugbuild.com/?p=213 with a tweak to hide the Tuple
public static IObservable<TResult> WithPrevious<TSource, TResult>(this IObservable<TSource> source, Func<TSource, TSource, TResult> projection)
{
    return source.Scan(Tuple.Create(default(TSource), default(TSource)),
                       (previous, current) => Tuple.Create(previous.Item2, current))
                 .Select(t => projection(t.Item1, t.Item2));
}

RX Select 运算符的重载为 int,它只是一个计数器。 我将使用它来添加到匿名对象以向下链接。 WithPrevious 现在只是投影到另一个包含我们所需数据的匿名对象。 然后在订阅中,我们将更新两个标签和新的列表视图。

var observable = Observable.FromEventPattern<MouseEventArgs>(this, "MouseMove")
                                   .Select((eventPattern, count) => new {eventPattern.EventArgs, Count = count})
                                   .WithPrevious((previous, current) =>
                                       {
                                           var currentPoint = current == null
                                                                  ? new Point(0, 0)
                                                                  : current.EventArgs.Location;
                                           var previousPoint = previous == null
                                                                   ? new Point(0, 0)
                                                                   : previous.EventArgs.Location;
                                           return
                                               new
                                                   {
                                                       CurrentPoint = currentPoint,
                                                       PreviousPoint = previousPoint,
                                                       Count = current == null ? 0 : current.Count
                                                   };
                                       })
                                   .Subscribe(a =>
                                       {
                                           label2.Text =
                                               String.Format(
                                                   "Curr X: {0} * Y: {1} |||| Prev X: {2} * Prev Y: {3} - Count = {4}",
                                                   a.CurrentPoint.X, a.CurrentPoint.Y,
                                                   a.PreviousPoint.X, a.PreviousPoint.Y,
                                                   a.Count);

                                           label1.Text = String.Format("X: {0} ** Y: {1}",
                                                                       a.CurrentPoint.X.CompareTo(
                                                                           a.PreviousPoint.X),
                                                                       a.CurrentPoint.Y.CompareTo(
                                                                           a.PreviousPoint.Y));
                                           var listItem = new ListViewItem(a.Count.ToString());
                                           listItem.SubItems.Add(a.CurrentPoint.X.ToString());
                                           listItem.SubItems.Add(a.CurrentPoint.Y.ToString());
                                           listView1.Items.Add(listItem);
                                           listView1.EnsureVisible(listView1.Items.Count - 1);
                                       });
您现在可以看到,如果您将鼠标向下或向

右移动缓慢,事件只会触发一次,但是,在我的计算机上,如果您向上或向左移动鼠标,我可以获得的最小事件数是三个,最后一个总是与前一个相同。 这就是为什么它总是零,零。

现在,如果我们添加.DistinctUntilChanged(a => a.CurrentPoint) 紧接在 WithPrevious 之后和订阅之前,您可以获得更多您想要的东西。 我说大多数是因为移动鼠标对角线在我的电脑上的事件永远不会有 X 和 Y 不同。 您也许可以使用 Throttle - 类似 .Throttle(TimeSpan.FromMilliseconds(25),CurrentThreadScheduler.Instance)在选择之后和之前与Previous。 这对我的电脑有帮助,但并不完美。