调用线程不能访问这个对象,因为不同的线程拥有它
我的代码如下
public CountryStandards() { InitializeComponent(); try { FillPageControls(); } catch (Exception ex) { MessageBox.Show(ex.Message, "Country Standards", MessageBoxButton.OK, MessageBoxImage.Error); } } /// <summary> /// Fills the page controls. /// </summary> private void FillPageControls() { popUpProgressBar.IsOpen = true; lblProgress.Content = "Loading. Please wait..."; progress.IsIndeterminate = true; worker = new BackgroundWorker(); worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork); worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged); worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); worker.RunWorkerAsync(); } private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) { GetGridData(null, 0); // filling grid } private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) { progress.Value = e.ProgressPercentage; } private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) { worker = null; popUpProgressBar.IsOpen = false; //filling Region dropdown Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards(); objUDMCountryStandards.Operation = "SELECT_REGION"; DataSet dsRegionStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards); if (!StandardsDefault.IsNullOrEmptyDataTable(dsRegionStandards, 0)) StandardsDefault.FillComboBox(cmbRegion, dsRegionStandards.Tables[0], "Region", "RegionId"); //filling Currency dropdown objUDMCountryStandards = new Standards.UDMCountryStandards(); objUDMCountryStandards.Operation = "SELECT_CURRENCY"; DataSet dsCurrencyStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards); if (!StandardsDefault.IsNullOrEmptyDataTable(dsCurrencyStandards, 0)) StandardsDefault.FillComboBox(cmbCurrency, dsCurrencyStandards.Tables[0], "CurrencyName", "CurrencyId"); if (Users.UserRole != "Admin") btnSave.IsEnabled = false; } /// <summary> /// Gets the grid data. /// </summary> /// <param name="sender">The sender.</param> /// <param name="pageIndex">Index of the page.( used in case of paging) </pamam> private void GetGridData(object sender, int pageIndex) { Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards(); objUDMCountryStandards.Operation = "SELECT"; objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null; DataSet dsCountryStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards); if (!StandardsDefault.IsNullOrEmptyDataTable(dsCountryStandards, 0) && (chkbxMarketsSearch.IsChecked == true || chkbxBudgetsSearch.IsChecked == true || chkbxProgramsSearch.IsChecked == true)) { DataTable objDataTable = StandardsDefault.FilterDatatableForModules(dsCountryStandards.Tables[0], "Country", chkbxMarketsSearch, chkbxBudgetsSearch, chkbxProgramsSearch); dgCountryList.ItemsSource = objDataTable.DefaultView; } else { MessageBox.Show("No Records Found", "Country Standards", MessageBoxButton.OK, MessageBoxImage.Information); btnClear_Click(null, null); } }
步骤objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
在获取网格数据时抛出exception
调用线程不能访问这个对象,因为不同的线程拥有它。
这里有什么问题?
这是人们入门的常见问题。 无论何时从主线程以外的线程更新UI元素,都需要使用:
this.Dispatcher.Invoke(() => { ...// your code here. });
您也可以使用control.Dispatcher.CheckAccess()
来检查当前线程是否拥有该控件。 如果它拥有它,你的代码看起来是正常的。 否则,使用上面的模式。
没有足够的评论或+1的声誉,但我想推广Candide的答案。
Dispatcher.Invoke
另一个好用途是立即更新执行其他任务的函数中的UI:
// Force WPF to render UI changes immediately with this magic line of code... Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
我使用这个来更新button文本到“ 处理… ”,并在WebClient
请求时禁用它。
要添加我的2美分,即使您通过System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
调用您的代码,可能会发生exception。 重点是你必须Invoke()
你试图访问的控件的Dispatcher
的Invoke()
,在某些情况下,这可能与System.Windows.Threading.Dispatcher.CurrentDispatcher
不一样。 所以相反,你应该使用YourControl.Dispatcher.Invoke()
是安全的。 在我意识到这一点之前,我正在敲头了几个小时。
如果有人试图在WPF和线程中使用BitmapSource,并且有这个相同的消息:只要在将BitmapSource作为线程parameter passing之前调用.Freeze()方法。
这发生在我身上,因为我试图access UI
another thread insted of UI thread
组件
喜欢这个
private void button_Click(object sender, RoutedEventArgs e) { new Thread(SyncProcces).Start(); } private void SyncProcces() { string val1 = null, val2 = null; //here is the problem val1 = textBox1.Text;//access UI in another thread val2 = textBox2.Text;//access UI in another thread localStore = new LocalStore(val1); remoteStore = new RemoteStore(val2); }
为了解决这个问题, 在他的答案中提到上面提到的Candide中的任何一个ui调用
private void SyncProcces() { string val1 = null, val2 = null; this.Dispatcher.Invoke((Action)(() => {//this refer to form in WPF application val1 = textBox.Text; val2 = textBox_Copy.Text; })); localStore = new LocalStore(val1); remoteStore = new RemoteStore(val2 ); }
你需要更新到用户界面,所以使用
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
由于某种原因,Candide的答案没有build立。 不过,这很有帮助,因为它让我发现了这一点,
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() => { //your code here... }));
问题是你从后台线程调用GetGridData
。 此方法访问绑定到主线程的几个WPF控件。 任何尝试从后台线程访问它们都会导致这个错误。
为了回到正确的线程,你应该使用SynchronizationContext.Current.Post
。 但是在这个特殊情况下,你所做的大部分工作似乎都是基于UI的。 因此,你将创build一个后台线程,只是立即回到UI线程,并做一些工作。 您需要稍微重构代码,以便在后台线程上执行昂贵的工作,然后将新数据发布到UI线程
此外,另一个解决scheme是确保您的控件在UI线程中创build,而不是由后台工作线程创build。
我还发现System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
并不总是调度目标控制,就像dotNet在他的答案中写道。 我没有访问控制自己的调度,所以我使用Application.Current.Dispatcher
,它解决了这个问题。
(没有足够的声望来添加评论)
VikramBosebuild议使用BeginInvoke
,但不要提及EndInvoke
。 好的做法是,“每个BeginInvoke
都有一个匹配的EndInvoke
”,当然还需要一些防范竞争条件的保护措施(想想:多个BeginInvoke
代码会发生什么,但是还没有完成处理呢?)
这很容易忘记,我已经看到这个错误(是的,这是一个错误)在MSDN的例子和WinForms出版的书籍