问题
I have a strange question and a vb.net 2010 and wpf4 project. I have a label that, when clicked, captures the mouse (MyLabel.captureMouse) and follows it around the screen until the mouse clicks again, at which point the object and the mousecapture is released.
However, I still need the functionality of mouseenter for another object. How do I get these two things to work together?
EDIT: There are two solutions to this, it seems, one of which I discovered. However, since Rick's should work as well (though untested because of my deadline), and I hate answering my own questions on here, I accepted his answer.
In the interim of waiting for him to comment back to a problem I had, I wound up discovering my own solution. Thus, be sure to read both my answer and Rick's.
回答1:
If the other object is one of your objects, then your label and the other object can cooperate while you have the mouse captured using synthetic events. For example, in your mouse move handler, you can check Mouse.DirectlyOver
to see if it is the other object and if so, do a little bookkeeping and then call RaiseEvent
with either MouseEnter
or MouseLeave
on the other object. If you have a bunch of these objects then you just have more bookkeeping to do.
Edit:
The above refers to Mouse.DirectlyOver
which specifically does not work when the mouse is captured. To make the above more concrete and to fix that error, here is a complete working example.
Here is the markup showing a canvas, a rectangle with mouse capture handling, and an ellipse with enter/leave handling:
<Grid>
<Canvas>
<Rectangle Canvas.Left="0" Canvas.Top="0"
Fill="Red" Width="100" Height="100"
MouseLeftButtonDown="Rectangle_MouseLeftButtonDown"
MouseMove="Rectangle_MouseMove"
MouseLeftButtonUp="Rectangle_MouseLeftButtonUp"/>
<Ellipse Canvas.Left="0" Canvas.Top="100"
Fill="Green" Width="100" Height="100"
MouseEnter="Ellipse_MouseEnter"
MouseLeave="Ellipse_MouseLeave">
<Ellipse.RenderTransform>
<ScaleTransform/>
</Ellipse.RenderTransform>
</Ellipse>
</Canvas>
</Grid>
and here are the event handlers that demonstrate how to generate synthetic enter/leave events (but only for the ellipse) while the mouse is captured:
private void Rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var capturer = sender as FrameworkElement;
capturer.CaptureMouse();
}
bool over = false;
UIElement element;
private void Rectangle_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed) return;
var capturer = sender as FrameworkElement;
var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this));
if (hit == null) return;
var thisElement = hit.VisualHit as Ellipse;
var nowOver = thisElement != null;
if (nowOver) element = thisElement;
var args = new MouseEventArgs(Mouse.PrimaryDevice, 0);
if (!over && nowOver) { args.RoutedEvent = UIElement.MouseEnterEvent; element.RaiseEvent(args); }
if (over && !nowOver) { args.RoutedEvent = UIElement.MouseLeaveEvent; element.RaiseEvent(args); }
over = nowOver;
}
private void Rectangle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
var capturer = sender as FrameworkElement;
capturer.ReleaseMouseCapture();
}
private void Ellipse_MouseEnter(object sender, MouseEventArgs e)
{
Debug.WriteLine("MouseEnter");
}
private void Ellipse_MouseLeave(object sender, MouseEventArgs e)
{
Debug.WriteLine("MouseLeave");
}
If you run the demo under the debugger you'lll see that the enter/leave handlers are called whether the mouse is captured or not.
回答2:
In my experience using Capturing is not the way to handle drag and drop (if you are implementing drag and drop) precisely because of the reason you state here.
I solve this situation by keeping track of the mouse button and movement and translating the control along.
It is also possible to use the Blend Drag behavior.
回答3:
Since the code I was using to simulate the "dragging" of an object involved getting the mouse position anyway, I capitalized on the opportunity and wrote a code that checked whether or not the mouse's position on the canvas was mathematically within the boundaries of each of the objects I needed mouseenter/mouseleave for. This worked especially well since the objects I needed the mouseover/mouseleave for never changed positions.
Here is the trimmed version of my final code.
'Declare the left, right, top, and bottom boundaries of each of the "drop-spot" objects in relation to the canvas. This could also be determined programamtically for objects that change position.
Private Tile1PosL As Double = 55
Private Tile1PosT As Double = 30
Private Tile2PosL As Double = 164
Private Tile2PosT As Double = 69
Private Tile3PosL As Double = 282
Private Tile3PosT As Double = 41
Private Tile4PosL As Double = 405
Private Tile4PosT As Double = 69
Private Tile5PosL As Double = 514
Private Tile5PosT As Double = 12
Private Sub Tile1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles Tile1.MouseMove
If IsDragging1 = True Then
'My dragging functions go here.
'Get the mouse position on the canvas (canv).
Dim MousePosX As Double = e.GetPosition(canv).X
Dim MousePosY As Double = e.GetPosition(canv).Y
'Check to see if the mouse is within the boundaries of any of the "drop-spot" objects (Hole1, Hole2, Hole3, Hole4, Hole5).
If MousePosX > Hole1L And MousePosX < Hole1R And MousePosY > Hole1T And MousePosY < Hole1B Then
'Call the subroutine containing the code that would ordinarily go in "mouseenter".
Hole1_TileEnter()
ElseIf MousePosX > Hole2L And MousePosX < Hole2R And MousePosY > Hole2T And MousePosY < Hole2B Then
'Call the subroutine containing the code that would ordinarily go in "mouseenter".
Hole2_TileEnter()
ElseIf MousePosX > Hole3L And MousePosX < Hole3R And MousePosY > Hole3T And MousePosY < Hole3B Then
'Call the subroutine containing the code that would ordinarily go in "mouseenter".
Hole3_TileEnter()
ElseIf MousePosX > Hole4L And MousePosX < Hole4R And MousePosY > Hole4T And MousePosY < Hole4B Then
'Call the subroutine containing the code that would ordinarily go in "mouseenter".
Hole4_TileEnter()
ElseIf MousePosX > Hole5L And MousePosX < Hole5R And MousePosY > Hole5T And MousePosY < Hole5B Then
'Call the subroutine containing the code that would ordinarily go in "mouseenter".
Hole5_TileEnter()
Else
'If the mouse is not within any of the "drop-spot" objects, call the subroutine containing the code that would ordinarily go in each object's "mouseleave". NOTE: This code contains statements that determine that the mouse had been inside one of the "drop-spots" before actually triggering the rest of its code.
Hole_TileLeave()
End If
End If
End If
'This subroutine, with minor modifications, works for my Tile2, Tile3, Tile4, and Tile5 as well.
End Sub
来源:https://stackoverflow.com/questions/5761691/how-to-fire-mouseenter-for-one-object-if-another-object-has-mousecapture