views:

47

answers:

4

Is there any way to temporarily disable ALL events on an windows form?

I have situation where processing on a secondary thread is being corrupted by events on the main thread. (The main thread events are modifying the contents of controls that are data-bound to variables used by the secondary thread.) Looking for a way to "lock" the form until the processing on the secondary thread finishes. (Obviously, moving processing to the main thread would do this--but it would also "freeze" the UI, which isn't what I want either.)

A: 

Blocking events on a form is effectively freezing the UI. Events occur as part of standard message handling in the message pump - stopping events would freeze the UI.

It would be better to disable UI features while your work is processing (such as disabling the individual buttons, etc), then re-enable them when it completes. This will allow normal events to occur, but prevent the user from triggering a button click, etc.

Reed Copsey
The problem is the events causing me problems are still enabled on disabled UI elements. (Believe it or not, a lot of events still fire when users interact with disabled elements.) But I like the of where you are taking this.
Jeff
@Jeff: Which event is causing you the problems? You can always take this one step further - instead of just disabling/enabling controls, make a small routine that unsubscribes and resubscribes to the events on the controls... (I've done this for mouse overs, in windows forms code, for example.)
Reed Copsey
+1  A: 

A few different options should allow you to solve this:

  • Disable any controls that would fire events that are not thread-safe,
  • Lock all code that modifies the variables, or all event-raisers, using a Monitor on the same Object, or
  • Detach and reattach all handlers.

I would go with the first option, possibly coupled with setting the cursor to an hourglass and/or setting a message in the status bar. It's simple, it works, and it doesn't cause the actual UI to freeze. I would avoid the third option as it will "break" the behavior of your form while it's working. The second option's efficacy is dependent on how you implement it; it could very easily freeze the UI which is something you don't want to do..

KeithS
This doesn't solve the problem. The event handler might have already started running. Standard threading race.
Hans Passant
Then lock all code that touches the data bound to the controls using locks on a single object reference; I mentioned that as another option. However, it will probably block the UI thread.
KeithS
+1  A: 

This is a standard threading problem. Even if you could block the events, very hard to do, it still won't solve the problem. Because the event handler might already have started, but not yet finished, by the time you want to start blocking.

And it has a standard solution, use the lock keyword.

If that is not practical because the user needs to change several controls before you can run a query again then you need to terminate the running thread first. BackgroundWorker.CancelAsync for example. If that's not practical because it takes too long to cancel the query then you need to set the controls Enabled property to false while the query is running.

Hans Passant
A: 

If you want to suppress all events resulting from a PostMessage, then you can implement the IMessageFilter interface and use AddMessageFilter.

This will probably trap all of the events in which you're interested. It won't trap everything, for example an event resulting from a SendMessage. To trap absolutely everything, you will need a thread-specific application hook.

Note that this will cause the UI to freeze completely, which may not be what you want. A better apporach might be to intercept the appropriate events and suppress unwanted behaviour while your background work is in progress.

RoadWarrior