WPF BindingOperations.EnableCollectionSynchronization + ObservableCollection + Reentrancy - wpf

Firstly I am terrible at titles and articulating my problem at hand.
I am producing a file-system like structure. Files and Folders are stored in a local sqlite db, you have File entities and Folder entities.
When the screen first opens, goes to the local db and fetches the data and pushes it into the ObservableCollection. This is done on a new background thread.
Here's the problem, if you have 50,000 items, it seems like WPF's new BindingOperation mechanism for cross thread collection access, does some special tricks to queue all the changes to the UI on another thread which then trickles it onto the Dispatcher.
Now obviously I have no control over how it does this. But my question is, how do I handle reentrancy in these scenarios? If the user was to spam-click a reload button that I have, how do I either cancel all these queued UI updates that are slow and pending, or how do I know there's still so many pending UI updates?
It seems rather slow on the updates, with 50,000 items it takes at least a minute or 2 to completely process all items into the list.

Related

Update UI control frequently with multiple threads

I have a DevExpress GridControl oneway binding to a tableview in the viewmodel. There are about 20 background threads querying data from databases and update the tableview individually. The update to the table view is guardard with lock for async update. Dispatcher is used for refreshing the main UI thread. I also have another button to cancel the database and update functions via CancellationTokenSource.
However, when the applicatoin runs, I have to click the cancellation button many times in order to execute code in the cancel command. In another word, the UI Main thread is busy refreshing the GridControl and it blocks the Cancel Button.
Is there a way to achive this function?
Edit: Found this method helps a lot await Dispatcher.Yield(DispatcherPriority.ApplicationIdle);
It simply gives other UI controls a chance to be executed.
Creates an awaitable object that asynchronously yields control back to the current dispatcher and provides an opportunity for the dispatcher to process other events. (MSDN)
AFAIK, UI stands for User Interface, where main part is User! User is not robot, it is a person, which uses your application. You do not need to query data updates so often on UI thread. Why do you need 20 threads for database query operation? You have the single GridControl, which can show only several rows at time on the screen, 20, 50, 100 (no more). So, I suggest you to have only one thread for reading data from database, and do it once per 2-5 seconds, in order to provide User with some interactivity. For this purpose, TPL - is the good choice. Using it with CancellationToken - is good way to support cancellation with your scenario. Between database queries you can do the following:
while (true)
{
myToken.ThrowIfCancellationRequested();
// database query here
myToken.WaitHandle.WaitOne(2000)
}
Also, before updating source collection, you can use the following:
GridControl.BeginDataUpdate();
//Update your source collection
GridControl.EndDataUpdate();
This will prevent DevExpress control from listening CollectionChanged event, or any other, because on each Add/Remove action DevExpress call UpdateLayout() method, which is no so fast as we want to.
if layout is invalid in either respect, the UpdateLayout call will redo the entire layout. Therefore, you should avoid calling UpdateLayout after each incremental and minor change in the element tree.

Best way to display realtime queue data from database in grid

I have a client-server VB.NET/SQL2008 application which, among other things, keeps track of people who are waiting in a queue. The records for the queue are all updated via the application but they are added/removed on different workstations.
I need to display the queue of people in a grid (could do a multi column listbox if it would add any benefit) and have it automagically update when the queue changes. I don't need it to be real-real-time but I would like the people to show up within a few seconds. I currently have a datagrid bound to the data and I have a timer which is refreshing the datagrid every few seconds. However I'm thinking there must be a better way to do it.
The one thing I don't like is how the grid flickers when it refreshes and if someone double-clicks to select a record exactly as it's refreshing it selects the first person in the list and not the one selected.
I've done some research and it seems like one option is SQL Dependency - however from what I've seen people say it's difficult to set up and get working and "fragile".
Any help is appreciated!

Is it possible to refresh a CollectionViewSource too often? Having some strange issues occur

I've got these ListViews bound to a couple of CollectionViewSources. Those are in turn bound to ObservableCollection lists which have plenty of data that updates quite often (it monitors a 32 player game server and displays player metadata and events). I wouldn't ever call Refresh() on the view if it weren't for the apparent necessity to make sure it is continually sorted by Score and to ensure added/removed players are updated on the UI.
Now the issue I've run into is that too many refreshes to the Views will cause them to lose the ability to open a context menu. You can right-click all you want but it will not appear until you destroy the form and re-instantiate it (an unacceptable workaround).
Has anybody ever seen this behavior, and know how to get around it? Initially I was Refreshing as often as it felt it needed to -- this caused the issue almost immediately. I reduced these Refreshes to every 5 seconds and now it happens but not quite as often.
We have had several problems with the CollectionViewSource. It causes lots of performance issues after openening the same window for appr. 5 times. It might be because we were using an LLBLGen collection as items source, but I will never use the CollectionViewSource again.
Now, I wouldn't refresh the view manually. Your ObservableCollection is responsible for updating the data. Try removing the CollectionViewSource for now (so you will lose the sorting ability), and see if the problem still occurs. If it disappears, you will need another way to sort your collection.

Creating WPF components in background thread

