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");
}
}
精彩评论