If you create a standard C# WinForms application, you fill find that a form has two events: Move and LocationChanged.
Move is raised when the form moves and LocationChanged is raised when the form location property changes.
Surely if the form moves, the location property will change, too?
What is the difference between the two events? In which case will one fire and not the other?
The Move and LocationChanged events are declared on the Control class, which is then inherited by ScrollableControl, ContainerControl and finally Form.
According to the source code, OnLocationChanged calls OnMove before it invokes the LocationChanged event handler. So, the OnMove event will be raised first and then LocationChanged. You could in theory handle both events knowing that Move will be occur first.
If you look through the source you'll see that LocationChanged is raised when the bounds change (or similar events). You'll also notice that the only thing which actually invokes OnMove is in fact OnLocationChanged.
According to MSDN, the LocationChanged event:
Occurs when the Location property value has changed.... This event is
raised if the Location property is changed by either a programmatic
modification or through interaction.
It makes no such distinction for OnMove, where it merely states:
Occurs when the control is moved.
Which is curious since the two events are tied to each other.
This is however how one specific class handles these events. I did a bit of searching through the reference source and I couldn't find anything (inheriting from Control) which explicitly called OnMove other than the instance I've already cited. That doesn't mean they don't exist or that one couldn't invoke it separately in their own subclass of Control.
Both Move and LocationChanged events are interconnected. I believe there is no situation when one if fired and the other is not. The difference is that they belong to different categories of events.
The Move event has [SRCategoryAttribute("CatLayout")] attribute.
The LocationChanged event has [SRCategoryAttribute("CatPropertyChanged")] attribute.
Related
The UIElement class defines static RoutedEvent members MouseLeftButtonDownEvent and MouseLeftButtonUpEvent -- but there is no MouseMoveEvent. As far as I can tell, neither does any class in the framework hierarchy. There is the regular event definition:
public event MouseEventHandler MouseMove;
So you can write:
void AttachHandler(UIElement element)
{
element.MouseMove += OnMouseMove;
}
but you can't use the other form, which allows you to subscribe to even handled events:
void AttachHandler(UIElement element)
{
element.AddHandler(UIElement.MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);
}
So my question is twofold:
Why is there no MouseMoveEvent defined anywhere?
Is there a workaround that allows you to get a notification for MouseMove events even when they are handled?
Edit
I see that the MSDN docs acknowledge this as a limitation:
A limitation of this technique is that the AddHandler API takes a parameter of type RoutedEvent that identifies the routed event in question. Not all Silverlight routed events provide a RoutedEvent identifier, and this consideration thus affects which routed events can still be handled in the Handled case.
Edit #2
Per #HansPassant, the general answer is that "MouseMove" events cannot be marked as "handled", thus they always bubble. This is true of the TextBox, except for an apparent edge case: when you click on the TextBox's text area, thus activating the drag-to-select thingo, the "MouseMove" events no longer get triggered. I have no idea why that would be.
Note -- for anyone curious -- I am trying to write a behavior that allows the user to drag/drop a TextBox. The TextBox control intercepts mouse events by default, in order to allow text selection.
It is explicitly mentioned in the MSDN article:
MouseMove cannot be used with AddHandler because there is no Handled in its event data
So that answers your questions:
Why is there no MouseMoveEvent defined anywhere?
Because none is needed.
Is there a workaround that allows you to get a notification for MouseMove events even when they are handled?
You don't need one, they can't be handled and thus always bubble. The Window's MouseMove event handler will see them.
So I am registering some event handlers in an object's loaded event.
tv.PreviewMouseDown += new MouseButtonEventHandler(SignalScrollViewer_PreviewMouseDown);
but I had two questions.
If loaded occurs twice and it tries to add the event handler again will there be problems?
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
Yes that will cause another subscription which causes the handler to execute twice. You can remove the loaded handler inside the loaded handler.
MSDN:
Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Loaded cannot be assumed to occur only when a page is first loaded through navigation to the page.
If the object is gone it cannot raise any events, so no need to do anything about that. And the handler will not keep the object alive (it's the other way around).
If loaded occurs twice and it tries to add the event handler again will there be problems?
The event will get subscribed multiple times. You may want to handle this scenario.
How should I handle unregistering the event? Will it automatically handle unregistering on destruction or do I need to handle it in some event like unloaded or something?
You can unregister it in Unloaded or similar. In general, you only need to do this if object on which the event you're subscribing will live beyond your lifetime. If it's an object within yourself (ie: subscribing to an event on a button within a user control, from the user control), unsubscription isn't required.
One normally subscribes to events from inside the constructor, i.e. immediately after the tv object gets created. If you keep those together, it's not possible to subscribe multiple times to the same tv object.
One would want to register event handlers in Loaded and remove them in Unloaded, so any objects which handle the event would have a defined lifetime. So, one would wish that every Loaded event occurs only once when the control is shown, and is followed by an Unloaded event when the control is removed from sight.
However, Loaded might be raised several times, depending on your layout: Expander causes Loaded to be raised twice, but Unloaded only once on controls inside the Expander, and TabControl might reuse the same controls for different TabItems (different data) without raising Loaded or Unloaded in between.
I found two ways to get around this: Either use DataTemplates (this helps at least in the TabControl case), or use the DataContextChanged event to register / unregister the events, which is a good way to ensure that the object which receives the events is always the current DataContext (i.e. ViewModel).
You might also want to check out the following link for more information on attaching and removing event handlers and other behaviours: http://wpfglue.wordpress.com/2009/12/11/the-sticky-component-framework/
that is a question I have been asking myself for a while.
Giving a certain flow of events, can I when handling one of them, stop the next ones to be raised?
For example, when collapsing a node which child was selected in a treeview (winform), the events are raised like that:
BeforeCollapse
BeforeSelect
AfterSelect
AfterCollapse
I could stop them by using a class member, but I was wondering whether there was a built-in function or just another way (a more elegant way) to achieve this, by acting directly on the events queue.
Any idea?
Not easily, no. The order of the events firing is controlled by the TreeView control class, and there is no built-in way to prevent events from firing. But you have a couple of options:
Create your own TreeView class that inherits from the base class,
then add a bool property to prevent the events from processing.
Then you can override BeforeCollapse, etc. to check the bool
before calling base.BeforeCollapse.
Just create a bool flag, and check the flag in each of the events.
No there is no way to do that for that type of event (you are asking for TreeView).
Like for example could be managed KeyEventArgs.Handled via built-in mechanism.
You can use some instance (boolean ?) value to manage the flow,
or you can, unsubscribe from the event that you don't want more recieve, but after subscribe to it again. Sounds rough solution, but sometimes turns out reasonable one.
even if the event are raised nothing will happen if you don't bind an event handler to them. In this case you can just remove the handler using the code below:
object.Event -= new EventHandlerType(your_Method)
Otherwise you should create your own custom control
according to OnBeforeCollapse you get an TreeViewCancelEventArgs which has an Cancel property. Setting this to true should stop the flow, but will also not collapse it.
Same goes for OnBeforeSelect.
The only times you can easily "cancel" an event is if the event handler has the CancelEventHandler delegate type. Even then it doesn't really cancel it as much as set a flag for the remaining events that makes it skip performing all the events subscribed to it.
If you did have a CancelEventHandler type (which these don't) you'd simply set Cancel to true on the event object itself in the handler.
Plenty of other answers give you suggestions for what you should o. I'd just go with your idea: set a 'event cancelled' flag in your control class, and check it. When the last event in the series gets called, reset it.
I've written a custom control (an immediate child of Forms.Control) in C++/CLI that will offload most of its processing and all of its painting to a separate legacy-ish MFC control. I'm wrapping this control in a C# application. I need to be able to drag items onto the C++/CLI control from a UserControl on the same form in the application.
For some reason, my custom control isn't receiving any of the drag/drop events in spite of my setting AllowDrop to true. (I've verified that AllowDrop is true at runtime.) I never get an opportunity to examine/manipulate the DragEventArgs because the callbacks are never called.
I've verified that drag and drop otherwise functions normally. If I replace the custom control with a Panel, for example, I get the dragdrop callbacks just fine.
Is there something additional that must be implemented in a Control to support dragdrop callbacks? Is there something subtle that must be dealt with for the system to recognize that a control is being hovered over and should be the target of a dragdrop operation?
Notes:
The MFC control that will do the painting is not yet available to me, so I'm just painting a simple gradient background (via OnPaintBackground) that I'm trying to paint to. I'm also painting a smaller gradient rectangle in OnPaint.
I have tried setting AllowDrop = true both before the custom control's handle is created (via designer/constructor code) and after the handle is created (via OnHandleCreated override). No difference in behavior.
The drag cursor never changes from the default "no drag available" cursor over the custom control.
I'm calling this.DoDragDrop() from an event handler for a MouseDown event on a subcontrol of the UserControl.
DragDrop seems to work ok with another custom control I created in C# in the same assembly as the user control. It captures the callbacks just fine. Only my C++/CLI control isn't seeing them. The initializecomponent goo is irrelevant. I can comment it all out with no change.
I figured out my mistake, so I'll answer it here in case someone else makes a similar mistake. It looks like I did hint at the answer in my question, though I didn't realize it at the time.
In my question, I stated that
I have tried setting AllowDrop = true
both before the custom control's
handle is created (via
designer/constructor code) and after
the handle is created (via
OnHandleCreated override). No
difference in behavior.
This is actually where the breakdown occurred. I failed to read the OnHandleCreated documentation that specifically states I needed to remember to call the base class' implementation of OnHandleCreated.
Reflector shows that Control::OnHandleCreated is responsible for setting up Control's internal event notification structure. I never called Control::OnHandleCreated, so the internal event notification structure was never getting properly built up. This eventing structure (reflector again shows) is responsible for firing OnDragOver, etc, so the missing eventing structure resulted in my missing events.
Answer:
Be sure that you call the base class implementation of OnHandleCreated when you override it!
I'm using a large open-source control and I can't seem to find the code that handles a double-click event. Could I perhaps have the debugger break when a double-click occurs or otherwise learn what code is associated with that event?
Well, I stumbled around until I found the code I was looking for.
BTW, before the DoubleClick event occurs, a MouseDown event occurs for the second click, with MouseEventArgs.Clicks == 2.
The event you're probably looking for is Control.MouseDoubleClick, which is raised whenever a control is double-clicked by the mouse. Or, if you're deriving from Control, you can override OnMouseDoubleClick.