I am developing a custom user control in WPF and trying to receive all touch events OUTSIDE of my control (so I can implement "cancel touches", where the user cancels an action by tapping somewhere else). Is there any good way to do this? Obviously, if I use TouchDown or other events I will only get notified of touches inside my control.
I tried receiving PreviewTouchDown events from Application.Current.MainWindow, but besides the fact it feels kind of dirty it also doesn't work that well - as soon as I capture those events, the SurfaceListBox inside my custom control stops working (it does not recognize touches anymore).
Is there a good way to solve this? Receiving simply ALL touch events would be sufficient as well, I can then check if they are inside my control.
In WPF, events travel the tree of elements. Events starting with "Preview" are the ones that travel up to bottom, meaning from the parent to the elements it contains. In your case, TouchDown event will travel from the control in which it originates and then it will bubble up towards the parents. PreviewTouchDown will travel in the opposite direction: from the parent towards the control.
Your idea of handling PreviewTouchDown event in the Application.Current.MainWindow is a good one and should handle all PreviewTouchDown events, if this is what you want. But in your case, if you want to check if the event originates in your custom control, you should catch all the TouchDown events in your MainWindow and check their Source property, like this:
private void CommonClickHandler(object sender, RoutedEventArgs e)
{
FrameworkElement feSource = e.Source as FrameworkElement;
if(feSource.Name == "MyCustomControlName")
{
//cancel whatever you want to cancel
e.Handled=true;
}
}
Do not catch the PreviewTouchDown event as this one will first pop in the MainWindow and just later will go all the way to your control.
This msdn link should give you more information: http://msdn.microsoft.com/en-us/library/ms742806.aspx
Related
I have a little problem with winforms and mousewheel events.
I have a custom user control representing a slider. Now, I have a couple groups of sliders in which each group is wrapped inside a panel. All the groups are then wrapped in another panel (which has AutoScroll set to true) and this is wrapped in a form. The slider logic is implemented such that the mousewheel can be used to change its value. For this, the slider user control gets focus when the mouse is over the slider. However, when I scroll, also the AutoScroll parent panel scrolls with it.
I've already lost a lot of time on this issue. Anybody knows what is happening here and how I can solve it? I thought the event was bubbling to the parent panel but I don't find a Handled property on the event when handling it in the Slider control (as is possible with WPF).
many thanks
We implemented the Slider as a complete custom user control (inheriting the UserControl class) with own look-and-feel.
You might have noticed that a UserControl doesn't show the MouseWheel event in the Properties window. Hint of trouble there. The WM_MOUSEWHEEL message bubbles. If the control that has the focus doesn't handle it then Windows passes it on to its Parent. Repeatedly, until it finds a parent window that wants to handle it. The Panel in your case.
You'll need to invoke a bit of black magic in your slider control. The actual event args object that get passed to the MouseWheel event is not of the MouseEventArgs type as the event signature suggests, it is HandledMouseEventArgs. Which lets you stop the bubbling. Like this:
protected override void OnMouseWheel(MouseEventArgs e) {
base.OnMouseWheel(e);
// do the slider scrolling
//..
((HandledMouseEventArgs)e).Handled = true;
}
If you are creating event dynamically like
object.event += new EventHandler<EventArgs>(eventfunction);
try un-registering the event after the eventfunction is called like this
object.event -= new EventHandler<EventArgs>(eventfunction);
I have few controls (Label, Custom Textbox, Datagridview) docked on a form. When I tried to hook the MouseMove event to the individual controls, the event fires perfectly fine but when I tried to hook the event on the form itself, the mousemove event do not respond at all. What could be the possible cause of this?
Edit:
Here is the event hook from resources.cs
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.LogicSimulationViewerForm_MouseMove);
and here is the function handled on catching the event
private void LogicSimulationViewerForm_MouseMove(object sender, MouseEventArgs e)
{
//DOESN'T WORK!!!
}
Winforms events don't bubble up like in WPF or in Html. Therefore if the controls are docked on the form, the form doesn't expose any surface of it's own and it doesn't catch any mouse events. However 'under water', all windows messages (a mouse move is a windows message), do pass the form, so it is possible to catch that message.
edit
Tigran has linked to a good example for the use of IMessageFilter that makes creating another example a bit superfluous :)
The cause of this is that in difference of WPF in WindowsForms the event is "blocked" by the control that handled it (in WPF the event will be pushed to the parent up to the Visual Tree, or in opposite direction: from parent to child).
So to catch that event you need to process it on application level and do not subscribe to single control event.
For more detailed example how to handle it can look here:
How do I capture the mouse mouse move event in my winform application
I'm writing a custom control and I'd like the control to switch from an editing state to it's normal state when a user clicks off of the control. I'm handling the LostFocus event and that helps when a user tabs away or if they click on to another control that's Focusable. But if they don't click on something Focusable, it won't switch out of it's editing state. So I have two solutions in mind:
Walk up the tree to the top most element when it goes in to an editing state and add a handler for MouseDownEvent (and handle "handled" events). In the handler I'd kick the control out of it's editing state and remove the handler from the top most element. This seems like a bit of a hack, but it would probably work well.
Example code:
private void RegisterTopMostParentMouseClickEvent()
{
_topMostParent = this.FindLastVisualAncestor<FrameworkElement>();
if ( _topMostParent == null )
return;
_topMostParent.AddHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ), true );
}
private void UnRegisterTopMostParentMouseClickEvent()
{
if ( _topMostParent == null )
return;
_topMostParent.RemoveHandler( Mouse.MouseDownEvent, new MouseButtonEventHandler( CustomControlMouseDownEvent ) );
_topMostParent = null;
}
Use Mouse.PreviewMouseDownOutsideCapturedElement and add a handler to my control. In the handler I'd kick the control out of it's editing state. But I don't seem to get the event to fire. When does the Mouse.PreviewMouseDownOutsideCapturedElement get kicked off?
Example code:
AddHandler( Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler( EditableTextBlockPreviewMouseDownOutsideCapturedElementEvent ), true );
Just to clarify the answer provided about mouse focus - it was useful but I had to do some further digging + mucking about to get something that actually worked:
I was trying to implement something like a combobox and needed similar behaviour - to get the drop down to disapear when clicking on something else, without the control having knowledge of what something else was.
I had the following event for a drop down button:
private void ClickButton(object sender, RoutedEventArgs routedEventArgs)
{
//do stuff (eg activate drop down)
Mouse.Capture(this, CaptureMode.SubTree);
AddHandler();
}
The CaptureMode.SubTree means you only get events that are outside the control and any mouse activity in the control is passed through to things as normal. You dont have the option to provide this Enum in UIElement's CaptureMouse, this means you will get calls to HandleClickOutsideOfControl INSTEAD of calls to any child controls or other handlers within the control. This is the case even if you dont subscribe to the events they are using - full Mouse capture is a bit too much!
private void AddHandler()
{
AddHandler(Mouse.PreviewMouseDownOutsideCapturedElementEvent, new MouseButtonEventHandler(HandleClickOutsideOfControl), true);
}
You would also need to hang on to + remove the handler at the appropriate points but I've left that out here for the sake of clarity/brevity.
Finally in the handler you need to release the capture again.
private void HandleClickOutsideOfControl(object sender, MouseButtonEventArgs e)
{
//do stuff (eg close drop down)
ReleaseMouseCapture();
}
Capture the mouse.
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.
I usually get the parent window and add a preview handler, even if already handled. Sometimes when MouseCapture is not enough, this technique comes handy:
Window.GetWindow(this).AddHandler
(
UIElement.MouseDownEvent,
(MouseButtonEventHandler)TextBox_PreviewMouseDown,
true
);
I would approach this a different way - have the form that contains the control remove focus from the control when a user clicks on another part of the form.
Having the control actually loose focus is far cleaner than attempting to have the control "simulate" focus being lost in certain situations, when in fact it hasn't. Bear in mind that unless the control has really lost focus it will still accept things like keyboard input.
I have a ScrollViewer that contains a large image. I want the user to be able to hold the mouse down and drag the image to move it side to side, and I'm trying to implement this using a Behavior. The problem is that the mouse down event doesn't seem to be able to fire on the ScrollViewer when a user presses down on the mouse button. With a code behind, I would handle that event on the image, but with a behavior that is only attached to one control I don't know how to approach this issue.
What approach should I use to create a System.Windows.Interactivity.Behavior that attaches to both objects?
The MouseLeftButtonDown event is raised just fine on the ScrollViewer. The problem is that the ScrollViewer is handling the event itself (e.Handled = true). And since it is already handled your behavior do not receive it.
Depending on what you are doing you might be able to just use the PreviewMouseLeftButtonDown event instead. This way it doesn't matter if the ScrollViewer will handle it since the Behavior is receiving it first.
You could also try to use the Drag&Drop events directly. But I'm not sure if that will work.
You don't need to attach to an element to be able to add an event handler to it. If your AssociatedObject is a ScrollViewer then you might be able to get a reference to the image like this:
var image = AssociatedObject.Content as Image;
and then you can add a mouse event handler:
image.MouseLeftButtonDown += (s, e) => Debug.WriteLine("Clicked!");
So the object you are attached to, your associated object, is your "home base", but you can work with any object you can get your hands on up to and including using VisualTreeHelper to walk the visual tree.
Is it possible to set up a Grid or other container control to be sort of an event dam? So that when any events, specifically a left click, that start within it are working their way up that they stop at that control and go no further?
PreviewMouseDown is your friend...
Add this event to your control, and set the Handled property on true...
All events tunnel first from root to leaves in the preview fase, then they are handled from leaves to root in the actual event case...
So PreviewMouseDown handles the Grid before the Button, while the MouseDown event handles the Button before the Grid...
hope this helps...
You should be able to extend whatever control you want (assuming it is not sealed). In your extended class you can override the click event and swallow it (do not pass it to the base class).