这让我有点困惑。
我在 UIElement 上编写了一些扩展方法来为某些鼠标事件提供 Observables。以下是相关的:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseLeftButtonDown(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseLeftButtonDown");
}
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseMove(this UIElement element)
{
return Observable.FromEventPattern<MouseEventArgs>(element, "MouseMove");
}
到目前为止,如此简单。然后我创建一个复合事件来检测用户何时开始在 UIElement 上拖动(这全部由我的自定义控件使用,其确切性质并不是特别相关)。首先这里有一个小辅助函数来查看用户是否拖动了最小拖动距离:
private static bool MinimumDragSeen(Point start, Point end)
{
return Math.Abs(end.X - start.X) >= SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(end.Y - start.Y) >= SystemParameters.MinimumVerticalDragDistance;
}
然后是复合可观察对象本身:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
return Observable.CombineLatest(
element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element)),
element.ObserveMouseMove().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed),
(md, mm) => new { Down = md, Move = mm })
.Where(i => MinimumDragSeen(i.Down, i.Move.EventArgs.GetPosition(element)))
.Select(i => i.Move);
}
这一切都很好,经过很多咬牙切齿和烦恼,没有任何像样的基于 Rx 的 WPF 控件拖放示例(其中大多数是在画布中拖动图像的天真简单的副本)。
当窗口最大化时出现问题,特别是通过双击标题栏。如果在最大化布局中,订阅了自己的 ObserveMouseDrag() 的控件之一最终在鼠标光标下,它会收到一个 MouseLeftButtonDown 和一个 MouseMove,其中一个显然在最大化之前,另一个在之后。最终结果是它启动了一个拖动事件,然后在释放鼠标按钮时立即停止,然后往往会导致控件掉落到自身上,在这个应用程序的某些情况下,这实际上做了一些事情。
这非常奇怪,因为我不明白为什么我应该收到由双击最大化引起的 MouseDown 和 MouseMove。当然,所有涉及的鼠标事件都应该由 Windows 处理,因为我没有使用自定义窗口边框或类似的东西。
那么,有人有什么想法吗?
第二天...
修复!(在下面 Lee 的回答和这个问题的帮助下:什么是使用 Rx 确定鼠标拖动结束的正确方法?)
代码现在如下所示:
public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseDrag(this UIElement element)
{
var mouseDown = element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element));
var mouseMove = element.ObserveMouseMove();
var stop = Observable.Merge(
element.ObserveMouseUp(),
element.ObserveMouseLeave().Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
);
return mouseDown.SelectMany(
md => mouseMove
.Where(ep => ep.EventArgs.LeftButton == MouseButtonState.Pressed)
.Where(ep => MinimumDragSeen(md, ep.EventArgs.GetPosition(element)))
.TakeUntil(stop)
);
}
并处理最小拖动距离、鼠标向上事件以及拖动完全正常工作所需的各种事情。最大化错误也消失了(虽然我仍然不完全理解那个错误,但我怀疑鼠标向上处理可能与它有关)。
这里的关键是使用 SelectMany 处理从 mouseMove 到控件中的 mouseUp 或鼠标指针离开它并按下鼠标按钮的多个事件流。