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.
Related
I'm developing a Revit add-on which performs some lengthy tasks. During the process, I want to display a simple WPF window with an indeterminate progress bar, a label to inform about current process and a button to enable aborting.
I already tried the most obvious ways of accomplishing that: creating a WPF window inside the add-on and displaying it, but the problem is that the UI gets frozen, no matter how I implement this. During some processes, the whole Revit UI gets frozen/white so I really wouldn't expect my embedded WPF window would behave normally in these conditions anyway.
The workaround I figured out was to have the WPF window as a separate app (EXE file) I could run from the add-on. I based my implementation on this example .
The good part of it is that it doesn't hang no matter what is happening with Revit.
The bad part is that the sequence of how Windows is queuing the calls of my separate WPF app is sometimes different from the sequence of these calls from my add-on. It sometimes results in a situation when the Revit process is over but the WPF window is still displayed (waiting for the final, closing call which had been apparently already executed, but then the app got reactivated with another, delayed call).
Preferably I would like to handle the WPF app the same way as you can i.e. handle an Excel application from .NET. You create an ExcelApp object, do what you want with it and dispose of in the end.
The problem is I don't have a clue of how to do this.
How should I expose the WPF app's API to my add-on?
Could it be possible to have the WPF app responsive and controlled from the Revit add-on at the same time? (user can still click the abort button, the indeterminate progress bar doesn't freeze)
The First thing to know is about interacting between two processes. there are some Standard approaches:
Interacting through Socket (Socket Programming)
Using Named PipeLines (Useful when your messages aren't so long)
There are some other predefined Libraries based on above techniques. Using a FileSystem Based method is not a reliable way to proof the outputs.
This was a part of your solution. The next step is to use Threading in your WPF application. I'm not familiar to Revit and I don't know how it works.
UI freezing is normal in a long running process. because UI is busy and it can't answer your requests (e.g Mouse Move, Click, ...). So using a Thread you can put your long running process into a separate place and wait for the response at the end of it.
There is a problem while using a Thread. Because you left your UI and started your long running process on a separate Thread, you can't directly access to your ProgressBar. In this situations you have to use ThreadDispacher. It's not a terrifying concept, it just a three line of codes that will adds to your callings.
for example:
Dispatcher.Invoke(() =>
{
ProgressBar.Value++;
});
Search for a Library to doing your IPC (Inter Process Communication) to get the result faster (or you can learn about above techniques to do it by your means) and next add a simple thread to your WPF application so you be able to Start, Pause and resume the running job based on the situation.
When a browser is opened, before it's completely loaded, we can use the controls as others are being loaded (the address bar appears and, while the bookmarks are loaded, we can already type in it).
I'm making a personal browser, and I don't know how to perform that. I imagined creating the controls in another thread, but soon I discovered that that's not possible.
In my last question (where I discovered the above), I received an answer talking about Attribute, Reflection, async/await modifiers and observable collection as the closest solution to that and I'll study them yet. In this new question, I would like to receive others suggestions of how that could be made (allow the user to use the window and controls while others are being created/loaded).
Thanks in advance.
Actually I believe the process of loading the UI part of controls isn't the heavy one.
In the other hand, loading the data which is later bound to the control is the problem.
You can't draw controls outside UI thread, but you can load heavy data, preload resources or do calculation in a background thread.
While heavy controls' data is prepared to hit the UI in some background thread, UI will still be responsive.
For example, I guess Web browsers do HTML to DOM parsing in a background thread and they stream results in real time to the UI thread. That is, address bar and other UI components are responsive because UI thread isn't stressed.
We have a button click event that starts a long running task that updates the status bar labels and progress control to give user feedback. Before we moved the task to a seperate thread we noticed the status labels {label.text = "some message"; }(in general) would update immediately while the progress bar and some custom controls often would not update until the function finished and the main UI thread started sending messages again ( which we realized makes sense considering the main UI thread).
But this lead us to wonder - Do some windows controls repaint directly instead of being issued a WM_Paint message?
Your findings are pretty contradictory from what I know about .NET controls. The common rule is that changing the Text property or altering a property like ForeColor or BackColor merely causes the Invalidate() method to be called. Which ultimately causes a WM_PAINT message to be delivered when the UI thread starts pumping messages again. You can call the control's or the Form's Update() method to force any pending paints to be performed before entering the slow code. This is all entirely standard Windows behavior.
A special case is the ProgressBar control. The native Windows implementation for it updates the bar length directly. That's compat behavior, this control often is used in code that doesn't pump properly.
TextBox has special painting behavior, it partly paints directly to the window instead of going through the WM_PAINT message handler. That's legacy behavior way back from Windows 2 when it needed to work reasonably on a 386SUX machine. But that's not relevant in this case.
Of course, rather than fretting about this you should just never get yourself in a place where any of this matters.
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'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.