views:

491

answers:

5

I have a WinForms app that is retrieving data from a web service on a worker thread and I need to disable all input to my app until the data is loaded.

Currently, I create a semi-transparent form and place it over my application. When the data call completes I close this overlay form. This works fine accept that it causes considerable performance problems for users running the application over terminal services. I tried making the overlay entirely transparent but that still triggers two redraws of the entire window so this did not help at all.

I know that a common recommendation for handling this is to disable all the controls, but that would also redraw much of the screen so I'm looking for another way to block all user input. Any assistance would be greatly appreciated!

UPDATE: I should have mentioned that we have considered the modal dialog. Currently we show the overlay, start the data access thread then construction the form. If there is no better way to block input (App.BlockInput() might be nice) then we could use the modal dialog idea, but we would need to wait until the form construction had completed and there isn't currently a nice, central location to do this.

+4  A: 

You could display a small modal (modalForm.ShowDialog(yourForm)) form with progress bar rolling on top of your app. This won't cause big areas to be redrawed.

Nikolay R
Aargh, you beat me to it by mere seconds... I agree, this is probably the best way to resolve this. Also, a modal dialog has the advantage that it's unlikely to confuse the user as much as a locked-up form would.
Daniel Pryden
Sorry, I forgot to mention that we had considered this. You can see my update to the question for more info. This may be are best option, but doesn't fit into our existing code base that well.
Jim Clark
Daniel, oops. Good old times of WinForms. Silverlight all async model is so hard to cope with. Jim, maybe you could show us some code. Is constructing a form so expensive that you take it into account? Seems to be a "bed smell" in code.
Nikolay R
I would agree that we may have a "bad smell" here. We have a fairly complex UI with layers of controllers managing windows. We may be able to implement this pretty easily but it seems like such a common scenario that I'm surprised there isn't an easy, centralized way to do this, like App.BlockInput().
Jim Clark
+2  A: 

One thing you could try for keyboard input is setting the KeyPreview property of the form to True. This will pass all keyboard events to the Form object first instead of to the individual controls. Create an event handler for the KeyPress event of the form and in there you can set the Handled property of the KeyPressEventArgs to True to prevent the key stroke from being passed to any of the controls. If you're currently retrieving data from the web service, set the Handled property True otherwise set it to False and the key stroke will be passed to the controls.

If someone has a good idea on how to handle the mouse input yet you're set.

TLiebe
Thank you for this suggestion. I have used KeyPreview before but had not even considered that.
Jim Clark
+3  A: 

If your app really is blocked while the operation is running, I'd do what Microsoft frequently does: open a modal dialog box with some kind of throbber animation or ProgressBar, and a Cancel button. Redraw is limited because you're only drawing the size of the new dialog, and input to the rest of your application is blocked because the dialog is modal. Also, users are much more willing to wait when you have some kind of status updates and or animation, because it looks like the computer is "working".

However, if there are operations your user can do while your web service request is running, it's better to leave the controls accessible. At very least, there should always be a way to interrupt/abort the process.

Update: Since you now changed the question: How long is it taking to construct the modal dialog? Why not simply construct the dialog empty, and then populate its controls? If all you have is a small dialog box with a single button and a single ProgressBar, then calling dialog.ShowDialog() should happen faster than your user can interact with your UI. Is that not the case?

Daniel Pryden
A: 

I'd typically create a LockUI() and UnlockUI() functions in my form that toggle controls and flip a local form field that acts a flag to indicate a long running process. This approach works really well if you use a command pattern.

As previously mentioned, you could toggle keyboard input by using the KeyPreview property of the form (as suggested by TLiebe).

As far as mouse input is concerned, you could disable mouse activity by hooking the WinProc messages and intercepting mouse input messages. This is basically what KeyPreview does.

bryanbcook
+2  A: 

Using Application.AddMessageFilter might do it: http://stackoverflow.com/questions/804374/capturing-mouse-events-from-every-component-on-c-winform

roland