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.
Related
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.
I have an event to fire, named ValueGenerated. The code that generates values and fires ValueGenerated is running in a thread and the method which recieves this event is on a form.control (i.e. a form). As UI thread does not allow another thread to change the UI I wrote the following code on the event generation:
if (ValueGenerated.Target is System.windows.form.control)
{
Control targetForm = ValueGenerated.Target as control;
targetForm.Invoke(ValueChanged,new object[]{this,args});
}
But I think what happens if the event is registered by more than one methode. For example, by two or three destinations. Why on the event and delegate classes we have just the Target property which returns the instance object of the last method added? Do we always need just the last one?
You're doing it wrong.
As noted in the comments, you can get the full list of invocation targets by calling GetInvocationList() on the delegate instance. Then you can invoke each target individually.
But this is not the right way to do it. Your event should treat all handlers the same.
If the event is the kind of event that is always raised in a background thread, and is always handled by a UI object, then it should always use an appropriate mechanism to dispatch to the UI thread. See the BackgroundWorker class for an example of this sort of design, specifically its ProgressChanged and RunWorkerCompleted events.
If either of those conditions are not true, then your event should not attempt to deal with the cross-thread invocation in any way. Subscribers to the event that have thread affinity should be expected to deal with that themselves.
Unfortunately, there's not enough context in your question to provide any more specific advice than that. The only thing that is clear is that you've started down a dead-end road. Turn around, come back, and take the smoother path. :)
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.
C# WinApps: Is there any way that I can check if something like CTRL-V is pressed but not in the KeyDown,PreviewKeyDown,KeyPress,etc ... events? those are being eaten by some other parts in my App and it is so hard to find them so I thought Ok for this contorl lets check the pressed keys in its GotFocus event! Is it possible?
Not sure what you mean by the events being "eaten". Events can call multiple handlers. So even if the event is already being subscribed to by one handler, you can subscribe to it with another handler and it should work just fine.
Another option would be to subclass the control you are using and use the subclass instead. Then you can override the On{event} methods and do anything you want with those (be sure to call the base method as well to ensure the behavior of the original class is still in place).
HTH
Here's what I am working with:
Part of my project is a windows form app. I want to basically capture every event that fires and has listeners. So some button's click event, some checkbox's check event, everything on a basic "Control" event list. So that event still fires, but also fires my event.
Is there a generic "some event fired" event under the hood I can tap into, or is there a way using reflection to enumerate through all objects in my form, parse out all the events, parse which have listeners, and then subscribe all of them to a generic event elsewhere in addition to where they are already going?
Anyone know how to do this?
You fundamentally can't do this: an event is a black box with just "subscribe" and "unsubscribe" functionality. So while you can use reflection to find out all the events, you can't reliably detect which have been subscribed to. For field-like events you could fetch the backing field and check whether or not it's null, but even that's not reliable - to avoid null checks, the author may have written something like this:
public event EventHandler SomeEvent = delegate {};
For other events, you'd have to work out what subscribing to the event actually does - for example, it might use EventHandlerList.
Basically, you should rethink your design so you don't need to do this.
Doesn't the fact that a subscribed event got fired indicate it has subscriber(s)? So then all you would need is a list of subscribable events, which you can validate against during an intercepted call.
You can intercept a call using any AOP framework. For instance, by using Unity Interception, you can do something like this:
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
// 1. assuming that you are keeping a list of method names
// that are being subscribed to.
// 2. assuming that if the event is fired, then it must have
// been subscribed to...
if (MyReflectedListOfSubscribedEvents.Contains(input.MethodBase.ToString())
{
HandleItSomeHow();
}
// process the call...
return getNext().Invoke(input, getNext);
}