WPF Mousedown => No MouseLeave Event - wpf

I'm building a Windows Presentation Foundation control with Microsoft Blend.
When I leave my control by pressing the left-mouse-button, the MouseLeave-Event is not raised. Why not?

This is intended behaviour: When you are doing mousedown on a control and leaving the control, the control STILL retains its "capture" on the mouse, meaning the control won't fire the MouseLeave-Event. The Mouse-Leave Event instead will be fired, once the Mousebutton is released outside of the control.
To avoid this, you can simple tell your control NOT to capture the mouse at all:
private void ControlMouseDown(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
Control control = (Control) sender;
control.Capture = false; //release capture.
}
Now the MouseLeave Event will be fired even when moving out while a button is pressed.
If you need the Capture INSIDE the Control, you need to put in more effort:
Start tracking the mouseposition manually, when the mousekey is pressed
Compare the position with the Top, Left and Size Attributes of the control in question.
Decide whether you need to stop the control capturing your mouse or not.
public partial class Form1 : Form
{
private Point point;
private Boolean myCapture = false;
public Form1()
{
InitializeComponent();
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
myCapture = true;
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (myCapture)
{
point = Cursor.Position;
if (!(point.X > button1.Left && point.X < button1.Left + button1.Size.Width && point.Y > button1.Top && point.Y < button1.Top + button1.Size.Height))
{
button1.Capture = false; //this will release the capture and trigger the MouseLeave event immediately.
myCapture = false;
}
}
}
private void button1_MouseLeave(object sender, EventArgs e)
{
MessageBox.Show("Mouse leaving");
}
}
of course you need to stop the own tracking ( myCapture=false;) on MouseUp. Forgot that one :)

When I don't get mouse events I expect I typically use Snoop to help me understand what is happening.
Here are a couple of links:
1- Snoop (a WPF utility)
2- CodePlex project for Snoop

And for completeness and historical reasons (not the bounty - it doesn't make sense having two duplicate questions - you should probably move it into one if not too late)...
I made a thorough solution using global mouse hook here (approach 2)
WPF: mouse leave event doesn't trigger with mouse down
And simplified its use - you can use it by binding to commands in your view-model - e.g.
my:Hooks.EnterCommand="{Binding EnterCommand}"
my:Hooks.LeaveCommand="{Binding LeaveCommand}"
my:Hooks.MouseMoveCommand="{Binding MoveCommand}"
...more details in there

Old question but I came across the same problem with a Button (MouseLeave does not fire while MouseDown because MouseDown Captures the Mouse...)
This is how I solved it anyway:
element.GotMouseCapture += element_MouseCaptured;
static void element_MouseCaptured(object sender, MouseEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.ReleaseMouseCapture();
}
Hope that helps someone looking for a quick fix :P

Related

Handling MouseWheel event in a WPF custom control

I have am making a map editor for a game which has a user control that has an image. Inside that control I attached the MouseWheel event to it, but I've noticed two issues that I hope to have a better understanding of why it behaves the way it does and how to properly implement it.
For one the event only seems to fire when the mouse is hovering over it instead of when the control is in focus. If possible I would like to switch that and be able to fire the event no matter where the mouse is as long as that control is in focus and the second issue is that checking the delta when the number is positive works fine, but when I get a number back when it's negative I get a value of 0xfffffffd or something in that range. How would I go about differentiating the difference between a positive balue and a negative value if I always get something positive?
Thanks in advance for the help.
If you want to fire MouseWheel event for focused element try:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MouseWheel += OnMouseWheel;
}
IInputElement focusedElement;
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (focusedElement is TextBox)
{
var tbx = focusedElement as TextBox;
//do something
}
}
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
focusedElement = e.NewFocus;
}
}

Click event in UserControl- WPF

