this is my first post. I have a huge problem which make me headaches. I have an app uses WinForms, a TTS (Text-To-Speech) voice and custom-buttons with states.
In my 1st form -main- when I click a button, the app opens a 2nd form above the 1st. Ok.
When I close the 2nd form trough a button, I tell the TTS say something and the form closed itself, viewing again the 1st form. Ok.
The problem starts when I click two times in the button on the 2nd form: the TTS says something, the button closes and the 'second click' is still in the click buffer (or somewhere) and it makes click in the 1st form (which appears 4 seconds later when I hit the button for the first time).
I am using the voice in a Sync mode; if I use the voice in an Async mode, the application ends wit h a nice exception.
If I click three or four times in the 2nd form, the other clicks still remains in buffer and clicks in the 1st form all the times.
I tried to (1) delete the DoubleClick event, (2) delete the event associated to the button and (3) hide the button which is clicked automatically when I return from the 2nd form, (4) hide the 1st form before create the 2nd and restore when it finishes.
Suggestions?
Thanks!
PD: I'm sorry by my English :S
PD2: I've uploaded a very simple example of what happens.
EDIT 2
Having looked at the code I understand the issue you're having now. The reason button clicks are being stacked up is when you call Speak within TTS the application locks up while it waits for the function to finish. Any presses in that time are stacked up until the application is free again to process them, you then close the form instanly before the messages are handled and these are then dealt with in the first form.
I've come up with a few solutions which could work for you:
Use only the SpeakAsync command within your TTS class and introduce a Waiting system where you wait for the speech to finish before doing anything. This will free the application and won't cause the mouse click events to stack up.
After you trigger a Speak command you could access the Windows message list and clear all the mouse click events that occurred before the process finished. Unfortunately, I'm not sure how you'd implement this as I've not done this before. I think you need to overwrite the WndProc function but again I'm not sure. This might be also be a bit dangerous as you may end up clearing a perfectly valid or important system message by mistake. Sorry can't provide any more help on that one.
Implement a background worker in your second form which will process the Speak commands seperately on a background thread. This again will free the application so the mouse click events won't stack up. I've modified your sample project and zipped it up for you to take a look. If you want I can explain further but essentially it does the following:
Form 2 loads and creates a background worker.
Worker_DoWork and Worker_WorkComplete delegates are created and set in the background worker. These functions are called when the worker is started and after the worker has finished.
Form 2 triggers the background worker to start. The background worker then sits in an infinite loop waiting for commands to process.
When the "Hello" button is pressed this sets a SayHello boolean to true, the worker spots this, carrys out the appropriate speak function and then resets the boolean ready for the next press.
When the "Close" button is pressed a CancelASync request is called in the background worker.
CancelASync interupts the BackgroundWorker's main loop (CancellationPending becomes true). The appropriate speak command is sent and the cancel property of the DoWorkEventArgs is set to true before breaking out of the BackgroundWorker's main loop.
Breaking out of the main loop causes Worker_WorkComplete to be called where the form is then closed.
I hope you can follow the example (linked below) and I've explained it well enough here. I prefer this solution as its quite extendable, you can add more conditions within the main worker thread for example.
Like I said, if you have any questions please ask and I'll try help as much as possible.
Hope this helps.
Example Link: http://www.mediafire.com/?2mf1yahto50ljs6
Use a boolean flag to track whether the form is in a state that accepts the click.
IE - when you open the 2nd form, 'boolean canPlaySound = true;' When the button click event fires, only play the sound if canPlaySound is true (and set it to false before playing the sound).
The next click will be ignored because canPlaySound = false. You won't play the sound.
Related
I am new to Winforms development and I do not see a solution yet on Stackoverflow, but may have missed it.
I have a dialog box that comes up, but due to application startup processing, it is only half displayed for the first 2 seconds or so (i.e. shows border and the background except where controls will be shown). The control locations are white until controls are displayed after that initial 2 seconds.
I understand I could put a delay in the application while it is starting up, but would prefer something like a Suspend() / Resume() pair in strategic locations. I have tried putting in the load event, but that had no effect. Also, it looks like Refresh() breaks the Suspend/Resume. Ideas appreciated since I would like to use this strategy elsewhere in the application as well. I am wondering what is an approach that will work for this and other areas that flicker badly (or outright show a long delay before fully displaying like this startup dialog box as described).
Try putting your long-running code in the Load event handler instead. By putting it in the Shown event handler, it causes the form to freeze until it's done loading because the shown event handler is not letting other events in the message loop, e.g. the Paint event -- get processed. At least if you put it in the Load event, all the long running code will occur before anything gets displayed at all.
If you don't like having any delay, consider putting the long running code in a timer that kicks off in the Shown event.
Then there's always the BackgroundWorker if you want to get more advanced with long-running code.
Here's the scenario:
Platform: Windows
IDE: Microsoft Visual Studio 2008
Language: C#
.NET framework: 3.5
My application contains 2 buttons - "Load Data" and "Stop Loading Data" and a multi-line textbox. Upon clicking "Load Data" button some data starts getting loaded in the textbox. To prevent the user clicking on the "Load Data" button multiple times, I have disabled that button once it is clicked. When the entire data gets loaded in the textbox then the "Load Data" button gets activated again. On the other hand on clicking "Stop Loading Data" button the loading of data is stopped (if user wishes to stop it before loading the entire content).
As stated earlier, to prevent the user clicking on the "Load Data" button multiple times, I have disabled that button with the intention that user can only click on "Stop Loading Data" button or else wait for the entire data to be loaded in the textbox. I implemented this. At first glance it seemed to work well. But while testing I found that even though the "Load Data" button is disabled, if the user clicks on that button, although nothing happens at that instant but as soon as the entire data gets loaded and the button becomes enabled again, that click made during the disabled state is found to be executed. As if the program was recording the keystrokes and mouse clicks and waiting for the button to become active again. But there are no such keystrokes or mouse-clicks recording facility in my program. What is causing such an activity? How can I prevent such behavior?
Thanks.
One option would be to work with a reentrancy sentinel:
You could define an int field (initialize with 0) and update it via Interlocked.Increment on entering the method and only proceed if it is 1. At the end just do a Interlocked.Decrement.
To make it visible for the user you can disable the button at the beginning of execution and enable it when the execution is finished...
BTW: long-running tasks should be done async (via a separate thread for example)...
if you make your call a synchronous one, it will lock up the entire page until loading finishes.
Otherwise you'd be just doing the method you've already tried, i'd like to see your code for the disabled state, because something tells me you just made it appear to be disabled, and it was still a functional button
Check out this post:
http://www.codeguru.com/forum/showthread.php?t=480279
My first thought was removing the event handler and rebinding it at the end of the click event. This thread suggests using a BackgroundWorker and making it async.
I bet that you can't even move the form until data is loaded, and you also can't stop loading data. The problem is that the whole form freezes until loading is done. You must move the loading part in separate thread.
Well, another silution again:
On begin load simply hide a load button and in place of it (say) show a progress bar. On finish of loading or on stop loading click make load button visible again. In this case you avoid "chain clicks" management you complains about.
Or manage one button. First it is load, on click, instead, becomes stop load. Solution like this you would find often in mobile environment, considering the limited screen space. But I think it can be applied to desktop with great sucess too. Why not?
I'm writing a WinForms window in C# which displays about 12 ListBox and 6 ComboBox controls each with a few hundred to a few thousand items.
It takes a little while to populate these. Not a long while - just a few seconds, but it's nice for the user to have something to look at so they know the program is working away in the background while they wait.
I have a generic "Please Wait" animated borderless top-most window which appears while this happens, however I'm having trouble with the animation.
For most tasks which take a little while, I solve this in the following way:
Program.ShowPleaseWait(); // Show top-most animation
Thread t = new Thread(stuffToDo); // Run stuffToDo() in separate thread
t.Start();
While (t.IsAlive)
Application.DoEvents(); // Run message queue, necessary for animation
Program.HidePleaseWait(); // Hide top-most animation
and it works quite well. Occasionally the stuff in the thread will need to Invoke something and that sometimes causes a small hiccup in the animation, but it's generally not a big deal.
With this form, however, the entire code in the thread is UI code: populating ListBoxes and ComboBoxes. That means everything would have to be enclosed with Invoke blocks, which doesn't make any sense because then there's no point in having it run in a separate thread in the first place.
Aside from scrapping the whole worker thread for this particular case and throwing in an Application.DoEvents() every hundred or so insertions in each loop, is there anything I can do to allow the working animation to continue while the controls are populated?
Just run your animation in a second thread. You're allowed to have multiple UI threads, what's not allowed is accessing any UI object from a thread other than the one that initialized it. The new thread should accept an instance of LoadingAnimationForm (or whatever you call your animated dialog) and call Application.Run(animForm); When the main thread gets done populating everything, call animForm.Invoke(animForm.Close). Do not call any other methods on animForm from the main thread.
One possible approach is to use idle time processing for performing your populating code. So you create a dialog box class that is used to show yours waiting animation. You hook into the idle time processing event at the same time you show the waiting dialog box. Once the idle time processing has fully completed you send a message to your dialog telling it to quit.
The only complication is you need to organize your idle time event so it only performs a little work each time it is called, rather than performing all of it in one go. If you perform it all in one go then the dialog never has chance to process and show an updated wait animation.
Taken from this post:
May I add this CodeProject link?
All you need is to build, drag from toolbar and use. The LoadingCircle component works without any trouble at all. Works like a charm, you can even customize it!
I am having a very difficult time trying to debug/fix an application.
Briefly:
- I created a "wizard" type app that starts with the user taking a photograph (using the common dialog for photos)
If the user tries to use the text input window (SIP) (the little keyboard input window) after a photo is taken the event loop seems to hang - the event is not processed or is delayed for a while.
If the user does not take a picture the SIP keyboard works great.
This only happens on some of my devices. Specifically it is not a problem on an MC65 but is a problem on an ES400.
It appears that the app's event loop gets screwed up with the way I am displaying forms and taking photos.
If created a simple test app with single form containing a button (Event handler takes a photo) and a text box that accepts input. That works fine. But it is only a single form app that does nothing else.
When I combine the photo taking with my form displaying (making a "wizard" ) things go badly.
I wonder what kind of event loop should I be running?
Essentially the user takes a photo then goes through some forms (I hide one form and show another when they click the "next" button.)
The Form.Show is called from the main form after a picture is taken and then I have something like:
while(UserNotFinished)
{
Application.DoEvents()
}
Where UserNotFinished is a flag set from my wizard/forms after the "submit" button is pressed.
I will be happy to provide more code but not sure what would be useful.
I am new to C# and CF development (lots of years of C++/Win32)
The real confusing part is that this works on one device but not on another. In fact, the device hangs completely. It ends the activesync connection and sometimes I have to hard reset by removing the battery.
I think your problem stems from the while(true) { DoEvents(); } and perhaps how you are trying to go between forms. The only time I've used the DoEvents() method is when I'm already in the scope of a windows event and I need to be sure something in the message queue is processed so screen updates are correct. I'd suggest making a controller class to manage the screen flow for your wizard. You can control the screen flow by either using ShowDialog() and execute the flow control directly in the scope of a single call, or you'll have to use Show() and an asynchronous mechanism such as subscribing to and handling specific form and control events in the controller class. Also saw the comment about introducing another thread, beware that Forms belong to the thread they were created in and you must Invoke(...) all Form members in the context of the creating thread.
Hmm. Very strange
I started a new thread and basically call Application.DoEvents() in in as well and it seems to fix the problem...
I don't know why the
while(true)
{
DoEvents()
}
in the main thread doesn't work.
I have this massive nested loop scenario that is calling the DB and making HTTP requests to Basecamp API. At first it was a web app but it took much time to run the app so the user (billing department) would often quit out early or complain because it would take so long with no feedback and no way to cancel it. I wanted to make it more responsive and give it a Cancel button as well as a real time log, I also wanted to make it more controllable. I put it in forms so they could have control of every instance of it and have a cancel button and a real time log.
However when I hooked it all up with form buttons, multi-line text box to replace the response and error log, I cannot get anything to work! I added checks in the loop to break out if Cancel becomes pressed. However I can't even click cancel and the multiline TextBox will not live update when I .Text.Insert and then .Update() it. The whole app just sits there and spins... How do I get it to be responsive, accept button clicks during looping, and live update the multi-line TextBox?
NOTE: The thing compiles fine and I can step through it and it writes to a log file just fine so I can tell it's working after the fact that my form freezes up by looking at that log file.
Here is the code I am trying to update the multi-line TextBox with:
TimeSyncLog.Text.Insert(TimeSyncLog.Text.Length, "(((" + clientCode + ")))\n");
And here is the code for my loop breakout:
if(CancelPressed)
{
TimeSyncLog.Text.Insert(TimeSyncLog.Text.Length,"\n\nSYNC STOPPED BY USER.");
break;
}
But I can never click the Cancel button to toggle that boolean because the window says 'Not Responding'...
You shouldn't do any time consuming business logic on the UI thread.
you can use the BackgroundWorker class for those kind of things. it also support cancellation and progress report.
You can read about it here.
The UI thread should have only UI related changes in it. Semi-irrelevant to your question, there's an awesome threading tutorial here.
BackgroundWorker is a great class that uses the thread pool, though there are many options and things to consider when threading. If you want to cancel the thread, maybe nest an event in the GUI class so that the worker can subscribe to it, to handle the event of the 'Cancel' button being pressed. Perhaps that isn't the most efficient way, but I'm sure someone else around here can recommend a few more alternative routes. Hope this helps.