我正在慢慢学习如何在WPF中使用Reactive Extensions for .NET。有一些初学者的例子说明编写拖放或绘图例程是多么简单,但它们都非常简单。我试图更进一步,对我而言,“正确”的方式是什么并不明显。
这些示例都展示了如何定义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
事件并在那里做一些事情。
mouseUp.Subscribe(x =>
{
// Do stuff here..
}
但是在这一点上,我可以回到使用“普通”MouseLeftButtonUp
事件处理程序。
还有另一种方法可以确定mouseDrag
何时“完成”(或TakeUntil(mouseUp)
)何时发生并执行某些操作?
答案 0 :(得分:5)
序列永远不会完成,因为源(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()。