Создание наблюдаемого перетаскивания мышью с помощью Reactive Extensions

У меня есть следующие

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

Это работает нормально, пока я не перетащу правой кнопкой мыши. Затем, когда я щелкаю левой кнопкой мыши, я получаю окно сообщения. Если я перетаскиваю только левую кнопку, я получаю окно сообщения, а затем щелчок левой кнопкой мыши не приводит к появлению окна. Я бы хотел сделать это без внешнего состояния, но, по крайней мере, я бы хотел, чтобы он работал правильно.

К вашему сведению: я пытался сделать перетаскивание нестабильным и использовать блокировку, но это не сработало.

ИЗМЕНИТЬ

Оказывается, моя проблема была в контекстном меню, вызываемом правой кнопкой мыши. Как только я избавился от этого, мой приведенный выше код заработал. Итак, теперь моя проблема в том, как мне получить контекстное меню и по-прежнему работать мой код. Я предполагаю, что контекстное меню обрабатывало щелчок левой кнопкой мыши, и это почему-то привело к тому, что мой код не работал, но я все еще ломаю голову.


person juharr    schedule 23.04.2010    source источник
comment
Я задал очень похожий вопрос: stackoverflow.com/questions/2574046/ - помогает ли ответ сергея?   -  person Rob Fonseca-Ensor    schedule 23.04.2010
comment
@Rob, это близко, но в моем случае я не хочу знать, когда они просто щелкали мышью, не перетаскивая, где, как в вашем коде, создавали бы эллипс в этом месте и меняли его цвет. Я посмотрю, смогу ли я включить Publish and Connect, чтобы заставить это работать.   -  person juharr    schedule 23.04.2010


Ответы (1)


Если перетаскивание - это все, что вам нужно, разве не подойдет простой .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));

Вы можете применить фильтр «Где», если вам нужно исключить перетаскивание нулевой длины.

Изменить: тем не менее, Zip будет уязвим для отпускания мыши за пределами окна - в этом случае вы не получите mouseUp, если вы не захватите ... Вот версия, которая использует только последнюю версию mouseDown перед 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();
person Sergey Aldoukhov    schedule 25.04.2010
comment
Я возился с этим, и я не могу понять, что _mouseDown и _mouseUp в вашем примере. Кроме того, MouseButtonEventArgs, похоже, тоже не существует. Может, это просто старые примеры кода? - person BrianLegg; 18.03.2020