C# 在WPF用户控件上使用Rx over事件,当窗口最大化时,为什么控件会收到mousedown和mousemove?
这个让我有点困惑 我已经在UIElement上编写了一些扩展方法,以提供一些鼠标事件的可观察性。以下是相关信息:C# 在WPF用户控件上使用Rx over事件,当窗口最大化时,为什么控件会收到mousedown和mousemove?,c#,wpf,system.reactive,maximize,C#,Wpf,System.reactive,Maximize,这个让我有点困惑 我已经在UIElement上编写了一些扩展方法,以提供一些鼠标事件的可观察性。以下是相关信息: public static IObservable<EventPattern<MouseEventArgs>> ObserveMouseLeftButtonDown(this UIElement element) { return Observable.FromEventPattern<MouseEventArgs>(element, "M
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");
}
然后是合成物本身:
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);
}
public static IObservable ObserveMouseDrag(此UIElement)
{
返回可观测的(
element.ObserveMouseLeftButtonDown().Select(ep=>ep.EventArgs.GetPosition(元素)),
元素。ObserveMouseMove()。其中(ep=>ep.EventArgs.LeftButton==MouseButtonState.Pressed),
(md,mm)=>new{Down=md,Move=mm})
.Where(i=>MinimumDragSeen(i.Down,i.Move.EventArgs.GetPosition(元素)))
.选择(i=>i.Move);
}
这一切都很好地工作,在经历了大量的磨牙和烦恼之后,没有任何基于Rx的WPF控件拖放的好例子(大多数都是在画布上拖动图像的简单复制品)
当窗口最大化时会出现问题,特别是双击标题栏。如果在最大化布局中,订阅了自己的ObserveMouseDrag()的控件中的一个在鼠标光标下结束,它将收到一个MouseLeftButtonDown和一个MouseMove,其中一个明显在最大化之前,另一个在最大化之后。最终的结果是,它启动了一个拖动事件,当鼠标按钮被释放时,该事件立即停止,然后趋向于使控件自身掉落,这在这个应用程序中的某些情况下实际上起到了作用
这是非常奇怪的,因为我不明白为什么我应该接收由双击最大化引起的MouseDown和MouseMove。当然,其中涉及的所有鼠标事件都应该由Windows处理,因为我没有使用自定义窗口边框或类似的东西
那么,有人有什么想法吗
第二天…
修好了!(以下是李的回答,还有这个问题:)
代码现在如下所示:
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)
);
}
public static IObservable ObserveMouseDrag(此UIElement)
{
var mouseDown=element.ObserveMouseLeftButtonDown().Select(ep=>ep.EventArgs.GetPosition(element));
var mouseMove=element.ObserveMouseMove();
var stop=可观察的。合并(
元素。ObserveMouseUp(),
元素。ObserveMouseLeave()。其中(ep=>ep.EventArgs.LeftButton==MouseButtonState.Pressed)
);
返回mouseDown.SelectMany(
md=>mouseMove
.Where(ep=>ep.EventArgs.LeftButton==MouseButtonState.Pressed)
.Where(ep=>MinimumDragSeen(md,ep.EventArgs.GetPosition(元素)))
.TakeUntil(停止)
);
}
并处理最小拖动距离、鼠标向上移动事件以及拖动完全正常工作所需的各种事情。最大化错误也消失了(虽然我仍然不完全理解这个错误,但我怀疑鼠标上的操作可能与此有关)
这里的关键是使用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;
这将只在鼠标落下时开始移动,并在鼠标上升时立即将其杀死。我认为你的代码中没有MouseUp的特性
我也会考虑避免存储元素并将其用作状态,因为我认为这可以通过事件ARG获得。我也会考虑在MuSeMeo上使用Zip,这样你就可以得到最后一个鼠标移动变化的增量(如果合适的话)。 我很想知道你在做什么。我正在写一本关于RX的书,在工作楼里,我刚刚完成了一些使用拖放的WPF控件。
希望有帮助Lee你说得对,我们没有控制鼠标,这确实导致了一个我们可以演示的错误。最好对此进行修复,问题是元素本身上的MouseLeftButtonUp不够,因为MouseLeftButtonUp可能在其他地方触发。最终,如果鼠标被释放到任何地方,甚至在应用程序之外,它都需要触发,以便正确重置拖动。您的代码有一个有趣的错误,在第二次拖动操作中,where子句似乎总是正确的,在可观察到的触发之前,没有观察到的最小拖动距离,然而这是第一次。我怀疑它实际上并没有完全重置,仍然使用它看到的第一个鼠标向下移动。至于我们正在做的事情——在一个网格状的布局中拖放条目,代表一个时间表。我接受这个答案,因为它让我朝着正确的方向前进,尽管这并不完全是我所需要的。非常感谢!我也很想听听你关于Rx的书,尤其是如果你要谈论比无处不在的“拖动图像”例子更复杂的事情。自从我使用DoDragDrop后,我的解决方案有点不同,但这个答案很有帮助。此外,我还做了一些关于MinimumDragSeen方法的家庭作业,显然它并不是那么简单。底层windowsapi调用的规范说,这些数字可以是负数。我最终发现了这一点,我认为这是非常确定的。我的C版本:var rc=new Rect(开始,开始);充气(系统参数.最小水平阻力距离,系统参数.最小垂直阻力距离);回来!rc.Contains(现在);
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;