I have a UserControl in my WPF application.
I want to call a click event and do some things when the user clicked the UserControl.
The problem is- the UserControl doesn't have a click event.
I searched on the web and found that you can use the MouseLeftButtonUp event.
I tried it- but it doesn't respond to my clicks.
You didn't write what you are trying to do but if you need a click event maybe you are writing some kind of button (the Button class is actually "something you can click" with the visual representation in a control template you can replace)
If you need a button with complex content inside - put your user control inside a button
If you need a button that doesn't look like a button write a custom control template for button
If you need a button with extra functionality subclass button, add the extra data/behavior in code and put the display XAML in a style.
I think for your needs PreviewMouseLeftButtonUp(Down) event is more suitable. Then you need to handle ClickCount for counting amount of clicks and then raise your own event, on which other controls will know, that your control is clicked. There are much more methods on handling click event. You should look at this msdn article and this
UPDATE to handle both Click and DoubleClick
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_myCustomUserControl.MouseLeftButtonUp += new MouseButtonEventHandler(_myCustomUserControl_MouseLeftButtonUp);
_myCustomUserControl.MouseDoubleClick += new MouseButtonEventHandler(_myCustomUserControl_MouseDoubleClick);
}
bool _doubleClicked;
void _myCustomUserControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_textBlock.Text = "Mouse left button clicked twice";
_doubleClicked = true;
e.Handled = true;
}
void _myCustomUserControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_doubleClicked)
{
_doubleClicked = false;
return;
}
_textBlock.Text = "Mouse left button clicked once";
e.Handled = true;
}
}
To test this example name your control as _myCustomUserControl and add a TextBlock named _textBlock to your MainWindow.xaml
Why not just use MouseDown?
Put the event in the User Control and simply do this:
private void MyControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
MessageBox.Show("Clicked!");
}
}

Loss of Silverlight mouse up events after mouse capture?

I created a very simple test control that has a Rectangle on a canvas (within other containers, but inconsequential). The Rectangle has event handlers for mouse down, mouse move, and mouse up. If I capture the mouse in the Rectangle's MouseLeftButtonDown event, I do not receive a corresponding MouseLeftButtonUp event.
Some code:
private void rect1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (_captured = CaptureMouse())
{
_offset = new Point(Canvas.GetLeft(rect1), Canvas.GetTop(rect1));
_origin = e.GetPosition(RootCanvas);
e.Handled = true;
}
}
private void rect1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_captured)
{
ReleaseMouseCapture();
_captured = false;
e.Handled = true;
}
}
I attached event handlers for the container elements as well, just to make sure one of them wasn't getting the mouse-up event somehow, but none of them were. Is there an expectation of this in Silverlight that I haven't yet learned?
I think you are little confused about what is actually capturing the mouse events.
Consider when you do this:-
if (_captured = CaptureMouse())
what object is the CaptureMouse actually being called against?
Answer: The user control for which your code is the code-behind. Had you wanted the rectangle to capture the mouse you would do:-
if (_captured = rect1.CaptureMouse())
CaptureMouse(); from mouseDown Event and then try.

Why is the TreeViewItem's MouseDoubleClick event being raised multiple times per double click?

