在 WPF 中,我希望能够使用具有反应式扩展的鼠标事件来为 UIElement 创建一个类似于 Click 事件的可观察对象。有很多使用它来创建拖放行为的示例,但我找不到任何只需简单点击的东西。
我预计它将涉及 MouseLeftButtonDown、MouseLeftButtonUp、MouseLeave 和 MouseEnter 上的可观察量。但我不确定需要使用 Merge、SelectMany、TakeUntil 或 TakeWhile 的哪种组合。在尝试将其全部包装在扩展中时,这是我到目前为止所得到的:
public static IDisposable GetClick(this UIElement item, Action clickAction)
{
var obs1 = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
h => (s, e) => h(s, e),
h => item.MouseLeftButtonDown += h,
h => item.MouseLeftButtonDown -= h);
var obs2 = Observable.FromEventPattern<MouseButtonEventHandler, MouseButtonEventArgs>(
h => (s, e) => h(s, e),
h => item.MouseLeftButtonUp += h,
h => item.MouseLeftButtonUp -= h);
var obs3 = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => (s, e) => h(s, e),
h => item.MouseLeave += h,
h => item.MouseLeave -= h);
var obs4 = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
h => (s, e) => h(s, e),
h => item.MouseEnter += h,
h => item.MouseEnter -= h);
var finalObs = ???
return finalObs.Subscribe(x => clickAction.Invoke());
}
最佳答案
以下似乎可行,但我怀疑可以用更简洁的方式来实现。
var click = mouseEnter
.SelectMany(_ => mouseDown.TakeUntil(mouseLeave))
.SelectMany(_ => mouseUp.TakeUntil(mouseLeave).Take(1));
我已将 finalObs
重命名为 click
、obs1
重命名为 mouseDown
、obs2
> 到 mouseUp
...
编辑:添加了 Take(1) 以修复 Enigmativity 指出的缺陷
编辑(2):
这是我更喜欢的另一个解决方案。
您需要将 .Select(_ => "U")
添加到 mouseUp 的定义中,.Select(_ => "D")
鼠标按下...
var click = Observable.Merge(mouseDown, mouseUp, mouseLeave, mouseEnter)
.Scan((s, c) => c == "L" ? "" : s + c) // Create a string of the events, reset on mouseLeave
.Where(s => s.Length >= 2 && s.Substring(s.Length - 2) == "DU");
经过思考,在用户将鼠标放在项目上,然后移到项目外部,然后向后移动并抬起鼠标的情况下,不可能获得完全正确的行为。这是因为当鼠标不在该项目上方时,您不会获得鼠标向上移动的信息,因此您无法确定它们是否没有在项目外时向上移动鼠标,然后向下移动鼠标。
关于c# - 使用Reactive Extensions模拟UIElement的点击,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22490646/