开发者

PreviewMouseMove firing twice

I have a problem with a simple code. I was looking for a few hours a solution, but no effects. I have a Canvas and Rectangle. I move Rectangle, if the cursor is outside, delegate pMouseMove fires only once for each pixel. Conversely, if the cursor is at the Rectangle, delagate fires twice for each pixel. I want to run it only once, as if it were outside the Rectangle, how to do it?

XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
  <Canvas x:Name="Can" Height="257" Width="503" Background="Gray">
    <TextBox Name="tb" Width="77" Height="20" Canvas.Left="0" Canvas.Top="-21"/>
  </Canvas>
</Window>

Code-behind:

public partial class MainWindow : Window
{
    Rectangle rect = new Rectangle();
    private static int i;
    private static string s;

    public MainWindow()
    {
        InitializeComponent();

        rect.Height = 50;
        rect.Width = 50;
        rect.Fill = Brushes.Black;
        Can.Children.Add(rect);
        Can.PreviewMouseMove += pMouseMove;
    }

    private void pMouseMove(object sender, MouseEventArgs e)开发者_开发技巧
    {
        //cursor over Rectangle
        Canvas.SetTop(rect, e.GetPosition(Can).Y + 10);
        Canvas.SetLeft(rect, e.GetPosition(Can).X + 10);

        //cursor outside Rectangle
        //Canvas.SetTop(rect, e.GetPosition(Can).Y - 10);
        //Canvas.SetLeft(rect, e.GetPosition(Can).X - 10);

        //Counter
        i++;
        tb.Text = i.ToString();

        //e.Handled = true;
    }
}

Sorry for my bad english


Events in WPF are Routed Events, which effectively means that your Canvas will receive events from the canvas itself and everything inside the canvas. As you noticed, the Canvas's PreviewMouseMove event is receiving events from both the Canvas and the Rectangle.

[Update] I ran your code and added a line to check the value of the e.OriginalSource to see what originally raised the event. Like this:

private void pMouseMove(object sender, MouseEventArgs e)
{
    // print out e.OriginalSource just for learning purposes
    Console.WriteLine("OriginalSource:" + e.OriginalSource.ToString());
}

My original answer was to check e.OriginalSource's type because I thought you were receiving the same event twice. But I now see what you are saying: if e.OriginalSource is the Rectangle, the PreviewMouseMove event gets raised twice as often compared to when e.OriginalSource is the Canvas. There's something internal to the Rectangle's implementation that is doing this (only way to find out is to use a tool like Reflector to see the internal logic. However, there is a workaround where you can make the frequency of the event consistent.

You can set rect.IsHitTestVisible = false; and that will eliminate the Rectangle from sending events and being e.OriginalSource -- so that means all PreviewMouseMove events will come from the Canvas. Then you can use VisualTreeHelper.HitTest to check to see if the mouse position is inside the Rectangle.

I just ran this code below and I think this is a way to guarantee consistent raising of events, but still have your hit test capability.

In the constructor:

rect.Fill = Brushes.Black;
rect.IsHitTestVisible = false;
Can.Children.Add(rect);

In the PreviewMouseMove handler:

private void pMouseMove(object sender, MouseEventArgs e)
{
    // Debug.WriteLine(e.OriginalSource.ToString());

    HitTestResult result = VisualTreeHelper.HitTest(rect, e.GetPosition(sender as UIElement));

    if (result != null) {
        Debug.WriteLine("Mouse inside rect")
    }
    else {
        Debug.WriteLine("Mouse outside rect");
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