Как правильно определить конец перетаскивания мышью с помощью Rx? - PullRequest
2 голосов
/ 03 декабря 2010

Я медленно учусь, как использовать Reactive Extensions для .NET с WPF.Есть несколько примеров для начинающих о том, как просто писать процедуры перетаскивания или рисования, но все они чрезвычайно просты.Я пытаюсь сделать еще один шаг, и для меня не очевидно, что такое «правильный» путь.

Все примеры показывают, как вы можете определить потоки событий из MouseDown, MouseMove иMouseUp

var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(..., "MouseDown")
                select evt.EventArgs.GetPosition(...);

var mouseMoves = from evt in Observable.FromEvent<MouseEventArgs>(..., "MouseMove")
                 select evt.EventArgs.GetPosition(...);

var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(..., "MouseUp");

И затем, как вы можете легко делать вещи во время MouseDrag (это отображает координаты прямоугольника, созданного от начальной точки перетаскивания до текущей позиции мыши)

var mouseDrag = from start in mouseDown
                from currentPosition in mouseMoves.TakeUntil(mouseUp)
                select new Rect(Math.Min(start.X, currentPosition.X),
                                Math.Min(start.Y, currentPosition.Y),
                                Math.Abs(start.X - currentPosition.X),
                                Math.Abs(start.Y - currentPosition.Y));

mouseDrag.Subscribe(x =>
             {
                 Info.Text = x.ToString();
             });

Мой вопрос: каков «правильный» способ выполнить задачу в конце перетаскивания мышью?Первоначально я думал, что мог бы сделать что-то вроде этого:

mouseDrag.Subscribe(
     onNext: x =>
             {
                 Info.Text = x.ToString();
             },
     onCompleted: () =>
              {
                 // Do stuff here...except it never gets called
              });

Чтение большей части документации, хотя, кажется, что onCompleted вызывается, когда больше нет данных (никогда) и когда объект можетбыть уничтоженным.

Таким образом, первый вариант, который кажется правдоподобным, - это подписаться на событие mouseUp и что-то там делать.просто использовать «нормальный» обработчик событий MouseLeftButtonUp.

Есть ли другой способ определить, когда mouseDrag «завершен» (или когда TakeUntil(mouseUp)) происходит, и выполнить какое-либо действие тогда?

1 Ответ

5 голосов
/ 03 декабря 2010

Последовательность никогда не завершается, потому что источник (MouseDown) никогда не завершается (это событие).Стоит отметить, что IObservable не может вызвать OnComplete абонента более одного раза, это часть контракта (OnNext* (OnCompleted|OnError)?).

Чтобы узнать, когда последовательность mouseMove.TakeUntil(mouseUp) завершится, выВам нужно будет подключиться к вызову SelectMany:

public static IDisposable TrackDrag(this UIElement element, 
    Action<Rect> dragging, Action dragComplete)
{
    var mouseDown = Observable.FromEvent(...);
    var mouseMove = Observable.FromEvent(...);
    var mouseUp = Observable.FromEvent(...);

    return (from start in mouseDown
            from currentPosition in mouseMove.TakeUntil(mouseUp)
                    .Do(_ => {}, () => dragComplete())
            select new Rect(Math.Min(start.X, currentPosition.X),
                            Math.Min(start.Y, currentPosition.Y),
                            Math.Abs(start.X - currentPosition.X),
                            Math.Abs(start.Y - currentPosition.Y));
            ).Subscribe(dragging);
}

Тогда вы можете использовать его следующим образом:

element.TrackDrag(
    rect => { },
    () => {}
);

Для ясности, вот выражение LINQиспользуя основные методы расширения:

return mouseDown.SelectMany(start =>
{
    return mouseMove
        .TakeUntil(mouseUp)
        .Do(_ => {}, () => dragComplete())
        .Select(currentPosition => new Rect(...));
})
.Subscribe(dragging);

То есть для каждого значения mouseDown будет подписана новая последовательность.Когда эта последовательность завершится, вызовите dragComplete ().

...