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

我正在慢慢学习如何使用WPF的.NET Reactive Extensions 。 有一些初学者的例子说明编写拖放或绘图例程是多么简单,但它们都非常简单。 我试图更进一步,对我来说,“正确”的方式是什么并不明显。

这些示例都展示了如何从MouseDownMouseMoveMouseUp定义事件流

 var mouseDown = from evt in Observable.FromEvent(..., "MouseDown") select evt.EventArgs.GetPosition(...); var mouseMoves = from evt in Observable.FromEvent(..., "MouseMove") select evt.EventArgs.GetPosition(...); var mouseUp = Observable.FromEvent(..., "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事件并在那里做一些事情。

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

但是在这一点上,我不妨回过头来使用“正常”的MouseLeftButtonUp事件处理程序。

有没有其他方法可以确定mouseDrag “完成”(或TakeUntil(mouseUp) )何时发生并执行某些操作呢?

序列永远不会完成,因为源(MouseDown)永远不会完成(它是一个事件)。 值得指出的是, IObservable不能多次调用订户的OnComplete ,它是合同的一部分( OnNext* (OnCompleted|OnError)? )。

要找出mouseMove.TakeUntil(mouseUp)序列何时完成,您需要挂钩对SelectMany的调用:

 public static IDisposable TrackDrag(this UIElement element, Action 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()。