什么是正确的方法来确定接收使用鼠标拖动的结束?鼠标、拖动、方法来、正确

2023-09-09 21:10:43 作者:醉卧云端笑人间

我在慢慢学习如何使用无扩展的.NET 与WPF。有关于它是多么简单写拖放或绘图程序,但他们都是非常简单的几个初学者的例子。我想更进一步,这不是明显对我的正确的方式是什么。

I am slowly learning how to use Reactive Extensions for .NET with WPF. There a few beginner examples about how simple it is to write drag-drop or drawing routines but they are all extremely simple. I'm trying to go one step further and it's not obvious to me what the "proper" way is.

这些例子都说明了如何从的MouseDown 的MouseMove 定义事件流,而的MouseUp

The examples all show how you can define streams of events from MouseDown, MouseMove, and 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 (这将显示坐标从开始拖动点创建到当前鼠标位置的矩形)做的事情

And then how you can easily do things during a MouseDrag (this displays the co-ordinates of the rectangle created from the starting drag point to the current mouse position)

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

我的问题是,什么是正确的方式来完成在鼠标拖动的最后一个任务?本来,我想我可以做这样的事情:

My question is, what is the "proper" way to accomplish a task at the end of the mouse drag? Originally, I thought I could do something like this:

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

阅读更多的文档,不过,似乎 onCompleted 叫时,有没有更多的数据(曾经),当对象可以被释放。

Reading more of the documentation, though, it seems that onCompleted is called when there is no more data (ever) and when the object can be disposed.

这样看来plausable被预订到 mouseUp事件事件,并做一些有第一个选项。

So the first option that seems plausable is subscribing to the mouseUp event and doing something there.

mouseUp.Subscribe(x =>
           {
              // Do stuff here..
           }

但后来在这一点上,我不妨再回到刚才用了正常的MouseLeftButtonUp 事件处理程序。

有另一种方式来确定何时 mouseDrag 是已完成(或当 TakeUntil(mouseUp事件) )发生,并且执行一些动作呢?

Is there another way to determine when the mouseDrag is "completed" (or when the TakeUntil(mouseUp)) occurs and perform some action then?

推荐答案

该序列永远不会完成,因为源(的MouseDown)永远不会完成(这是一个事件)。值得指出的是,一个的IObservable 不能打电话的的onComplete 用户超过一次,这是合同的组成部分( OnNext *(OnCompleted |的OnError)

The sequence never completes because the source (MouseDown) never completes (it is an event). It's worth pointing out that a IObservable cannot call OnComplete of a subscriber more than once, it's part of the contract (OnNext* (OnCompleted|OnError)?).

要找出当 mouseMove.TakeUntil(mouseUp事件)序列完成后,你需要挂接到调用的SelectMany

To find out when themouseMove.TakeUntil(mouseUp) sequence completes, you'll need to hook into the call to 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);
}

然后你可以使用它像这样:

Then you can use it like so:

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

有关为清楚起见,这里是使用底层的扩展方法的LINQ EX pression:

For the interest of clarity, here is the LINQ expression using the underlying extension methods:

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

即,对于从MOUSEDOWN每个值的新的序列将被预订。当是的序列完成后,调用dragComplete()。

That is, for each value from mouseDown a new sequence will be subscribed to. When that sequence completes, call dragComplete().