WPF user interface with long processing execution hangs - wpf

I am extremely Sorry for this long post. I need some help on c# wpf issues. I have build a complicated UI(somehow) and there is some buttons... like start and stop and others.
When i click the start button a execution process starts with communicating with some protocol layer and others and it is a long process .. and during this process i have to show some notification UI like "Enter a Text", "Select Something" etc... this time i have to show some wpf window object... and after some time i have to automatically destroy the window and go with processing again.
At first i tried to run the execution in the Main window class. But it results that when the execution starts.. user can't click anything and ui doesno't respond rather just hangs. I investigate the problem... and found that UI is busy with processing in the execution on protocol layer so its not responding.
Here is my problem... can u give me some solution that...
i will have 2 button..start and stop
when i click the start button... a large process will start( like nested for loop with a large int which will continue for 50 seconds) in function named Processor.
at time of processing the function Processor will create several window and show them wait for 5-10 seconds and also destroy them. or user click;s on the window
And the whole time the stop button should be clickable so that when i click the stop button .. the process should be stop.
I tried this with backgroundworker, dispatcher... and using separate thread. but no luck. I guess i am missing something. because if i wait for some result showing a window..the window will definitely hang.. and if i separate them with different thread.. it will not communicate with each other. please give me some suggestions

Dispatcher is definitely the solution. You may need to set the Dispatcher Priority. Sharing some relevant code may also reveal some issues.

BackgroundWorker should do what you need. Set WorkerSupportsCancellation and WorkerReportsProgress to true.
I wouldn't suggest popping up multiple windows. Pop up one window to display status. In your loop in DoWork, call BackgroundWorker.ReportProgress. Then in the ProgressChanged event handler, update the status of the window.
To implement Stop:
In your DoWork method you need to check the CancellationPending property on the BackgroundWorker in your loop. When it is true you need to exit that method. On the stop button click, call BackgroundWorker.CancelAsync().

Related

LabVIEW: how to stop a loop inside event structure

I create an event structure for two buttons, start ROI and stop ROI. When the user presses start ROI it goes to this event and do the following:
check if the camera is open and is in idle
enqueue "none" to the queue to initialize the queue
in the loop dequeue every iteration to find if there's invoked message, which is inserted from the callback
if the element is "invoked" then update the region
The problem I am seeing is that when it is in the loop I cannot press the stop ROI or any other buttons. But the ROI keeps updating. I am puzzled why this is happening.
Could you please help me ?
Thanks,
Edit events for that case (the one pictured in your screenshot) and make sure the box titled "Lock front panel" is unchecked. This should solve your issue.
As far as I can tell from the code you have shown, your event structure should not be attempting to handle the stop ROI Value Change event. It doesn't need to, because the only place you need to respond to that event is inside your innermost loop and there you are handling the button click by polling the value of its terminal anyway.
However as #Dave_St explains, this will only work if the loop runs regularly, i.e. if the Dequeue Element function either receives data regularly or has a short timeout, because otherwise it will wait for data indefinitely and the loop iteration will not complete until the dequeue has executed. Having an event handler for the button click can't help here because it can't interrupt the program flow - the event structure only waits for an event to happen and then allows the code in the corresponding frame to execute.
More generally though, and looking at your front panel which suggests you are going to want to deal with further controls and events, the problem is that you are trying to do a time-consuming task inside an event structure. That's not how event structures are designed to be used. You should use a design pattern for your app that separates the UI (responding to user input) from the process (acquiring images from a camera) - perhaps a queued message handler would be suitable. This may seem harder to understand at first but it will make your program much easier to develop, extend, and maintain.
You can find more information, examples and templates in your LabVIEW installation and its online help. I do recommend using one of the templates as your starting point if possible because they already implement a lot of common functionality and can save you a lot of redundant effort.

How to clear keyboard buffer from stale messages

