- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
这个让我有点困惑。
我已经在 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 控件拖放的像样的例子(它们中的大多数是在 Canvas 中拖动图像的天真简单的复制品) .
问题出现在窗口最大化时,特别是双击标题栏时。如果在最大化布局中,我的一个订阅了它自己的 ObserveMouseDrag() 的控件最终在鼠标光标下方,它会收到一个 MouseLeftButtonDown 和一个 MouseMove,其中一个显然在最大化之前,另一个在最大化之后。最终结果是它启动了一个拖动事件,然后在释放鼠标按钮时立即停止,然后往往会导致控件落到自身上,在某些情况下,这个应用程序实际上做了一些事情。
这非常奇怪,因为我不明白为什么我应该收到由双击最大化引起的 MouseDown 和 MouseMove。当然,所有涉及的鼠标事件都应该由 Windows 处理,因为我没有使用自定义窗口边框或类似的东西。
那么,有人有什么想法吗?
第二天...
修复了! (在下面李的回答和这个问题的帮助下:What is the proper way to determine the end of a mouse drag using 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 或鼠标指针在按下鼠标按钮时离开控件为止。
最佳答案
听起来这可能是一个错误,因为我不确定为什么当您最大化窗口时鼠标先移动。
我会使用 selectMany + takeuntil 而不是 combine latest。即:
var mouseDown = element.ObserveMouseLeftButtonDown().Select(ep => ep.EventArgs.GetPosition(element));
var mouseUp = element.ObserveMouseLeftButtonUp();
var mouseMove = element.ObserveMouseMove();
var from md in mouseDown
from mm in mouseMove.TakeUntil(mouseUp)
where MinimumDragSeen(md, mm.EventArgs.GetPosition(element)))
select mm;
这现在只会在鼠标按下时启动一个 Action ,并在鼠标抬起时立即杀死它。我认为您的代码中没有 MouseUp 功能。
我还会考虑尝试避免存储元素并将其用作状态,因为我认为这可以通过事件参数获得。我还会考虑在 MouseMove 上使用 Zip,这样您就可以获得上次鼠标移动变化的增量(如果适用)。
我真的很想知道你在做什么。我正在写一本关于 Rx 的书,并且在工作中我刚刚完成了一些使用拖放的 WPF 控件的构建。
希望对你有帮助
李
关于c# - 在 WPF UserControl 上使用 Rx over 事件,为什么控件会在窗口最大化时收到 mousedown 和 mousemove?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9263510/
我是一名优秀的程序员,十分优秀!