Im working on a reporting system, a series of DocumentPage are to be created through a DocumentPaginator. These documents include a number of WPF components that are to be instantiated so the paginator includes the correct things when later sent to the XpsDocumentWriter (which in turn is sent to the actual printer).
My problem now is that the DocumentPage instances take quite a while to create (enough for Windows to mark the application as frozen) so I tried to create them in a background thread, which is problematic since WPF expects the attributes on them to be set from the GUI thread. I would also like to have a progress bar showing up, indicating how many pages have been created so far. Thus, it looks like Im trying to get two things to happen in parallell on the GUI.
The problem is hard to explain and Im really not sure how to tackle it. In short:
Create a series of DocumentPage's.
These include WPF components
These are to be created on a background thread, or use some other trick so the application isnt frozen.
After each page is created, a WPF ProgressBar should be updated.
If there is no decent way to do this, alternate solutions and approaches are more than welcome.
You should be able to run the paginator in a background thread as long as the thread is STA.
After you've set up your thread, try this prior to running it.
thread.SetApartmentState(ApartmentState.STA);
If you really must be on the GUI thread, then check out the Freezable class, as you might have to move the objects from your background thread to the GUI thread.
If the portions that require the UI thread are relatively small, you can use the Dispatcher to perform those operations without blocking the UI. There's overhead associated with this, but it may allow the bulk of the calculations to occur in the background and will interleave the work on the UI thread with other UI tasks. You can update the progress bar with the Dispatcher as well.
My guess is that everything that is time-consuming to create is within your Visual. If so, there is an easy solution: Don't create actual DocumentPage objects and their associated Visuals until DocumentPaginator.GetPage() is called.
As long as the code that consumes your document only requests one or two pages at a time there will be no performance bottleneck.
If you're printing to the printer or to a file, everything can be done on a background thread, but if you're displaying onscreen you only need to display a few DocumentPages at a time anyway. In either case you won't get any UI lockups.
The worst case scenario would be an app that displays pages in a thumbnail view. In this case, I would:
The thumbnail view would bind its ItemsSource to a "RealizedPages" collection which initially is filled with dummy pages
Whenever a dummy page is measured, it queues a dispatcher operation at DispatcherPriority.Background to call DocumentPaginator.GetPage() and then replace the dummy page in the RealizedPages collection with the real page.
If there are performance concerns even with realizing a single page because of the number of separate items, this same general approach can be used within whatever ItemsControl on the page has the large number of items.
One more note: The XPS printing system doesn't ever process more than one DocumentPage at a time, so if you know that's your client you can actually just keep returning the same DocumentPage over and over again with appropriate modifications.
Elaborating further on Ray Burns' answer: Couldn't you have your dataprocessing done in a class on a background thread and then databind the DocumentPage's properties to this class when the processing is done?
A little late to the game on this one, but I just worked out a solution to this so I thought I would share. In order to display the UI elements they have to be created on the UI thread on which they will be displayed. Since the long running task is on the UI thread, it will prevent a progress bar from updating. To get around this, I created the progress bar on a new UI thread and created the pages on the main UI thread.
Thread t = new Thread(() =>
{
ProgressDialog pd = new ProgressDialog(context);
pd.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
pd.Show();
System.Windows.Threading.Dispatcher.Run();
});
t.SetApartmentState(ApartmentState.STA);
t.IsBackground = true;
t.Start();
Action(); //we need to execute the action on the main thread so that UI elements created by the action can later be displayed in the main UI
'ProgressDialog' was my own WPF window for displaying progress information.
'context' holds the progress data for my progress dialog. It includes a cancelled property so that I can abort the action running on the main thread. It also includes a complete property so the progress dialog can close when the Action has finished.
'Action' is the method used to create all the UI elements. It monitors the context for the cancel flag and stops generating the UI elements if the flag is set. It sets the complete flag when it is done.
I don't remember the exact reason I had to set Thread 't' to an STA thread and IsBackground to true, but I am pretty sure it won't work without them.

Separate UI thread for details view using WinForms

Our app's master/details view uses a datagridview as the master, and a custom control as the details view. The details view takes a long time to compute and render, making cursoring up/down the master view painfully slow.
Therefore, we'd like the details view to run asynchronously (in a separate UI thread) with change notifications from the master.
Creating a form in a separate thread is relatively straightforward, as Application.Run takes a form parameter.
Is there a way to run a winforms control on a separate thread? I'm aware that native windows in different threads can have a parent/child relationship, just not sure how to set that up using winforms.
TIA,
Updating the UI from a Secondary Thread
http://msdn.microsoft.com/en-us/magazine/cc188732.aspx
Intuitively, you also ought to be able to accomplish the same thing by using a BackgroundWorker. The BackgroundWorker is designed to update UI things like progress bars while executing stuff in the background, and it can be cancelled during its operation.
Is the slowdown caused by the loading of the data, or the population of the UI itself?
Most of the time it's the former, so if that's the case, then the logic that does the data loading should be abstracted into a different thread. The UI code can live in the main thread since updates are quick. You could use either a Thread or a BackgroundWorker in this situation. The key is to separate your data loading from your GUI population.
If you are firing off the update of the detail view in code, you can greatly improve the usability by sleeping 500ms between the time that the user selects the master record, and the time you update the detail view.
This gives the user 1/2 second to scroll to the next record without the details view updating at all.
If you're taking a speed hit during rendering, you should consider suspending layout until the form has completed updating, and then refresh the visible display once at the end.
this.SuspendLayout();
// Do control stuff here
this.ResumeLayout();
If that doesn't help, try this:
[DllImport("user32.dll")]
public static extern bool LockWindowUpdate(IntPtr hWndLock);
//
LockWindowUpdate(this.Handle);
// Do control stuff here
this.Refresh(); //Forces a synchronous redraw of all controls
LockWindowUpdate(IntPtr.Zero);
http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/8a5e5188-2985-4baf-9a0e-b72064ce5aeb

Resources