My WinForms application has a button. This button has accelerator key (e.g. Alt+L). When button is pressed I handle the Click event and disable UI to prevent further button clicks until processing is finished. However, when accelerator key is pressed using keyboard those keystrokes are queued and get processed as soon as UI is enabled again. I don't want this. My question is how to clear/flush keyboard buffer?
If I use KeyPress or KeyDown to eat those characters I don't know when they have been received. I only want to suppress old/stale messages that arrived when I was still processing first Click event.
Yes, indeed your theory of the problem is consistent with that proposed by both myself and madmik3 in the comment exchange above. The amount of work your application is doing on the UI thread is effectively blocking it from processing other events, including keystrokes by the user. Those are getting queued for later execution whenever your application finishes its time-consuming foreground task. Those are the perils of a modern-day, pre-emptive multitasking OS. Of course, without posting your actual code, the best I or anyone else can do is speculate about what the problem is, given our experience.
The quick check to confirm that this is actually the case is to toss Application.DoEvents into your processing loop. That will allow the OS to handle the keystrokes immediately, which will all fail because the button has been disabled. (Click events, whether initiated by the mouse or keyboard shortcuts, are not raised for a Button control that has its Enabled property set to "False".) This is the closest you'll get to anything like "flushing the buffers". I doubt you're receiving KeyDown or KeyPress events anyway until after whatever long-running task has completed.
If that fixes the problem, the long-term solution is to spawn a new thread and perform whatever processing you need to do there, instead of on your UI thread. This will prevent you from blocking your UI thread, and, assuming the Button control is correctly disabled, cause the keystrokes to get thrown away because the button they "click" is in a non-clickable state. The simplest way to create a new thread is using the BackgroundWorker component. The documentation contains a pretty good example.

Using ProgressBar as a wait bar - how to avoid freezes?

I'm creating a custom charting control and I would like to have the possibility of displaying some sort of wait bar while other commands are running (i.e. chart is being created).
I'm using ProgressBar (System.Windows.Forms.ProgressBar in marquee mode) as a part of this control. I do not need to update the state of the progress bar, I just want it to move over and over until the chart is created.
Right now I'm using it in following scheme:
- Start wait bar (ProgressBar appears and starts running)
- Call charting methods
- When the chart is ready, the wait bar will being hidden.
The problem is: Some charting methods are really computational demanding and the wait bar freezes in such moments. How can I avoid these freezes? Should I use some kind of threading/background worker? And if yes, then what is the simplest way to do it?
Note, that I do not need to change the state of the progress bar while the chart is being prepared. I just need the wait bar to start, run during all computations and stop after that.
EDIT
OK, as suggested, I created a separate thread for these demanding computations to avoid freezes.
But how to wait for a thread to finish and do not freeze the GUI?
I tried, as suggested here, something like that:
Thread t = new Thread( () => { DoSomeLongAndDemandingTask(withParameters); });
t.Start();
t.Join()
// do something that needs to be done after thread finishes
InvokeMeAfterThreadFinished();
But it freezes the GUI. Is there any other way to avoid these freezes?
You've answered your own question - typically the answer is to move the computation onto a background thread. There is a WinForms component called the BackgroundWorker, does a lot of the lifting for you:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
http://dotnetperls.com/backgroundworker
Note that you won't be able to access UI components from the background thread, you need to Control.Invoke onto the UI thread to get access to UI controls. This is heavily talked about (and solutions provided) on the net so Googling will be easy for this.
Alternatively, sometimes a background thread is unworkable (not sure why), so you can use Application.DoEvents() - if memory serves, this processes pending messages on the message queue (including control painting, UI updating). If you only do a little work that causes jittering, this could be a faster and simpler option - though not advised too often.
Using the BackgroundWorker class is the simplest way to perform a background computation.
However, just be careful that the charting methods you are running in the background do not update the UI. All updates to the UI itself must be performed by the UI thread. So a background thread will need to "marshall" such calls to the UI - see Control.Invoke for a starting point on that.

How to ensure that a winform closes in exactly X seconds

