在WPF中,Canvas上的OnMouseMove事件不会触发

19
我已经完成了自定义图表控件,我想在光标后面绘制一个简单的十字。该图表是在Canvas上实现的PolyLine,并在Canvas的OnMouseMove事件中更改它们的坐标绘制两条线。
令人惊讶的是,当界面线程空闲时(UI完全响应且如果我暂停应用程序,则主线程位于App的mainForm.ShowDialog()),该事件每10秒左右才被调用一次。
有任何想法如何找出为什么会发生这种情况吗?使用OnMouseMove或PreviewOnMouseMove都会获得相同的性能。
编辑:这是我如何绘制交叉点的方法(无论如何,如果我在OnMouseMove处放置断点,它只会偶尔停止)。
XAML:
<Border BorderThickness="1" BorderBrush="White" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Grid.ColumnSpan="2" >
    <Canvas x:Name="DrawArea" PreviewMouseMove="DrawArea_PreviewMouseMove" />
</Border>

计算机科学:

 public Chart()
 {
    _line = new Polyline();
    _line.Stroke = Brushes.Orange;
    _crossX = new Line();
    _crossY = new Line();
    _crossX.Stroke = Brushes.Orange;
    _crossY.Stroke = Brushes.Orange;
    _crossX.StrokeThickness = 1;
    _crossY.StrokeThickness = 1;

    InitializeComponent();

    this.DrawArea.Children.Add(_line);
    this.DrawArea.Children.Add(_crossX);
    this.DrawArea.Children.Add(_crossY);
}     

private void DrawArea_MouseMove(object sender, MouseEventArgs e)
{
    Point mousePosition = System.Windows.Input.Mouse.GetPosition(this.DrawArea);

    _crossX.X1 = 0;
    _crossX.X2 = this.DrawArea.ActualWidth;
    _crossX.Y1 = _crossX.Y2 = mousePosition.Y;

    _crossY.Y1 = 0;
    _crossY.Y2 = this.DrawArea.ActualHeight;
    _crossY.X1 = _crossY.X2 = mousePosition.X;
}

你能否不定义自定义光标,而是尝试进行额外绘制来“伪造”一个吗? - Damien_The_Unbeliever
那么,你没有性能问题,但问题在于事件根本没有被调用 - 我理解得对吗? - Daniel Hilgarth
丹尼尔:它被称为十字架,因为它被绘制出来,但它可能每10秒钟更新一次。 - Ignacio Soler Garcia
@Damien:它不是一个光标,而是一个横跨图表的十字线,让你可以看到所代表数据的不同水平。 - Ignacio Soler Garcia
你能展示一下你是如何在OnRender中绘制十字线的吗?作为画布上的一个子元素,你只需要更新位置即可。请提供一些代码示例。 - dowhilefor
2个回答

37

这很奇怪,我不知道为什么...

只有在区域设置了一些明确的背景刷子或填充后,FrameworkElement.MouseMove才起作用。

在您的情况下,设置 Canvas.Background="Transparent",它应该可以工作。

还有另一种解决方法...WPF捕获鼠标后不发送MouseMove事件;

这可能是因为HitTest依赖于有颜色的像素及其反馈。

无论是什么原因,在MSDN上都没有记录,这对许多UI设计师来说很令人困惑。


2
我只有两个词:谢谢和WTF????真的很奇怪。有人应该在微软提交一个缺陷报告,对吧? - Ignacio Soler Garcia
2
如果画布上有一个子元素,比如形状、文本块或其他什么东西,那么鼠标事件会为该对象和画布触发(它们是直接事件,因此不会通过从子元素冒泡到画布来到达画布)。这真的很奇怪。 - John Reynolds
2
这同样适用于“网格(Grid)”。 - Stefan
Background="Transparent"就是解决方法 - 显然,如果没有背景,框架元素就无法“看到”鼠标光标在其上方。 - Mike
尽管所描述的行为看起来很奇怪,但它是有意义的。 如果事件与控件相关,而不是空白区域,则会触发事件。例如,如果控件的形状不是矩形,则可能会有所帮助。 - user2291296

1
我最终编写了一个像这样的全局鼠标处理程序:
using System.Windows.Interop;

private const int WM_MOUSEMOVE = 0x0200;
public delegate void Del_MouseMovedEvent(Point mousePosition);

// Relative to this control, the mouse position will calculated
public IInputElement Elmt_MouseMovedRelativeElement = null;

// !! This is static; needs special treatment in a multithreaded application !!
public static event Del_MouseMovedEvent Evt_TheMouseMoved = null;

// your main function call
public MyMainWindows()
{
    // install the windows message filter first
    ComponentDispatcher.ThreadFilterMessage += ComponentDispatcher_ThreadFilterMessage;

    InitializeComponent();

    ...
}   

// filtering the windows messages
private void ComponentDispatcher_ThreadFilterMessage(ref MSG msg, ref bool handled)
{
    if(msg.message == WM_MOUSEMOVE)
    {
        this.Evt_TheMouseMoved?.Invoke(Mouse.GetPosition(this.Elmt_MouseMovedRelativeElement));
    }
}

// individual event for mouse movement
private void MyMouseMove(Point mousePoint)
{
    // called on every mouse move when event is assigned
    Console.WriteLine(mousePoint.X + " " + mousePoint.Y);
}

private void AnyFunctionDeeperInTheCode()
{
    // assign the handler to the static var of the main window
    MyMainWindows.Evt_TheMouseMoved += MyMouseMove;

    // set the element / control to which the mouse position should be calculated; 
    MyMainWindows.Elmt_MouseMovedRelativeElement = this;

    ...

    // undassign the handler from the static var of the main window
    MyMainWindows.Evt_TheMouseMoved -= MyMouseMove;
}

1
平滑的解决方案,无需使用 user32.dll。谢谢 - Bin4ry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接