XAML
<TreeView Name="GroupView" ItemsSource="{Binding Documents}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="OnTreeNodeDoubleClick"/>
</Style>
</TreeView.ItemContainerStyle>
....
</TreeView>
Code-Behind
private void OnTreeNodeDoubleClick(object sender, MouseButtonEventArgs mouseEvtArgs)
{
Console.WriteLine("{3} MouseDoubleClick Clicks={0} ChangedButton={1} Source={2} Handled={4} ButtonState={5}",
mouseEvtArgs.ClickCount, mouseEvtArgs.ChangedButton, mouseEvtArgs.OriginalSource,
mouseEvtArgs.Timestamp, mouseEvtArgs.Handled, mouseEvtArgs.ButtonState);
}
I find that for one double click, the event handler is called multiple times. I'm trying to open up a document in tab on a double-click on the corresponding tree node; so I'd need to filter out the extra calls.
23479156 MouseDoubleClick Clicks=1 ChangedButton=Left Source=System.Windows.Controls.TextBlock Handled=False ButtonState=Pressed
23479156 MouseDoubleClick Clicks=1 ChangedButton=Left Source=System.Windows.Controls.TextBlock Handled=False ButtonState=Pressed
In my slightly-complicated app, it is being raised 4 times per double-click. On a simple repro-app, it is being raised 2 times per double click. Also all the event argument parameters are the same too, so I can't distinguish the last one of a set.
Any ideas why this is the way it is?
I know this is an old question, but as I came across it in my searches for the solution, here are my findings for any future visitors!
TreeViewItems are recursively contained within each other. TreeViewItem is a HeaderedContentControl (see msdn), with the child nodes as the Content. So, each TreeViewItem's bounds include all of its child items. This can be verified using the excellent WPF Inspector by selecting a TreeViewItem in the visual tree, which will highlights the bounds of the TreeViewItem.
In the OP's example, the MouseDoubleClick event is registered on each TreeViewItem using the style. Therefore, the event will be raised for the TreeViewItems that you double-clicked on - and each of its parent items - separately. This can be verified in your debugger by putting a breakpoint in your double-click event handler and putting a watch on the event args' Source property - you will notice that it changes each time the event handler is called. Incidentally, as can be expected, the OriginalSource of the event stays the same.
To counter this unexpected behaviour, checking whether the source TreeViewItem is selected, as suggested by Pablo in his answer, has worked the best for me.
When a TreeViewItem is double clicked, that item is selected as part of the control behavior. Depending on the particular scenario it could be possible to say:
...
TreeViewItem tviSender = sender as TreeViewItem;
if (tviSender.IsSelected)
DoAction();
...
I've done some debugging and it appears to be a bug in WPF. Most answers already given are correct, and the workaround is to check if the tree view item is selected.
#ristogod's answer is the closest to the root problem - it mentions that setting e.Handled = true the first time handler is invoked doesn't have the desired effect and the event continues to bubble up, calling parent TreeViewItems' handlers (where e.Handled is false again).
The bug seems to be in this code in WPF:
http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Control.cs,5ed30e0aec6a58b2
It receives the MouseLeftButtonDown event (which is handled by the child control already), but it fails to check if e.Handled is already set to true. Then it proceeds to create a new MouseDoubleClick event args (with e.Handled == false) and invokes that always.
The question also remains why after having set it to handled the first time the event continues to bubble? Because in this line, when we register the handler Control.HandleDoubleClick:
http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Control.cs,40
we pass true as the last argument to RegisterClassHandler:
http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/EventManager.cs,161
which is handledEventsToo.
So the unfortunate behavior is a confluence of two factors:
Control.HandleDoubleClick is called always (for handled events too), and
Control.HandleDoubleClick fails to check if the event had already been handled
I will notify the WPF team but I'm not sure this bug is worth fixing because it might break existing apps (who rely on the current behavior of event handlers being called even if Handled was set to true by a previous handler).
private void TreeView_OnItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.Source is TreeViewItem
&& (e.Source as TreeViewItem).IsSelected)
{
// your code
e.Handled = true;
}
}
This is not actually a bubbling issue. I've seen this before. Even when you tell the event that you handled it, it continues to keep bubbling up. Except that I don't think that it's actually bubbling up, but rather firing the node above's own double click event. I could be totally wrong on that. But in either case, it's important to know that saying:
e.handled = true;
Does nothing to stop this from happening.
One way to prevent this behavior is to note that when you are double clicking, you are first single clicking and that the selected event should fire first. So while you can't stop the Double Click events from occurring, you should be able to check inside the handler to see whether the event logic should run. This example leverages that:
TreeViewItem selectedNode;
private void MouseDoubleClickEventHandler(object sender, MouseButtonEventArgs e)
{
if(selectedNode = e.Source)
{
//do event logic
}
}
private void TreeViewSelectedEventHandler(object sender, RoutedEventArgs e)
{
selectedNode = (TreeViewItem)e.Source;
}
Sometimes however you have situations where the nodes are being selected by other beans than through the TreeView SelectedItemChanged event. In that case you can do something like this. If you happen to have a TreeView with a single declared top node, you can give that node a specific name and then do something like this:
bool TreeViewItemDoubleClickhandled;
private void MouseDoubleClickEventHandler(object sender, MouseButtonEventArgs e)
{
if (!TreeViewItemDoubleClickhandled)
{
//do logic here
TreeViewItemDoubleClickhandled = true;
}
if (e.Source == tviLoadTreeTop)
{
TreeViewItemDoubleClickhandled = false;
}
e.Handled = true;
}
Regardless of the method you use, the important thing is to note that for whatever reason with TreeViewItem double clicking that you can't stop the events from firing up the tree. At least I haven't found a way.
I have a little bit more elegant solution than checking for selection or creating flags:
A helper method:
public static object GetParent(this DependencyObject obj, Type expectedType) {
var parent = VisualTreeHelper.GetParent(obj);
while (parent != null && parent.GetType() != expectedType)
parent = VisualTreeHelper.GetParent(parent);
return parent;
}
And then your handler:
public void HandleDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is DependencyObject)
if (sender == (e.OriginalSource as DependencyObject).GetParent(typeof(TreeViewItem)))
{
// sender is the node, which was directly doubleclicked
}
}
This is the wonderful world of event bubbling. The event is bubbling up the node hierarchy of your TreeView and your handler is called once for every node in the hierarchy path.
Just use something like
// ...
if (sender != this)
{
return;
}
// Your handler code goes here ...
args.Handled = true;
// ...
in your handler code.
There are some pretty major problems with this solution, but it could work in case someone needs to solve this problem in multiple places and I did find a scenario where the accepted solution doesn't work (double clicking on a toggle button that opens up a popup, where the toggle button is inside another element that handles double click.)
public class DoubleClickEventHandlingTool
{
private const string DoubleClickEventHandled = "DoubleClickEventHandled";
public static void HandleDoubleClickEvent()
{
Application.Current.Properties[DoubleClickEventHandled] = DateTime.Now.AddSeconds(1);
}
public static bool IsDoubleClickEventHandled()
{
var doubleClickWasHandled = Application.Current.Properties[DoubleClickEventHandled] as DateTime?;
return doubleClickWasHandled.HasValue && !IsDateTimeExpired(doubleClickWasHandled.Value);
}
private static bool IsDateTimeExpired(DateTime value)
{
return value < DateTime.Now;
}
public static void EnableDoubleClickHandling()
{
Application.Current.Properties[DoubleClickEventHandled] = null;
}
public static bool IsDoubleClickEventHandledAndEnableHandling()
{
var handled = IsDoubleClickEventHandled();
EnableDoubleClickHandling();
return handled;
}
}
Use DoubleClickEventHandlingTool.HandleDoubleClickEvent()
inside the inner/low level element eg:
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{if (e.ClickCount == 2) DoubleClickEventHandlingTool.HandleDoubleClickEvent();}
High level double click event then only performs it's action when:
if (!DoubleClickEventHandlingTool.IsDoubleClickEventHandledAndEnableHandling())
The most likely reason is that the doubleclick handler is installed multiple times, so each instance of the handler is being called once for each click.