In my WinForms application, I need to pop up a little custom dialog that stays on the screen for X amount of seconds and then disappears. So I use a System.Threading.Timer to invoke the _dialog.Close() method once the appropriate amount of time has elapsed. This of course means that I have to do the whole "if InvokeRequired BeginInvoke" dance which isn't really a problem.
What is a problem however is that my main thread might be off doing god knows what by the time the BeginInvoke is called. It might not get around to closing the window for quite a while. I don't need the window to close at a millisecond's notice, but within a second or so is really necessary.
So my question is how does BeginInvoke actually work itself into the main thread and how can I get around this odd limitation?
If your UI thread is busy for many seconds at a time, then:
You won't be able to close a window associated with that UI thread, without peppering your code with Application.DoEvents calls (a bad idea)
Your whole UI will be unresponsive during this time. The user won't be able to move any of the application's windows, and if the user drags other windows over the top of it and off again, you'll end up with an ugly mess while the UI waits to repaint itself.
Certainly use a System.Windows.Forms.Timer instead of a System.Threading.Timer for simplicity, but more urgently, look at fixing your code to avoid having such a busy UI thread.
UPDATE: The conclusion would seem to be that utilising ['BackgroundWorker](http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx) along with a System.Windows.Forms.Timer would be the best approach.
Best to use System.Windows.Forms.Timer for this purpose - this is precisely the sort of application it was designed for. Add one to the pop up form and start it as soon as the form is shown, then hide the form on the Tick event. This solution won't give you any threading issues because the timer runs purely on the UI thread.
Edit: If you want to move the logic outside of your popup form, then I recommend you just create an overload for the Show method within the form code that takes a timespan for its parameter and does the job of setting the Timers's interval and starting it.
Edit 2: If you're main (UI) thread is doing too much work and therefore blocking the message pump and not allowing the timer to fire, then it's the design that's the issue I'm afraid. Your UI thread should never be blocking for more than a fraction of a second. If you need to do serious work, do it in the background using a worker thread. In this case, because you are using WinForms, BackgroundWorker is probably the best option.
Create a dedicated thread and use Application.Run to create and show your form. This will start up a message pump on the second thread which is independent of the main thread. This can then close exactly when you want it, even if the main thread is blocked for any reason.
Invoke and BeginInvoke do get into the main thread by using a window message posted into that thread, waiting for it to be processed. Therefore, if the message pump of the main thread is not processing messages (e.g. busy), it will have to wait. You can mitigate this factor by calling Application.DoEvents() when doing time-consuming operations in the main thread, but that's not really a solution to the problem.
Edit: Sample from some splash screen code (the form requires no special code or logic):
private void ShowSplashInSeparateMessageQueue() {
Thread splash = new Thread(ShowSplashForm);
splash.IsBackground = true;
splash.Start();
}
private void ShowSplashForm() { // runs in a separate thread and message queue
using (SplashForm splashForm = new SplashForm()) {
splashForm.Load += AddDestroyTimer;
Application.Run(splashForm);
}
}
private void AddDestroyTimer(object sender, EventArgs e) {
Form form = (Form)sender;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(form.Container);
timer.Tick += delegate { form.Close(); };
timer.Interval = 5000;
timer.Start();
}
Invoke just places the delegate into the message queue of the thread you want to invoke it on. You could use the Dispatcher class to insert the delegate with a high priority, but there is no gurante that this will meet you timing constraints if the thread is doing a lot of work.
But this might be an indication that you are doing to much work on the user interface thread. Not responding for a second is a pain to a user. So you might think about moving some work out of the user interface thread.

Progress bar not showing until after task is completed

I have been trying to get a progressbar set to marquee to keep moving while another function is running. After this function runs, I message would display (for this example)
The only way I was able to get this working was with a background worker and then have a
Do
Loop until condition that runs in the main form until the operation is complete followed by my message box.
This seems like a kludge way to accomplish this and a thread.start followed by a thread.join seems like a much nicer way to fix this. However, I was not able to get that working either.
I have included a small demo program if anyone is interested.
http://www.filedropper.com/progressbar
Thanks
Thread.Start and Thread.Join is not the way to do it - that basically blocks your UI thread again. Application.DoEvents isn't the way to go either - you really do want a separate thread.
You could then use Control.Invoke/Control.BeginInvoke to marshal back to the UI thread, but BackgroundWorker makes all this a lot easier. A search for "BackgroundWorker tutorial" yields lots of hits.
EDIT: To show the message when the worker has finished, use the RunWorkerCompleted event. The ReportProgress method and ProgressChanged event are used to handle updating the progress bar. (The UI subscribes to ProgressChanged, and the task calls ReportProgress periodically.)
That is not a kludge. That is the correct way of doing it; what happens with the BackgroundWorker approach? The trick is to use the ReportProgress method to push the change back to the UI (don't update the ProgressBar from the worker).
Use Application.DoEvents() in your function from time to time so that your process has some time to process his events, including redrawing the form.
Alternatively, you can use a worker thread (like a BackgroundWorker) to process your treatement, while the UI thread is displaying your progress bar.
Complementing the answer given by Marc Gravell, the BackbroundWorker has a boolean property WorkerReportsProgress, if it is set to false, when you call ReportProgress, the program will raise an InvalidOperationException

Resources