I have implemented a UserControl. Then I would like to handle an event that is originally handled by Window (keyboard press). What is the best way to route the event caught by another component (higher in the components' tree)?
Thanks in advance for the replies and hints!
Cheers
It depends on the event you're trying to access. If it's a Preview event and the Window is setting e.Handled to true you'll need to use the method Alex suggests to circumvent the Window's handling of the tunneling. If it is a bubbling event (i.e. KeyDown) you don't need to do anything special since bubbling events hit the handlers on child elements first and go up the visual tree so the Window handler won't occur until after your UC's.
One thing you need to be careful with using Key events is that the event is only going to get picked up by your UC in the first place if the Focus is on or inside of it. This isn't something you need to worry about with things like Mouse events since they start at a specific location in the tree.
I believe you cannot gurantee that.
Window class is wrapping Win32 message-based event model and this will be the only WPF entity which will have access to those information.
I suggest that you create an attached property (which will be used by the Window) and implement the routing of the events yourself so that controls could subscribe to.
You can attach the routed handler specifying that you want to handle handled messages as well:
this.AddHandler(routedEvent, handler, true);
where this is an UIElement or derived class.
However there may still be events (key presses in this case) which don't make it past the window, not sure.
Related
I would like to have a clear explanation about how to determine in each situation which control is the sender and which one is the source for a WPF Routed Event event both in the case of tunnelling and bubbling events.
Edit:
Suppose you have an event handler and two controls one child of the other. The handler is in the parent control. How would I know beforehand and without debugging which control is passed as the sender and which one as the e.source? And does this change when you consider bubbling or tunnelling events?The general concept of events is clear to me, but I would like to understand which parameter to use in the eventhandler to indentify both controls without debugging
There probably isn't an exhaustive "clear explanation", because there are so many ways you could end up with events. Especially when a lot of events use EventArgs.Empty, because there are no other details, other than the source.
http://msdn.microsoft.com/en-us/library/17sde2xt(v=VS.100).aspx
or, more specificly:
RoutedEvent: Source vs. OriginalSource
Do you have a specific question you need answered?
Edit: from the above linked article, with an answer similar to your comment
Consider a custom control (called CustomControl1 in this example) that is composed of a TextBlock.
When a MouseDown event is raised on the TextBlock, the OriginalSource property will be the
TextBlock, but in CustomControl1's handler, the Source will be changed to the CustomControl1
object so that other elements along the event's route will know that CustomControl1 received a
MouseDown.
I have a visual tree with UIElement3D objects that get focus and fire keyboard events. I would like to catch them at their parent level which is of type ModelVisual3D. However WPF does not allow me to subscribe for Keyboard.KeyDown events on ModelVisual3D (it throws an exception at runtime).
The environment (shell) which this 'parent' lives in does not know anything about its children, which means it does not know anything about the events they will be expecting. So it cannot subscribe for this event and call a method on the relevant child.
Does anybody have an elegant solution for this problem?
Alex.
I solved it. Changed the parent to be ContentUIElement3D and the problem was solved.
I found some of my winform application controls, such as DataGridView and ToolStrips, are referred to by UserPreferenceChangedEventHandlers. I have no idea what setting of the controls will generate such references and why such references keep my control alive in memory. How can I remove such references from that event? Thanks.
It is the delegate type for the SystemEvents.UserPreferenceChanged event. This event fires when Windows broadcasts the WM_SETTINGCHANGE message. Which typically happens when the user uses a control panel applet and changes a system setting.
Several controls register an event handler for this event, DataGridView, DateTimePicker, MonthCalendar, ProgressBar, PropertyGrid, RichTextBox, ToolStrip, NumericUpDown. They typically are interested in font or cue changes and anything that would affect the layout.
SystemEvents.UserPreferenceChanged is a static event. Registering a handler and forgetting to unregister it causes a memory leak, it prevents the control from being garbage collected. The listed controls ensure this doesn't happen, they unregister the event handler in either the OnHandleDestroyed() or the Dispose() method.
You'll get in trouble when neither of those two methods run. That will happen when you remove the control from the container's Controls collection and forget to Dispose() it. While forgetting to call Dispose() is not normally a problem, it is a hard requirement for controls. It is easy to forget too, controls are normally automatically disposed by the Form. But that only happens for controls in the Controls collection.
Also be sure to call Dispose() on forms that you display with the ShowDialog() method, after you obtained the dialog results. The using statement is the best way to handle that.
One more excruciating detail is important about the UserPreferenceChanged event, it is often the one that deadlocks your app when you create controls on a worker thread. Typically when the workstation is locked (press Win+L). Which cannot come to a good end when you use the controls I listed, the SystemEvents class tries to raise the event on the UI thread but of course cannot do this correctly when more than one thread has created them.
Also the kind of bug that can have a lasting effect, a splash screen for example can get the SystemEvents class to guess wrong about which thread is your UI thread. After which it then permanently raises the event on the wrong thread. Very ugly to diagnose, the deadlock is well hidden.
I hope this makes sense.
I have created several WPF User Controls. The lowest level item is 'PostItNote.xaml'. Next, I have a 'NotesGroup.xaml' file that has an ItemsControl bound to a List of PostItNotes. Above that, I have a 'ProgrammerControl.xaml' file. Each ProgrammerControl has a grid with four different NotesGroup user controls on it (and each NotesGroup contains 0-many PostItNotes.
Then, I have my main window. It also has an ItemsControl, bound to a list of Programmers.
So, you end up with a high level visual view of a list of programmers, each programmer has four groups of tickets, each group of tickets has many PostItNotes.
The trouble I'm having, is that I want to respond to a mouse click event in my mainWindow's code behind file.
I can add a MouseClick event into my PostItNote.xaml.vb file and that is getting called when the user clicks a PostItNote, and I can re-raise the event; but I can't seem to get the NotesGroup to listen for that event. I'm not sure if that's even the correct approach.
When the user clicks the PostItNote, I'm going to do a bunch of business-logic type stuff that the PostItNote control doesn't have a reference to/doesn't know about it.
Can anyone point me in the right direction?
You have a couple choices:
Use the PreviewXXX events which are fired during the "tunneling" phase of WPF event routing. The parent controls can always preview the events going down through them to children.
Use the more advanced approach to hooking up events leveraging the AddHandler method to which you can pass a parameter called "handledEventsToo" which basically means you want to know when the event happened "within" you even if some descendent element handled the event itself.
I am going to take a flyer here. You probably don't want to be handling the event that high up; not really anyway. You are catching the event at the lower levels, which is unavoidable. Consider invoking a routed command from the PostItNote click event handler.
The routed commands bubble up and tunnel down through the tree. You can have an architecture where a high-level handler can listen to a logical event (Opening a postit note perhaps?). The handler for this doesn't need to care where the command originates from. It might be from you clicking something, it might be from clicking on a toolbar button. Both are valid scenarios.
It sounds like you are creating some kind of custom UI, am I right? You want the application to respond to the users interactions. That is what the RoutedCommands are for.
I have a 3D application in WPF which basically is supposed to zoom in and out as the MouseWheel event is fired. I have attempted to subscribe everything possible, but can't find what it is which is handling it.
Is there a way to find out what is? Or is there a way to have the event not handled, or for an encompassing UIElement to get to this event before/after the one dealing with it?
If you want to get called even though someone else has already handled the event, try subscribing to the event using the UIElement.AddHandled(RoutedEvent, bool) function. Pass in true as the second argument to get called even if the event has been handled.
You can also try subscribing to the PreviewMouseWheel event to get called when the event is tunneling.