使用 Rx 确定鼠标拖动结束的正确方法是什么?

发布于 2024-10-05 20:48:53 字数 2045 浏览 0 评论 0原文

我正在慢慢学习如何将 Reactive Extensions for .NET 与 WPF 结合使用。有一些初学者示例说明编写拖放或绘图例程是多么简单,但它们都非常简单。我试图更进一步,但我不清楚什么是“正确”的方法。

这些示例都展示了如何定义来自 MouseDownMouseMoveMouseUp 的事件流,

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

然后如何在 MouseDownMouseMoveMouseUp 中轻松执行操作code>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 事件并在那里执行某些操作。

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

但此时,我也可以返回使用“正常”MouseLeftButtonUp 事件处理程序。

是否有另一种方法可以确定 mouseDrag 何时“完成”(或 TakeUntil(mouseUp) 何时发生)并执行某些操作?

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.

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

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

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.

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

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

But then at this point, I may as well go back to just use the "normal" MouseLeftButtonUp event handler.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

黎夕旧梦 2024-10-12 20:48:53

该序列永远不会完成,因为源 (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()。

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)?).

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

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文