Wpf 反应式扩展(Rx)-仅在按下键时订阅鼠标位置数据
在WPF中,我只想在按下键时订阅鼠标位置点。然后,我只想在释放关键点时(即,当我有完整的捕获点集时)将捕获的点设置到属性上,并继续侦听下一个向下/向上组合键,以创建另一个鼠标位置捕获,等等 我对上面的解释是,我需要在按键按下时触发一个序列,在按键释放时停止执行,但我希望OnNext接收一组鼠标点 通过大量的阅读(我是Rx新手),我总结了以下伪/实样本:Wpf 反应式扩展(Rx)-仅在按下键时订阅鼠标位置数据,wpf,events,system.reactive,Wpf,Events,System.reactive,在WPF中,我只想在按下键时订阅鼠标位置点。然后,我只想在释放关键点时(即,当我有完整的捕获点集时)将捕获的点设置到属性上,并继续侦听下一个向下/向上组合键,以创建另一个鼠标位置捕获,等等 我对上面的解释是,我需要在按键按下时触发一个序列,在按键释放时停止执行,但我希望OnNext接收一组鼠标点 通过大量的阅读(我是Rx新手),我总结了以下伪/实样本: var keyDownSeq = Observable.FromEvent(...); var keyUpSeq = Observable.Fr
var keyDownSeq = Observable.FromEvent(...);
var keyUpSeq = Observable.FromEvent(...);
var mouseMoveSeq = Observable.FromEvent(...);
var mouseMovesWhileKeyDown = keyDownSeq
.Where(keyEventArgs => keyEventArgs.IsRepeat == false) //WPF fires the same KeyDown repeatedly
.Where(keyEventArgs => keyEventArgs.Key == Key.Space)
.Select(_ => mouseMoveSeq
.TakeUntil(keyUpSeq)
.ToList())
.Subscribe(listOfMousePoints => MyProperty = listOfMousePoints);
- 将局部变量设置为Select()中的KeyDown序列值
- 当KeyUpSeq遇到相同的键时,将局部变量重置为null
- 过滤KeyDownSeq以忽略此变量有值时的所有值
- 过滤KeyUpSeq以忽略与局部变量不匹配的所有KeyUp值
Rx有这样一个局部状态变量的概念吗?我认为有两种方法可以简化设置。第一种方法是创建一个
IObservable
,当您的按键在向下或向上位置之间切换时,它会准确地发出信号
//true means key down, false means key up
IObservable<bool> keyChange =
Observable.Merge(
Observable.FromEvent(/*keyDown*/).Select(_ => true),
Observable.FromEvent(/*keyUp*/).Select(_ => false))
.DistinctUntilChanged();
第二种方法是使用Observable.Window
在向下键“窗口打开”和向上键“窗口关闭”之间拉出连续的鼠标移动序列
IObservable mouseMoves=Observable.FromEvent(…);
IObservable mousepath=mouseMoves.Window(
keyChange.Where(b=>b),
_=>keyChange.Where(b=>!b));
所用方法的文件:
窗口的签名一开始可能有点吓人,但一旦你理解了它,它的使用就相当简单了。你必须小心这里的比赛条件。
有一些设置
IObservable<Unit> keyDown = Observable.FromEvent(/*keydown*/).Select(_=>true);
IObservable<Unit> keyUp = Observable.FromEvent(/*keyup*/).Select(_=>false);
IObservable<Point> mouseMoves = Observable.FromEvent(...);
使用此可观察对象创建窗口
IOBservable<IObservable<Point> mousePaths =
mouseMoves
.Window(keys)
.Where((_,i)=>i%2==0);
IOBservablei%2==0);
请注意,我们希望跳过奇数窗口。甚至连窗户都关上了。奇怪的窗户都关上了
现在没有比赛条件,我们将永远不会错过一个按键或按键事件
解释如果您使用窗口的另一个重载,即具有单独的打开和关闭触发器的重载,问题在于关闭触发器生成是延迟的。在触发窗口的打开边缘之前,它不会生成,也不会订阅其源。这意味着在窗口关闭触发器注册之前,有一个很小的时间窗口可以发生按键事件。谢谢-非常有用,但我有几个问题:1.如何处理两个键被按下,然后一个键被释放的情况?keyChange序列将再次显示false,否?2.我是否正确地认为我可以订阅mousepath,然后在窗口化的IObservable上的OnNext调用ToList(),这样我就可以将我的属性设置为此列表?3.看起来不像,但缓冲区是否有OpeningSelector和ClosingSelector重载?我这样问是因为缓冲区给了我一个我想要的IObservable。@Julius 1。两把钥匙怎么样?我不明白这个问题。也许可以单独制作一个,然后在这里发布链接?2.是的,这正是您对Window的结果所做的。3.缓冲区在这里也可以工作。行为上的区别如下。窗口将在每个窗口启动时发出一个可观察的窗口,每个窗口将在源生成内容时发出其内容。缓冲区将在其末尾发出每个“窗口”,所有窗口都作为列表一次发出。当你想进行批处理时,缓冲区很有用。这里没有竞争条件吗<代码>向下键
导致窗口打开。调用选择器并\u=>keyChange。其中(b=>!b)
被调用以注册窗口关闭观察值。但是,由于keyChange
是热的,因此在按下键和注册车窗关闭检测之间可能存在一个keyUp事件。基本上,在这种情况下,窗口将保持打开状态。我已经发布了一个正确的答案,下面没有种族条件。我对上述答案的回答是-1,因为我认为如果按原样使用,可能会导致很难发现错误。@BradGonessurfing不恰当地使用downvote。您复制了我的答案,只做了一次修改,很明显,您使用了相同的变量名。我非常感谢你的修改,因为你在这个答案中对问题提出了一个很好的观点,而且我也同意因为这个更正应该将它标记为答案。但我的答案仍然很有用,特别是因为我把提问者带到了解决方案的90%。这里唯一的错误是此窗口的细微行为
重载。SkipUntil将跳过所有值,直到源序列生成true。然后它将永远产生价值。”真,假,真,假,真,假'永远交替。如果您愿意,可以将其解读为SkipUntil(value=>value==true)
在本例中,窗口选择器用于关闭和打开这就是为什么我们以应该打开它的项开始序列。下一个将关闭它。之后的下一个将打开它,以此类推。链接在这里,但我误读了文件。该方法关闭,然后在发生边界事件时立即打开一个新窗口。修复它很容易。我现在就做。是的,你应该能做到
IObservable<Unit> keyDown = Observable.FromEvent(/*keydown*/).Select(_=>true);
IObservable<Unit> keyUp = Observable.FromEvent(/*keyup*/).Select(_=>false);
IObservable<Point> mouseMoves = Observable.FromEvent(...);
var keys = keyDown.Merge(keyUp).DistinctUntilChanged().SkipWhile(_=>!_);
IOBservable<IObservable<Point> mousePaths =
mouseMoves
.Window(keys)
.Where((_,i)=>i%2==0);