Jed*_*dja 2 c# wpf .net-4.0 system.reactive
我正在慢慢学习如何使用WPF的.NET Reactive Extensions.有一些初学者的例子说明编写拖放或绘图例程是多么简单,但它们都非常简单.我试图更进一步,对我而言,"正确"方式是什么并不明显.
这些例子都告诉你如何从定义事件流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");
Run Code Online (Sandbox Code Playgroud)
然后如何在a期间轻松完成任务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();
});
Run Code Online (Sandbox Code Playgroud)
我的问题是,在鼠标拖动结束时完成任务的"正确"方法是什么?最初,我以为我可以这样做:
mouseDrag.Subscribe(
onNext: x =>
{
Info.Text = x.ToString();
},
onCompleted: () =>
{
// Do stuff here...except it never gets called
});
Run Code Online (Sandbox Code Playgroud)
但是,阅读更多文档时,似乎onCompleted在没有更多数据(永远)以及可以处理对象时调用.
因此,看起来可行的第一个选择是订阅mouseUp事件并在那里做一些事情.
mouseUp.Subscribe(x =>
{
// Do stuff here..
}
Run Code Online (Sandbox Code Playgroud)
但是在这一点上,我不妨回过头来使用"普通" MouseLeftButtonUp事件处理程序.
是否有另一种方法来确定何时mouseDrag"完成"(或何时TakeUntil(mouseUp))发生并执行某些操作呢?
序列永远不会完成,因为源(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);
}
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
element.TrackDrag(
rect => { },
() => {}
);
Run Code Online (Sandbox Code Playgroud)
为了清楚起见,这里是使用底层扩展方法的LINQ表达式:
return mouseDown.SelectMany(start =>
{
return mouseMove
.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
.Select(currentPosition => new Rect(...));
})
.Subscribe(dragging);
Run Code Online (Sandbox Code Playgroud)
也就是说,对于来自mouseDown的每个值,将订阅新序列.当该序列完成后,调用dragComplete().