使用Reactive Extensions创建可观察的鼠标拖动

时间:2010-04-23 15:47:22

标签: c# system.reactive

我有以下

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

var leftMouseDragging = from down in leftMouseDown
                        let startPoint = down.EventArgs.GetPosition(displayCanvas)
                        from move in mouseMove.TakeUntil(leftMouseUp)
                        let endPoint = move.EventArgs.GetPosition(displayCanvas)
                        select new { Start = startPoint, End = endPoint };

当我订阅它时,它会给我拖动的起点和当前的终点。现在我需要在拖动完成后做一些事情。我试图用RX完全做到这一点并没有成功,最后做了

leftMouseDragging.Subscribe(value=>
    {
        dragging = true;
        //Some other code
    });

leftMouseUp.Subscribe(e=>
    {
        if(dragging)
        {
            MessageBox.Show("Just finished dragging");
            dragging = false;
        }
    });

这样可以正常工作,直到我拖动鼠标右键。然后,当我单击鼠标左键时,我会看到消息框。如果我只做一个左键拖动我得到消息框,然后单击鼠标左键不会产生该框。我想在没有外部状态的情况下这样做,但如果没有别的,我至少希望它能够正常工作。

仅供参考:我尝试拖动易失性并使用锁定,但这不起作用。

修改

事实证明我的问题是右键单击上下文菜单。一旦我摆脱了我的上述代码工作。所以,现在我的问题是如何获得上下文菜单并仍然使我的代码工作。我假设上下文菜单处理鼠标左键并且不知何故导致我的代码无法正常工作,但我仍然感到困惑。

1 个答案:

答案 0 :(得分:5)

如果只需要拖动,那么简单的.Zip不会起作用吗? :

var drag = _mouseDown
    .Select(args => args.EventArgs.GetPosition(canvas))
    .Zip(_mouseUp.Select(args => args.EventArgs.GetPosition(canvas)), 
        (p1, p2) => new { p1, p2 });
drag.Subscribe(p1p2 => 
        Console.WriteLine(@"({0}:{1})({2}:{3})", 
            p1p2.p1.X, p1p2.p1.Y, p1p2.p2.X, p1p2.p2.Y));

如果需要排除零长度拖动,可以应用Where过滤器。

编辑:但是,Zip很容易在窗口外释放鼠标 - 在这种情况下,除非你捕获,否则你不会得到mouseUp ... 这是在mouseUp:

之前仅使用最新mouseDown的版本
var drag = _mouseDown
    .Select(args => args.EventArgs.GetPosition(canvas))
    .TakeUntil(_mouseUp)
    .CombineLatest(_mouseUp
        .Select(args => args.EventArgs.GetPosition(canvas))
        .Take(1), (p1, p2) => new { p1, p2 })
    .Repeat();