I am using CaptureMouse() during a drag and drop operation to make sure I don't miss the MouseUp event, but this seems to prevent any mouse events reaching any other objects on my Canvas. This means that my IsMouseOver based triggers don't work, but I need them to indicate valid locations where the object can be dropped.
Am I doing this wrong, or is there a way of making sure everything on my Canvas still gets mouse events?
Are the elements part of the SubTree of your canvas? or outside of the canvas? If they are within then you could probably use the Capture method that takes a CaptureMode.
Mouse.Capture(elementToCapture, CaptureMode.SubTree);
Alternatively, you should look at the DragDrop class and consider using the Drop event instead?
Related
I wonder if D&D events not always firing (DragLeave/DragEnter).
I implemented a D&D feature in my WPF GUI. While dragging some element around I 'dragleave' a GUI element. Normally it fires an appropriate 'DragLeave' event, but not always. I fear, that for some speed reasons sometimes these events get not fired reliable. If that's the case, how can I overcome these issue?
Short Version:
I wrote some sample app to check it myself - and yes - the DragLeave event is not fired reliable.
Long Version:
What I did:
I placed a Label as a draggable object inside a StackPanel. Then I implemented the code for the MouseMove and DragLeave events. An additional TextBlock - added to the StackPanel as well - served as event output. If the DragLeave handler gets called, I changed the Text Property of the TextBlock to 'DragLeave occured'. Afterwards I tested it dragging the Label towards the StackPanel.
What I recognized:
If I was fast enough or pressed the mouse button close to the border of the Label, the DragLeave event was not fired. If I did it slow, everything worked fine as expected.
What I conclude:
I guess, if the mouse leaves the Label before the 'DoDragDrop' thread is initiated, the event is not fired, since the mouse is already outside the Label. So there is a small glitch between starting the D&D and the firing of the first event.
One can see the same behavior in the DragOver/MouseMove handler. The faster you move the mouse, the fewer points (e.GetPosition(...)) you can capture in the event handler.
Problem is, that for the DragLeave that missing event can be critical, since it is only fired once. However, the DragOver event is fired very often during a dragging and a missing event can be balanced by the next DragOver event directly afterwards.
I am opening a separate question about the reason for this issue so that my other question can be about possible workarounds.
So I have drag and drop enabled for a listview but I noted that the UIelement.IsMouseOver doesn't work during a drag and drop operation. In particular during a drag and drop operation, if IsMouseOver is tested while the mouse is over an element it will return false when it should return true. The same function works perfectly when tested before the drag and drop operation.
I have also read that there is an issue online.
Source: "This is because WPF blocks all mouse actions when we doing drag drop action. You can use EventTrigger with an animation to do this."
http://social.msdn.microsoft.com/Forums/vstudio/en-US/9a73b1b0-ec76-4b2c-8da6-91c71e3c406f/wpf-mouse-click-event-on-scrollbar-issue?forum=wpf
However, I would like to know what exactly "blocking all mouse action" entails. It seems like someone ought to fix IsMouseOver to operate with drag and drop
I wouldnt see this as an issue because it is how things are in wpf by design. Mouse is not blocking anything mouse is being captured when doing drag and drop. In order to have the IsMouseOver property set to True the control or visual in wpf needs to capture the mouse pointer. However when you start dragging something from a source control to a destination control, the destination control is not capturing the mouse once over it because it is the source control on which the mouse was and still is being pressed. Source control needs to capture the mouse and hold it caputured until the desired operation is done. For example by capturing mouse by source control the source control may keep track where mouse is and check if the visual which is mouse pointer currently standing on allows drop behavior at all. If so the mouse cursor will get changed.
There are many solution for this. You could use EventTrigger in your style and listen to drag event instead of having simple Trigger listening to IsMouseOver. Another solution would be to have own property. Since IsMouseOver only has a getter you could create your own attached property that allows itself to be set when mouse over or drag and drop is being performed. You could use your own property instead IsMouseOver.
I think I'm going crazy...
I have Canvas with event handlers for MouseMove & MouseLeftButtonUp.
However MouseLeftButtonUp is not being fired when it happens with cursor over TextBlock that is inside canvas.
(it fires just fine when I release mouse button in empty space of the canvas)
I tried attaching handler via AddHandler and using regular += syntax, nothing seems to work.
I tried using Canvas.CaptureMouse() but it doesnt seem to work either (CaptureMouse returns true btw).
MouseLeftButtonUp just doesnt want to propagate to it's parent when it happens over TextBlock (or any other element with IsHitTestVisible = true) inside Canvas.
Please help.
First I'd like to say that you are not going crazy. I've seen this before in Silverlight applications. Silverlight has some interesting event strategies. Silverlight events follow a bubble up approach for routed events but not with all events (msdn has some information on this) The events you are listening to are in that list but they are being handled by the TextBlock. Most UIElements have IsHitTestVisible=true so that mouse events and others are captured by the control and not bubbled up to its parent. Setting IsHitTestVisible=false should solve the problem. Other than that I can tell you what I have tried to overcome this issue when needing IsHitTestVisible=true.
Set the event hanlder from the parent on the TextBlock. Downside is you need to do this for every control in your canvas.
Try to fire the event through an extension class. I could not get this one to work cause i couldn't fire events using reflection.
You mentioned you tried AddHandler - did you try the AddHandler overload that accepts two parameters, the second one being "true" to indicate you want to get handled events as well?
I have two UIElements next to each other that I want to behave as if they are one MouseLeave area.
How can I check if the mouse is over a UIElement being entered during a MouseLeave event?
Currently, checking the mouse position using the following solution shows the mouse over the element being left during the MouseLeave event: http://forums.silverlight.net/forums/p/193378/448518.aspx.
Thank you.
I don't know if the event order is deterministic, so I don't know if you can do exactly what you are describing. You might be able to do it with some weird code. For example, you could try making a UI element hit-test function, and checking it during the mouse leave event handler. This would not be very flexible code.
I haven't tried this, but you could probably solve this problem by containing both UI elements in a parent element, such as a grid, and adding your event handlers to that parent. That would be much simpler, and much more flexible.
On System.Windows.UIElement there is a CaptureMouse() and a paired ReleaseMouseCapture() method. In this WPF DragDrop sample they call CaptureMouse on MouseDown and release it on MouseUp. The documentation in MSDN is about as useless as it comes - "CaptureMouse -> Captures the mouse."
In my head before trying it I assumed that it somehow locked the mouse inside the UIElement bounds, but that's clearly not the case when I try it. From experimenting, it seems to have something to do with responding to events when the mouse is outside of the UIElement, but not wanting to be a cargo cult programmer I don't want to just use it because the example does, I'd like an authoritative description of what it means.
From Capture and Uncapture the Mouse on MSDN:
When an object captures the mouse, all mouse related events are treated as if the object with mouse capture perform the event, even if the mouse pointer is over another object.
Only the capturing control receives the mouse events until released.
Capturing the mouse is useful for dragging because all the dragging code can exist in the one control, rather than being spread over multiple controls.
When it has captured the mouse, a control will receive mouse events even if the mouse pointer is no longer within its bounding area.
Typically, it's used for:
Drag and drop
Buttons (to handle Mouse Up when you put the mouse down on the button and move the mouse before you release the button)
The Silverlight 2 documentation for it has a more verbose description, I don't know why it isn't a part of the 3.5 documentation page too:
When an object has captured the mouse, that object receives mouse input whether or not the mouse pointer is within its bounding area. The mouse is typically only captured during simulated drag operations.
...
It works the same with WPF, and so the reason it is used with DragDrop, is that is how the
it knows to report back to the control being dragged from when the mouse may be outside of that control. If you comment out the MyCanvas.Capture() and the Capture(Null) (which clears it) then you can no longer drop.