Showing a please wait animation while UI is populated - c#

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!

Related

WinRT XAML UI stalls even though using background thread

I have an app where activities that aren't likely to finish quickly are run on a background thread. For example, I have some items to display where I show a generic icon to begin with while the background code tries to see if there is an associated image that should be displayed instead.
When XAML tries to fetch the image, if I don't have it already, I add the query to a queue for later processing and temporarily return null. The queue handling basically does this:
Get a mutex (so that I can manipulate single items safely)
If the item isn't already in the queue, add it
If the queue isn't already running, start it
Release the mutex
The queue is run with this:
objectFileQueueRunning = true;
cts = new CancellationTokenSource();
Task.Run(() => ProcessObjectFileQueue(cts.Token));
The queue code basically keeps on taking the top item off the list and processes it until we run out of items or the cancellation token gets set. The same mutex is used within the queue code but only when removing the top item off the list and when resetting objectFileQueueRunning to false.
My problem is that the UI thread is basically unresponsive until the queue code finishes running ... even though it is running on a background thread.
I've run performance analysis on the executing app but cannot spot anything that would seem to give me any hints as to what to look at.
Do I need to put a pause into the background thread to give the UI thread a chance to interact with the user? Is it the case that I'm running the background thread so tightly that the UI thread isn't getting a look-in?
The solution, in my case anyway, turned out to be the use of StackPanel as the ItemsPanelTemplate coupled with the background thread doing a lot of work.
The problem with using StackPanel is that it causes virtualisation to be disabled, which means that all of the elements get created in one go.
Switching to ItemsWrapGrid allows GridView to use virtualisation and therefore only create elements that are either visible or about to become visible. This, in turn, causes the background workload to be reduced.
The reason I was using StackPanel dates back to when my app was first written for Windows 8.0. Using the default settings caused problems when the user scrolled horizontally because the scroll bar size would vary. This seems to have been fixed in Windows 8.1 but I hadn't thought to revisit the XAML as I updated the app.
Shared in case anyone else hits similar issues.

Why is this WinMobile (Compact Framework) app hanging after photo taken?

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.

How can I flush buffer of form object properties BEFORE a Process.Start is executed?

On a button click, I make several changes to form elements (hiding some, showing some, bringing some to front, etc.). After those form element changes are made, I run an external process with a Process.Start(). However, even those those form element layout changes are sequentially coded before the Process.Start() call, they're not being executed/displayed BEFORE my Process.Start().
How do you force a flush of these layout changes that seem to be buffered?
You could try the Control.Invalidate(true) function on the control you want to be redrawn.
Here is a good post about the difference between Refresh, Update, and Invalidate
Based on the Post, I think you would want to use Refresh over Update to invalidate, then immediately update the control
Try either running the .Refresh method before the process.Start, or run Process.Start in a separate thread, such as:
System.Threading.ThreadPool.QueueNewWorkerItem(new System.Threading.WaitCallback(StartProcess));
void StartProcess(object state)
{
Process.Start(...);
}
By putting the start in a background thread, you allow .NET to update the UI before items in the background thread run. If the Process.Start is in the same thread as the UI, then the UI cannot refresh until all processes in that thread have finished running.
Found answer..
mainFormName.ActiveForm.Update();
Bang bang.

How do I keep my C# Windows Form Responsive while it churns loops?

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.

Automatically show loader on long operation

I'm trying to show a loader animation when my application it's blocked for more than 500ms.
I want that to be automatic, I don't want to add any piece of code before every long operation.
I know that in WinForms it was possible (I used this: https://snipplr.com/view/24851/), but It does not work in WPF.
I've found that I can do that with the mouse cursor(display Hourglass when application is busy)
I've tried to launch a window with a spinner.
I've found this answer (https://stackoverflow.com/a/21411656/10820863), that works detecting long operation.
Problem is that if I launch a window from a thread that is not the main one, I got a ThreadException because it's not the main thread. If I use Application.Current.Dispatcher.BeginInvoke
method, the window appears only when UI is not blocked anymore.
So, how can I automatically detect long operation and show a loading window/page/image/whatever if it lasts for more than 500ms?
[EDIT]
I don't want to add code to every long function, evaluating case for case which function can be long.
I'd prefer to have an automatic method that do that for me.

Categories