I have researched this and I have it working, but it makes no sense to me.
I have a parent panel with a PictureBox and a Label in it. I want to catch a mouse click event that occurs anywhere within the parent panel.
Here's how I expected it to work: If I click the mouse over the PictureBox or Label the system sees if they have an event handler for it. If so, their event handler gets the event, otherwise, the event automatically is forwarded to the parent panel. If the parent panel has an event handler then it gets the event, otherwise, it is forwarded to its parent. When a control handles the event, its event handler can decide if the forwarding should continue or if the event is consumed.
Here's how it seems to work: If I click the mouse over the PictureBox or Label the system sees if they have an event handler - end of story. No automatic forwarding to the parent panel.
Am I misunderstanding something? This approach seems like it puts an unnecessary burden on the programmer, having to programmatically catch and forward events. There must be an easier approach that I'm overlooking.
Related
Lets say I have my user-control somewhere in the visual tree. Parent and children are 3rd-party controls that I cannot modify. I want to filter keyboard events in my control so that children controls do not receive some keyboard events, but the parent controls do.
I'll try to explain what I want to achieve with some diagrams. If controls do not handle keyboard events, all events bounce through the visual tree:
But, f.e. when user presses A,
Child2.OnPreviewKeyDown() should NOT be called
but Parent2.OnTextInput should still receive an event
I can achive (1) by setting e.Handled = true in MyControl.PreviewKeyDown. The problem is that in this case TextInput event is not generated:
Is there a way to achieve behavior like on the 2nd picture?
Added:
The problem I'm trying to solve is that a 3rd-party control (Child 2) steals some input in OnPreviewKeyDown (and marks event as handled), and I'm trying to avoid that.
What you can generally do in WPF to handle a suppressed event is add a handler in code and re-raise the event. To do this, you use the UIElement.AddHandler() method, for example:
child2.AddHandler(UIElement.TextInput, new TextCompositionEventHandler(nameOfYourHandlerFunction), true);
The 'true' boolean value is what makes nameOfYourHandlerFunction fire even if the Handled flag is set. The event won't automatically re-bubble by doing that, so you need to raise the event again.
base.RaiseEvent(e);
This works for events that have a routing strategy of Bubble.
How does one go about raising an event for PreviewMouseDown. I can successfully do this for MouseClick events... but falling short on the preview variation. The goal is the automate PreviewMouseDown and then PreviewMouseUp only when needed.
Cheers.
Not sure what you mean by "automate PreviewMouseDown" but PreviewXXX routed events in WPF are tunneled down the control automatically (from top of the visual tree) and then it calls the XXX event which gets bubbled up.
There is no way you can 'just' call PreviewXX event. Though you can handle them separtely by attaching to the right event.
I've a situation in a windows phone project wherein I'm dynamically creating UI elements one inside the other. This can lead to say a 10 StackPanel hierarchy . Now each UI element has a tap event attached to it. How do I make sure that only the tap event associated with the bottom most element is triggered (my problem being all 10 events are triggered)?
Have you tried to use eventArgs.Handled = true; into your event handlers to stop the event propagation? (see in MSDN).
It will stop the event propagation at the first event handler receiving it, which will be (in your case) the one associated with the deepest StackPanel located where the Tap event occurs.
I'm trying to clear some variable on a panel, for example, if I had a bool which lets me know when I'm click dragging on a panel's surface I set this to false when a MouseUp event occurs (this may or may not be correct way to do this but serves as an example).
If while click dragging I then alt-tab to another application the panel itself doesn't appear to get any notifications, like focus->leave / mouseup for example, is there something I'm missing, an event I've overlooked?
There seems to be a way of doing this by using the forms Deactivate event, which I suppose I could just call a suspend type method on my panel if I create a new Panel class, but I was wondering if something already existed that would propagate all children on a form with some notification that our form is no longer the main focus.
Reliably getting these kind of notifications requires that you use the Capture property. Set it to true on the MouseDown event. It ensures that all mouse messages are directed to your panel, even if the mouse is no longer hovering the panel. That however still doesn't cover rude focus changes, like Alt+Tab or Alt+Esc. You also need to implement the MouseCaptureChanged event to know when the operating system stepped in.
In general, if you are trying to implement Drag + Drop then you ought to use DoDragDrop(). When it returns you can always be sure that the drag operation is completed, for whatever reason. The return value of the method tells you what happened. Note that this also supports switching to another window, albeit that it is not very discoverable, you drag to the task bar button to force a switch.
I'm making a custom control with a panel. I want to be able to drag and drop it so I've implemented that in the MouseDown event of my control. But I want the thing to react when you start drag to give a little feedback to the user. So in the MouseDown even I change the color. Then I want to change it back in the MouseUp event.
My control is not installed into VS2008 but just a class I've written that I instanciate at run time (I don't know in advance how many I need and so on). Now, my control exposes a MouseDown event so as to be able to be dragged. When I subscribe to this event from the parent application to actually perform the drag and drop my control is not repainted on its MouseUp event! In fact, the MouseUp is never invoked. If, on the other hand, I don't subscribe to the event in the parent app it works as intended.
What's going on? Is the parent interrupting the flow so that the MouseUp event never fires in my control? How do I get around this?
I'm not sure if you are using Windows Forms or WPF, but in Windows forms here is what I mean:
public class DerivedPanel : Panel
{
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
Capture = true;
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Capture = false;
// Change your color or whatever here
}
}
In WPF there are two methods, CaptureMouse() and ReleaseMouseCapture() to do the same thing. When the control captures the mouse, it will received mouse events even if the cursor isn't over the control. This could be causing your problem. See MSDN Article
Do you capture the mouse in the custom control on the mousedown event? Try capturing on the mousedown and releasing the capture on the mouseup.