Silverlight: MouseLeftButtonDown timer to repeatedly fire an event

I am trying to create a custom scrollbar and am using images as button.
For now a simple
I can handle the MouseLeftButtonDown and Up event just fine but what I'd like to do is while its held down, every so many millisecond trigger an event is fired.
I tried something like this but it isn't quite working. Suggestions?
public delegate void Changed(RangeScrollButtonControl sender, int value);
public event Changed OnChanged;
private System.Threading.Timer Timer;
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.Timer = new System.Threading.Timer(Timer_Callback, null, 0, 100);
}
private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this.Timer = null;
}
private void Timer_Callback(object sender)
{
if (this.OnChanged != null)
{
this.OnChanged(this, 1);
}
}
The piece of functionality you're looking for is a RepeatButton, this will fire it's Click event repeatedly while the mouse button is held down. You can configure the delay and the interval of the events.
You could then style this button to use the image at Silverlight Show
Hope this helps.
Which piece "isn't quite working" ?
Also, could you restyle or retemplate Silverlght's scrollbar similar to what is seen in this blog post to get what you need?
I would use a Storyboard as a timer. Something like:
Then you can do a MouseSTB.Begin. Once the Storyboard is finished you can catch it in the MouseSTB.Completeed Event. In that event you can do what ever you need to do and then just start it over again. It can easilly be controled by setting some flags on the mouseover, mouseenter and mouseleave events. I use these timers in a lot of place and they work just fine and they do not peg the processor.

Resources