We have a huge application that runs through a mass of code for every click you do. In one specific case, where you have a specific control in plain sight (so that the events are firing) and you load some special data into it and then click on the new button the form loses some events, like the closing event (you cannot close the form anymore and stop points in debug mode are not triggered) and one validating of a ComboBox that is really needed for the program. The only thing you can do when you get this kind of error is to restart the application to get out of this subform. But I tested it for 2 days now and even when I activate all points in the 'Exception Settings' I don't get any exceptions. The form (and other controls) are not losing ALL events. The resize, for example, still works. Even a specifically called AddHandler is not firing. Can you iterate somehow through the EventHandlerList? Or is there any way to test WHERE the event dies? Somehow any ideas on how to debug such a problem?
I am using simple event handler in the while loop.
I have value change event for the boolean button. There is some code that takes 3-4 seconds to execute.
The problem is I am not able to click anything on my Front panel during this period. Is it possible to allow the user to click on other controls when event handler is working on some case (as I understand the event handler is able to collect all events and process them ASAP)?
I fully agree with Mikhail N Zakharov's answer, but anyway your problem can be easily solved by just unchecking the checkbox named Lock panel until the case for this event complates
Please see screenshot below.
PS. Once again it is not the best practise to make event structure to work for 3-4 seconds.
I think you need to restructure your application to make it more responsive. LabVIEW best development practices suggest keeping event handler code as fast as possible. One of the ways to handle this would be to send a message into the queue on the change of this Boolean control and process the queue in a separate loop.
I have a vb.net based windows application, where when "GO" button is clicked a bunch of data is loaded into DB. So in my application as soon as "GO" button is clicked I want to just disable it and would like to enable it back when the uploading has completed.
Now in my specific method for btnGo_Click() I have:
btnGo.Enabled = False
as first line and
btnGo.Enabled = True
as last line in the same method.
But I fail to understand why the "GO" though appears as being disabled still allows click when processing is going on. Also if I remove the last line, it gets disabled permanently and doesn't allow the click event.
Kindly suggest what am I doing wrong?
Edit (Dated: 25th Jan 2012): I made changes as suggested by our collegues, but I am facing a new issue here. I am facing an issue where the textbox gets updated but not always. I have updated my textbox in "_ProgressChanged" event of the background worker thread. In my case if there is 10 records uploaded. Then there are 10 lines of updates that are expected in the texbox. But only few lines are shown in the textbox. Is it the repaint issue again? Kindly suggest...Because all other things are done as per your suggestion
You're not doing anything wrong. The problem is that the UI doesn't get updated until the code inside of your event handler method finishes executing. Then, the button is disabled and immediately enabled in rapid sequence.
That explains why if you forget to reenable the button control at the end of the event handler method, it is still disabled—because you told it to disable the button in the first line of the method.
This is a classic case of why you should never perform long-running computational tasks inside of an event handler method, because it blocks the UI from being updated. The computation actually needs to happen on a separate thread. But don't try to create the thread manually, and definitely don't try to update your UI from a separate thread. Instead, use the BackgroundWorker component to handle all of this for you automatically. The linked MSDN documentation has a great sample on how to use it.
Disable the button before starting the BackgroundWorker, and then re-enable it in its Completed event, signaling the completion of your database load.
Since you're trying to execute a function that can take some time, I'd advise you to make use of threading. In .NET there's a BackgroundWorker component which is excellent for performing tasks asynchronous.
On button click, invoke the BackgroundWorker like this:
if not bgwWorker.IsBusy then
btnGo.enabled = false
bgwWorker.RunWorkerAsync()
end if
And use the completed event to enable the button again:
Private Sub bgwWorker_DoWork(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles bgwWorker.DoWork
' Do your things
End Sub
Private Sub bgwWorker_RunWorkerCompleted(ByVal sender As System.Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles bgwWorker.RunWorkerCompleted
' Called when the BackgroundWorker is completed.
btnGo.enabled = true
End Sub
In the example above, I've used bgwWorker as the instance of a BackgroundWorker.
The button click event is handled as soon as the UI thread has idle time.
After you disable your button, the UI thread is keept busy by your code. At the end of your method, you re-enable the button, and after that you exit the method and allow for idle time.
As a consequence, the button will already be enabled at the point in time where the click event is handled, so your click is "recognized".
The solution is, as others already suggested, to use a Backgroundworker.
Dont try to use doEvents() as a solution (never do), since this would be prone to introduce other subtle problems. That said, you can prove the above explanation with some experimental doEvents in your code. You will see that the click is discarded if a doEvents is performed before the button gets re-enabled. On the other hand, performing a doEvents directly after the button.disable (to "update the GUI") will not help if it is executed before the click.
If your btnGo_Click() is ran inside main thread, UI could not be updated correctly inside a time-consuming task.
The best way you can do what you need is running your method in a BackgroundWorker.
I just tried disabling a button, Updateing the form, Sleeping, and enabling it again. It still performed the click (A click that was done while it "slept" with the button disabled) after it was enabled.
I guess forms "remember" clicks.
(EDIT: I did this in C#.)
It's usually not a good idea to manage the state of a submit button. Instead, perform validation on submit.
I would like to add 2 enhancements to the answer generally described here which is to 'do the work in another thread'.
Ensure button.enable=true always gets called
1.a. You should use a try block in button_click . If there is an error in launching the thread, CATCH should re-enable the button.
1.b. The task complete call back should also ensure the button is enabled using try/catch/finally
1.c The task timeout should also re-enable the button
A common error based on exactly the situation described here is rapid-clicker-person clicks the button twice in rapid succession.
This is possible because its possible, even if unlikely, that 2 click messages get queued and processed before the button is disabled. You can not assume the events happen synchronously.
IMHO a best practice is to use a static variable. Initialize it to 0. Set it to one as the very first statement and set it to 0 following the guidelines in POINT 1.
The second statement in button click should simply RETURN/EXIT if the value > 0
If you are using a worker thread, the static variable may have to be located in that code. I would not advise making it a form level variable.
I had a slightly different issue not being able to call click.
I have a routine that turns the UI on/off based on a validation routine.
i will say that I disagree w/ the suggestion to do validation in the submit. The button should not be enabled if we are able to tell the form is invalid.
My issue was that I was calling the validation from several places. One of the calls was the CustomCellDraw event of a grid which was firing very frequently.
So while it appeared that I was simply disabling/enabling the button a few times, I really was doing this almost continually.
I was able to trouble shoot by placing a label on the form and kind of doing a console.log thing. I immediately realized button.Enabled was flickering, which led me down the correct trouble shooting path.
I realize this addresses a different root cause than op described. But it does address the problem the op describes.
I have a DataGridView control in my Windows Forms Application.
I am adding rows to the grid using a background thread. I change the form's cursor to Waitcursor when the process starts and back to Default when it ends. This works well for the form, but not for the grid. When the form's cursor is changed back to default, the grid's cursor does not change, although the cursor over the rest of the form does.
Does this have anything to do with the fact that I am updating the grid from a background thread? (The cursor is being changed from the UI thread directly).
Edit: The background process raises an event, the handler checks the InvokeRequired property of the grid and decides if it needs to "Invoke" the method again from the main thread. So, in effect the actual UI update happens from the appropriate thread. I am not sure if this means that I am "using a background thread" or not. :|
I've had some problems doing single thread updating of my datagrids, where the datagrid did not reset to normal cursor after i've had waitcursor to true.
What I did was right after i went
this.UseWaitCursor = false;
I added
DatagridviewFoo.Cursor = this.Cursor;
Maybe it's just the same problem for you
I've had this problem as well. It was difficult to track down the cause, let alone a solution.
This issue only ever happened when I had a dialog box over the DGV control and the mouse clicked on a button to close the box such that when the box closed, the mouse was over a (resizable) column border. If the cursor ended up over a cell, the problem didn't arise. If I had to guess, I'd say the grid was seeing a column resize event as soon as the dialog box closed which wasn't properly handled.
Using Cursor.Current = Cursors.Default fixed my issue (without the need to explicitly reset the control's cursor). But maybe be aware that Application.UseWaitCursor = False didn't work even with the explicit control cursor reset.
I had a similar problem, but neither of the posted solutions worked for me. Mine was not caused by clicking a button above a movable column separator. It just randomly happened after opening and closing a dialog box. I'm pretty sure it came down to timing because .Net/Windows has issues when it comes to setting cursors and actually having them take effect. To try to overcome that, the library we use for showing and hiding the wait cursor calls - ack! - Application.DoEvents. I set a breakpoint in OnCursorChanged and saw that the cursor was sometimes actually getting set on a latter call to Application.DoEvents (used to keep the UI responsive while waiting for the file system to release a write lock on a file). So I guess sometimes the default cursor was getting turned back on before the call to set the wait cursor had fully taken effect. Anyway, my brute-force approach is to call
Cursor = Cursors.Default;
in my override of OnCellEnter (which always happens after the grid gets refreshed following the dialog box being closed). I'm not particularly proud of this, but it seems to work.
We have a question regarding VB.Net 2008.
We are used control array in vb.net and third party timer controls.
When handle received from external application to timer control event procedure,
after this form becomes blank and controls disappear.
What we have to do to persist the controls.
You asked what you have to do to persist the controls. It's not clear whether you mean winforms or webforms, but I can answer for both possibilities:
If it's the former, you have it backwards. The default behavior of all controls is that they are "persisted" until you tell them otherwise. If anything disappears, it's because you have code somewhere that tells it to. That's where you need to start looking.
If it's webforms/ASP.Net, the problem is that you don't understand the page lifecycle. Everything that raises server events, including your third party timer controls, causes a post back. That's how events work - the browser posts the form back to the same url in such a way that the server knows to call a your event code at the right time. The thing here is that as far your server is concerned, it's still just a new http request, and that means you're working with a brand new instance of your page class every time this happens. If you've previously added some controls to your page, it doesn't matter. That was an old instance that was discarded and probably disposed by the time the page was visible in the user's browser. If you want to keep those controls, you need to make sure you add them to the page on every postback.