brief history. I have a little reporting tool that I am creating that uses a backgroundWorker to do the reporting. Works great, the GUI updates nicely and reports what I want it to.
However if I get a lot of results back and then try to update my ListView control the GUI hangs while it is adding all of the ListViewItems. I would like to provide the user with some feedback while this is going on to let them know that the app is still working but can't seem to figure it out.
I have tried to update the ListView from within the BackgroundWorker and of course got the Cross Thread error.
I tried to create a new ListView from within the BackgroundWorker.DoWork and that worked up until I tried to add the ListView to the GUI in the BackgroundWorker.RunWorkerCompleted, and received the Cross Thread error.
I tried to update the ListView from the BackgroundWorker.RunWorkerCompleted and this is where the GUI hangs (actually turns white), I even created a progress window, and it does the same thing. Once the adding of the ListViewItems is complete the application returns to normal and works fine.
Does anyone have any thoughts?
Thanks
Patrick
You should add items to the ListView on the UI thread in RunWorkerCompleted.
You should call the ListView's BeginUpdate method before adding the items, then call EndUpdate afterwards. This should make it substantially faster.
To make it even faster, you should make an array of ListViewItems, then call AddRange with the entire array.
If you do this, you don't need to call BeginUpdate or EndUpdate, since AddRange will call them for you.
Instead of adding all items at once in RunWorkerCompleted add them in batches of 10 or 20 in the ProgressChanged event handler.
Related
What is the proper, most efficient way to perform a query in a second thread and update the GUI in a delphi xe5 iOS application ?
Currently, I've been starting the spinner (TaniIndicator) animation from the main thread, then creating the second thread, which in turns perform the database query AND updates the GUI. Before applying the work in the second thread, the app worked fine (minus the usage of a smooth spinner), but Ive noticed now that I have the workload placed in a second thread, the app sometimes crashes. I've also noticed the more extensive the GUI update, the more often it crashes as well....
What is the best way (safest and most efficient) to perform a database query and update the GUI using threads?
example; Should I be setting the parent of created controls in the second thread to, 'nil' ?
I created a small form that I want to put on top of other forms when they are working. Basically like a small "Loading... Please wait" notification. It includes an animated TGIFImage. The problem is that when another form is working hard the animation and updating of this form stutters. Probably since they are running in the same thread? Is there a quick and easy way to have this little form always update nicely? Perhaps a quick way to make sure it's handled in it´s own thread?
Move the hard work into an actual thread of its own. See the TThread class. Display your Form normally, then start the thread, then close the Form when the thread is done with its work. Have the thread post status updates to the main thread asynchronously if needed (such as for progress bars, etc) so the worker thread is not slowed down waiting for the main thread.
Thanx for the answer Remy Lebeau. I wanted to have to change as little as possible to the existing code though and not move existing code into threads etc. So what I ended up doing was to have a little factory for my notifications that spawns a thread and create the form in that thread. That way I basically only need to wrap my existing code with a Show and a Hide call.
I have a D2006 app that contains a page control and various grids, etc on the tabs. When I resize the main form (which ripples through and resizes just about everything on the form that is aligned to something), I experience long delays, like several seconds. The app freezes, the idle handler is not called and running threads appear to suspend also.
I have tries pausing execution in the IDE while this is happening in an attempt to break execution while it is in the troublesome code, but the IDE is not taking messages.
Obviously I'm not expecting anyone to point me at some errant piece of code, but I'm after debugging approaches that might help me. I have extensive execution timing code throughout the app, and the long delays don't show up in any of the data. For example, the execution time of the main form OnResize handler is minimal.
If you want to find out what's actually taking up your time, try a profiler. Sampling Profiler could answer your question pretty easily, especially if you're able to find the beginning and the end of the section of code that's causing trouble and insert OutputDebugString statements around it to narrow down the profiling.
OK. Problem solved. I noticed that the problem only occurred when I had command-line switches enabled to log some debug info. The debug info included some HTTP responses that were written to a debug log (a TMemo) on one of the tabs. When the HTTP response included a large block with no CR/LFs the TMemo wrapped it. Whenever I resized the main form, the TMemo resized and the control had to render the text again with the new word wrapping.
To demonstrate:
start a new Delphi project
drop a TMemo onto the form
align it to Client
compile and run
paste a large amount of text into the TMemo
resize the main form
I won't award myself the answer, as I hadn't really provided enough info for anybody else to solve it.
BTW #Mason - would SamplingProfiler have picked this one up - given that the execution is inside the VCL, and not in my code?
A brute-force approach that may give results.... Put a debug message to OutputDebugString() from every re-size event, sending the name of the control as the string to be displayed. This may show you which ones are being called "a lot".
You may have a situation where controls are bumping each other, setting off cascading re-size events. Like 3 siblings in the back seat of a compact car, once they start jostling for position, it can take a while for them to "settle down".
Don't make me turn this car arround....
The debug log (viewable in the IDE, or with an external ODS viewer), may show you which ones are causing the most trouble, if they appear multiple times for one "user-initiated re-size event".
Run your application in AQTime's performance profiler (included with XE, but you can get a time-limited version from their website).
Do some fanatic resizing for a while, and then stop the application.
After that, you'll see exactly which function was called many times, and where most time was spent.
I want to detect when a file date changes and update a DevX TdxMemData which is used as a Tdatasource which then would be seen in a TDBGrid that uses it.
I've found some code that uses ReadDirectoryChangesW, but seems rather complex for my simple needs.
I'm considering using a TTimer and firing it off every five seconds. (That's fine enough accuracy for me.)
Are there any caveats in doing this? I've read that Threads have all sorts of restrictions on VCL access, etc. Does the same thing apply to TTimer events?
Is there anything I need to watch out for when calling FileAge and updating a DevEx TdxMemData object while in a Timer event? Will those updates be seen by my main app?
Is there a way to detect the "state" of my program when a Timer event gets control so I can avoid problems?
Or am I opening an enormous can of worms in thinking about using a TTimer for this?
TTimer events are called within the main application thread, so there's no problems with accessing VCL objects from them. It's called when your application is idle, so it won't take place while your in an OnClick handler or anything similar unless you manually call Application.ProcessMessages.
I'd suggest using ReadDirectoryChangesW though. If you use a timer you will continue polling even if the application is idle and the file isn't changing. It will keep your CPU from going idle and keep could keep the hard drive from spinning down, which can have negative effects for power saving and battery usage.
In Demos directory there's "ShellChangeNotifier" component, which will fire events when files get modified, added or deleted inside directory. However it has only one OnChange event, so you don't know what really happened.
There's some discussion and solution about the issue in about.com
Windows lets you monitor file changes. As a jump start see http://delphi.about.com/od/kbwinshell/l/aa030403a.htm. There are several ready made components available, too. Google for "delphi monitor file change" or something similar
You can check my: DirectoryWatch
It is a wrapper around "ReadDirectoryChangesW" functions. It is more specific about changes than "ShellChangeNotifier".
When I try to set the width of a multiline EditBox widget, it flickers for a moment, then gets set.
Is there a way to get rid of the flickering? Or, alternatively, is there a workaround?
It might be a problem with the way the UI rendering is optimized. Try changing your UIFaster setting as described here: http://www.wowwiki.com/CVar_UIFaster
I've usually seen this as a result of multiple calls to :SetWidth() occurring in quick succession. There are two ways this can happen — (a) it's genuinely getting called multiple times, or (b) it's been hooked/replaced with another function which is internally causing multiple calls. As a quick test, try running the following command (or equivalent) via the WoW chat window while your edit box is visible:
/script MyEditBox:SetWidth(100)
If the size changes without flicker, you've got scenario A — go over your addon's logic paths and make sure :SetWidth() is only being called when appropriate (and only once). If it does flicker, you're probably looking at screnario B (or, of course, the UI issue Cogwheel mentions). This may be harder to debug, unless you're hooking/replacing SetWidth yourself, but a good first step would be to disable all other addons and see if the problem resolves itself. If not, my first guess would be a library issue (assuming you're using any).