views:

166

answers:

1

I have the following

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 };

which when I subscribe to it will give me the start point of the drag and the current end point. Now I need to do something once the drag is done. I was unsuccessful in attempting to do this completely with RX and ended up doing

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

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

This works fine until I do a right mouse button drag. Then when I click the left mouse button I get the message box. If I only do a left button drag I get the message box, and then clicking the left mouse button doesn't produce the box. I'd like to do this without the external state, but if nothing else I'd at least like for it to work properly.

FYI: I tried making dragging volatile and using a lock, but that didn't work.

EDIT

It turns out my problem was with a right click context menu. Once I got rid of that my above code worked. So, now my problem is how to I get to have the context menu and still have my code work. I assume the Context menu was handling the left mouse click and that somehow caused my code to not work, but I'm still puzzling it out.

+1  A: 

If drag is all you need, wouldn't a simple .Zip work? :

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));

You can apply Where filter if you need to exclude zero-length drag.

Edit: though, Zip would be vulnerable to releasing the mouse outside the window - in that case, you do not get mouseUp unless you capture... Here's the version that uses only the latest mouseDown before mouseUp:

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();
Sergey Aldoukhov