Reputation: 1402
I have a datagrid in my WPF app, and when the app launches, I get deployment records from the database and load them into an ObservableCollection, which is bound to the datagrid.
Via a timer, I use a BackgroundWorker to go out and get any new records from the database and put them into a new ObservableCollection.
The, via RunWorkerCompleted I try to update the datagrid with the items in the new ObservableCollection.
However, I am getting a 'System.NotSupportedException'.
Additional information: This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.
I thought that I could access the UI controls from the RunWorkerCompleted method, but appearently there is something going on I don't understand.
My code is below:
private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
{
//Init the deployment collection
deployments = DataAccess.GetDeployments();
dgDeployments.ItemsSource = deployments;
//Setup the background worker
bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
//Setup timer
System.Threading.Timer t = new System.Threading.Timer(new System.Threading.TimerCallback(TimerProc));
t.Change(10000, 0);
}
private void TimerProc(object state)
{
//MessageBox.Show("Timer fired!");
//Get the latest currentTime from the items in the grid
DateTime? latestTime = DataAccess.GetDeployments().ToList()[0].CurrentTime;
bw.RunWorkerAsync(latestTime);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Get new records that have changed since that time
e.Result = (ObservableCollection<Deployment>)DataAccess.GetDeployments((DateTime)e.Argument);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
MessageBox.Show("The operation was cancelled");
}
else
{
ObservableCollection<Deployment> newDeployments = (ObservableCollection<Deployment>)e.Result;
foreach (Deployment d in newDeployments)
{
//Remove this new/changed deployment from the collection bound to the datagrid
int index = deployments.IndexOf(deployments.Where(x => x.UniqueID == d.UniqueID).FirstOrDefault());
if (index > -1)
{
deployments.RemoveAt(index);
}
//Now add the new deployments
deployments.Add(d);
deployments.Move(deployments.IndexOf(d), 0);
}
}
}
Upvotes: 2
Views: 101
Reputation: 1402
Thank you everyone for the good information. It has helped me understand this issue better. Here is the code I used that ended up working for me.
Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate()
{
//Remove the old deployment
deployments.RemoveAt(index);
//Now add the new deployments
deployments.Add(d);
deployments.Move(deployments.IndexOf(d), 0);
});
Upvotes: 1
Reputation: 31616
The timer process starts
the background worker in a different thread. Try starting the background worker in the GUI thread so the completed operation will run on the GUI thread and not the thread started from the timer.
Either Invoke
the operation to the GUI thread or just make the timer a DispatchTimer
.
Upvotes: 2