WPF创建一个可以通过鼠标滚动的控件列表,但仍然保持功能性。
WPF创建一个可以通过鼠标滚动的控件列表,但仍然保持功能性。
我有一个控件列表,通过WrapPanel水平展示。我实现了一个“点击拖动”滚动技术,使得用户可以通过点击和拖动鼠标来进行滚动操作。代码如下:
然后在代码中实现了以下功能:
private Point _mousePosition;
private Point _lastMousePosition;
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
_lastMousePosition = e.GetPosition(ParentCanvas);
e.Handled = true;
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_mousePosition = e.GetPosition(ParentCanvas);
var delta = _mousePosition - _lastMousePosition;
if(e.LeftButton == MouseButtonState.Pressed && delta.X != 0)
{
var transform = ((TranslateTransform)_wrapPanel.RenderTransform).Clone();
transform.X += delta.X;
_wrapPanel.RenderTransform = transform;
_lastMousePosition = _mousePosition;
}
}
这一切都运行良好。但是我想做的是当用户点击拖动时,WrapPanel内部的项不要有响应(即用户只是浏览),但当用户点击(完整的点击)时,它们要有响应。就像iPhone的工作方式一样,当你在应用程序上直接按下并拖动时,它不会打开应用程序,而是滚动屏幕,而当你点击应用程序时,它会打开...
希望我表达清楚了。谢谢!
Mark
问题的出现原因是希望在WPF中创建一个可以通过鼠标滚动的控件列表,但仍然保持功能性。然而,当使用鼠标滚动时,控件列表无法正常工作。
解决方法是通过以下步骤实现:
1. 添加一个PreviewMouseDown事件处理程序,在其中设置Handled=true,并记录包括位置在内的参数,并设置一个"maybeClick"标志(除非设置了"recursion"标志),并设置一个计时器。
2. 添加一个MouseMove事件处理程序,如果鼠标移动距离超过预先记录的PreviewMouseDown位置的一个epsilon值,则清除"maybeClick"标志。
3. 添加一个PreviewMouseUp事件处理程序,检查"maybeClick"标志-如果为true,则设置"recursion"标志,执行InputManager.ProcessInput以重新引发原始的PreviewMouseDown事件,然后清除"recursion"标志。
4. 在计时器中,执行与PreviewMouseUp相同的操作,以使点击事件稍微延迟一会儿。
这样做的效果是延迟PreviewMouseDown事件,直到你有足够时间检查鼠标是否移动。
关于这个解决方法需要注意的一些事项:
- 在PreviewMouseDown事件上设置Handled=true也会停止MouseDown事件的触发。
- 在PreviewMouseDown处理程序中忽略递归调用,因为已设置了递归标志。
问题的原因是希望创建一个可通过鼠标滚动的控件列表,但仍然保持功能。然而,问题在于其他控件(例如按钮)也会尝试捕获鼠标。解决方法是在MouseDown事件中使用e.MouseDevice.Capture(_wrapPanel, CaptureMode.Element)来将鼠标输入定向到_wrapPanel而不是任何子树元素。在MouseUp事件中,通过调用e.Mousedevice.Capture(null)来释放捕获。如果没有发生滚动,您需要向本来应该接收点击的控件发送一个“点击”事件,可以使用自动化对等类来实现。然而,这种实现可能不是最佳的解决方法。在平板电脑和触摸设备上,也存在类似的问题,例如在平板电脑上存在一个名为“按住”(press and hold)的手势,它执行右键单击操作。然而,启用此手势后,无法像正常情况下那样单击和按住控件,因为手势会吞噬鼠标事件。如果手势未完成(即用户在按住持续时间之前松开),Windows必须模拟原始鼠标事件发送给本来应该接收这些事件的控件。虽然这种实现方法可能不是最理想的,但暂时可以使用。