I have a wpf application that populates an Infragistics XamDataGrid by the usual method of binding an observable collection to the grid. As each data item is populated into the collection, one at a time, the grid updates. I also have a cancel button that I would like to immediately stop the population if the user clicks it. However, it takes a few seconds or more to respond to the cancel.
The problem (I think) is that the message loop is full of the grid population events and my cancel is way in the back and must wait its turn. I was wondering if there is a way to insert a message in the front of the queue and thus make the cancel more responsive (hacky or not - if hacky, please explain what ill effects I can expect).
I am not experiencing bad performance; in fact the UI is quite responsive. The problem is purely that the cancel event has to wait its turn in the message queue and I would rather it have priority over the population messages.
edit: clarifications
When you say Grid, what kind of Grid do you mean? I think your issue might be that for a large collection, this setup might not use any type of item virtualization. You might be better off using a ListBox or ListView in this case, which can use a VirtualizingStackPanel to generate UIElements for only the on-screen items.
If this is the case, your ui thread is getting bogged down because its generating elements for every new item, whether or not it's being displayed on screen. If you're using a 3rd party Grid, you might check to see if it has any virtualization functionality built in that you can turn on.
The answer ended up being the DispatcherPriority:
private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Dispatcher.Invoke(new Action(() => btnCancel.Command.Execute(null)), DispatcherPriority.Send);
}
"Send" priority is the highest of an enumerated type, with "SystemIdle" being the lowest. When I invoke the button's command with that priority, the Cancel goes through immediately.
Related
I have a list of Customers and I update my DataGrid's ItemsSource to contain the customers list.
The problem is that after changing the ItemsSource, the thread update all binding targets to the values in each customer object, and this action freezes the UI for more than 30 seconds.
To solve this I tried to change the ItemsSource property in another Thread but the new thread can not access the datagrid control which owned by the UI thread.
If I put the ItemsSource changing code in Invoke methode the UI will freeze again.
//The following code is handled in another thread
// Geting a list of Customers
List<Customer> customers = manager.AllCustomers.SearchCustomers(fName, lName, address, city, tz, phone);
//Changing the DataGrid ItemsSource
Dispatcher.Invoke(() => customersSearchResultsDG.ItemsSource = customers);
//UI Thread is freezing until ItemsSource Changing complete
p.s
The amount of time it thinks not really bother me because I created a loading animation control.. but the freezing makes the animation stuck and that is what i'm trying to fix.
When the UI thread in a WPF Application is busy, the UI will appear to freeze. There is nothing that can be done about that, but to avoid this situation, we typically do as much long running work as possible in background threads. However, as you have discovered, you cannot manipulate UI elements from a background thread, so there is only so much that we can do.
Other things that we can do to minimise the amount of time that the UI thread is busy is to use Virtualization, container recycling, freezing resources and/or optimise used Templates. You can find out more about this from the Optimizing Performance: Controls page on MSDN.
However, with the small number of items in your collection, it would seem to me that either you are loading and/or preparing your data extremely inefficiently, or you have incredibly large and complex DataTemplates to define your items. The links provided above should help you if your problem is the latter.
for my purposes I am writing a GUI in 4.0 that displays my custom Instance objects in a ListBox with custom colors. I've been able to draw them in their respective colors, but am wondering if there is a way to change these colors.
I've attempted and went down the route of clearing the ListBox and readding the Instance objects in a separate BackGroundWorker every few seconds, but this route has lead to many headaches with the fact that the ListBox.selectedIndex will change and shoot off an event to my ListBox_SelectedIndexChanged event handler. So I would like to avoid that if possible as it is a nightmare to try to code through with threading, locking, raceconditions, turning event handlers on and off, Invalidargumentexceptions.
I was wondering, is there any way for a backGroundWorker to visually update the colors in my ListBox without the need to add and re-add the items to trigger the DrawItem event as described here?
Edit:
Calling ListBox.Refresh() redrew all the items in the listbox, and doing so, avoided the selectedIndexChanged event handler and updated the Colors of all the items
Change the color and call ListBox.Update(), or if from a different thread, ListBox.Invoke(new MethodInvoker( ()=>ListBox.Update() ))
If you have the background worker report progress, then you can in the progress changed method, access the items in your list box and redraw the one you want, depending on what percentage of progress you reported. Should be fairly simple, unless you have a lot of items and a lot of colors.
I have a WPF & C# app that contains a large DataGrid, about 35 cols x 50 rows, to monitor a bunch of frequently changing values. The trouble is, when the whole grid is visible, refreshing it hangs up the user interface for almost a second. I refresh it explicitly every two seconds, which is fine for what I'm doing, but having the rest of the UI hang up is a real pain.
OK, so I decide I'll run the rest of the UI in a separate window in a separate thread with a separate Dispatcher. I wrote a toy window containing just a textBlock with an incrementing count that updates 10 times a second using a DispatcherTimer. However, rather than the count incrementing smoothly, it pauses while the grid refreshes, then resumes displaying with the count about 10 higher than it was when it paused, so timer events are getting handled. I'm just not seeing a refresh.
Does WPF only draw all its elements in a single thread? Any way around it?
Here's my 2nd window creation code:
private void Window_Loaded( object sender, RoutedEventArgs e )
{
ThreadStart ts = new ThreadStart( RunSpareThread );
m_spare_thread = new Thread( ts );
m_spare_thread.IsBackground = true;
m_spare_thread.Priority = ThreadPriority.Highest;
m_spare_thread.SetApartmentState( ApartmentState.STA );
m_spare_thread.Start();
Dispatcher.Thread.Priority = ThreadPriority.Lowest;
}
void RunSpareThread()
{
m_spare_window = new SpareWindow();
m_spare_window.Show();
Dispatcher.Run();
}
FYI I've tried implementing the grid a few different ways - as a ListView, as a Canvas that overrides OnRender and draws a whole bunch of GlyphRunDrawings - WPF is just incredibly slow at drawing this stuff.
Unfortunately yes. That said there are a number of things you can do to increase the responsiveness of your ui. One of the main things is to make sure that the least amount of work is being done in the UI thread. This means doing all of the DB reading etc. within a separate context. You should also look into how your grid is displaying your values - is it virtualizing? Then there is also how you are databinding, a bindingsource should allow you to only update the binding after all of the changes are finished.
WPF is very fast if you use it properly.
Some tips:
Implement model (or ViewModel in terms of MVVM) of the data you want to display. Be sure that it implements INotifyPropertyChanged interface. Bind collection of such models to your DataGrid;
Do not refresh all data each N seconds. You should somehow detect data changes (using working thread) and update appropriate properties on appropriate models. Because of data binding, all changes will be automatically reflected on the DataGrid. So, you don't even need to use Dispatcher (at least explicitly);
Use ListView instead of DataGrid if you don't need to do special actions with data (like editing, sorting, filtering, etc.);
Consider implementation of a virtualization if you need to display a huge amount of rows.
I want to be able to add items to a Data grid at a fast rate without causing UI delay.
Here is what I am doing now:
I am using an ObservableCollection that is bound to the data grid.
I use a background thread that loops and calls Invoke on the current dispatcher only when inserting/removing from the observable collection. Calling BeginInvoke instead has undesirable results.
I know that invoking that much on the dispatcher is causing the delay but I don't know what else to do. I have used background workers before but I don't think that applies to my scenario.
What can I do to keep the UI responsive?
Batch the updates -- the background thread could add items to a queue and you can periodically refresh your bound observable collection by invocation. Take a look at the System.Collections.Concurrent namespace if you need to handle multi-threaded producers
A major weakness in your design is that by binding to an ObservableCollection, you are causing the UI to render every item that get's added to the list (possibly thousands) - even if at the end of processing there are only 10 items which need to be rendered.
I saw huge improvements by changing the ObservableCollection to a List, and refreshing the DataGrid manually at the end of processing - this way the UI only ever needs to process 10 items. I found that this change caused a 50% performance gain, as well as allowing the UI to be 100% responsive while the List is being processed.
If you are processing the list for a long time, and need to show live changes, you could refresh the DataGrid every 100 items. This would show results with about 0.5 second accuracy which should be close enough.
I have a form with two panels(top, bottom), each panel contains grids. Basically, it's a Master-Detail form where selecting a row from the top grid would show details in the bottom grid.
Binding the data to the detail grid is taking some time. Since binding is done on UI thread, it blocks the thread and therefore the user cannot select another row from the master grid until the binding is done.
Please note that by binding I don't mean getting data from data source. It's the actual binding that's taking longer as it does a lot of data massaging. How can I keep the UI thread free while the detail grid is doing it's binding?
Thanks a million.
You can't. The update of the UI has to be performed on the UI thread.
You may be able to speed up the binding by using things such as BeginUpdate/EndUpdate which is available on some controls but as you don't specify what you are using I can't say if that's available.
You can use a background thread to perform your data retrieval, then use the UI thread to display it.
If I were you, it sounds like you are dealing with a LOT of data, and I would separate all the "massaging" you can into a separate thread process.
So maybe for example when a master record is created, you "manually" spin off the detail data in a background thread to another dataset and do the massaging, then just bind the resulting dataset to the grid. That way the only thing taking place on the UI thread is just the UI binding.
Ultimately if it's taking that long, you may be approaching a critical point in your application where you need to manually do what you need to do in code rather than using the out of the box data binding features in .NET.
Finally I found the solution. The solution doesn't include multithreading to start with. As I said that the delay was in binding the grid meaning the main thread was held, we couldn't do much. So the solution is to bring delays. When user selects the master row, a timer ticks off for a certain time. If another request is made before the time is expired, timer gets restarted. This is we ignore all calls that are made because user was clicking or selecting rows too fast. Once timer is expired, I take the selected row and display data. Simple and elegant solution.