我的代码如下

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);
    }
}

该步骤遵循judmcountrystandards。Country = txtSearchCountry.Text.Trim() !=字符串。空的吗?txtSearchCountry。文本:null;在获取网格数据时抛出异常

调用线程不能访问此对象,因为有不同的 线程拥有它。

这里出了什么问题?


当前回答

补充一点,即使你通过System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()调用你的代码,也会发生异常。关键是你必须调用你试图访问的控件的Dispatcher的Invoke(),在某些情况下可能与System.Windows.Threading.Dispatcher.CurrentDispatcher不相同。因此,为了安全起见,您应该使用YourControl.Dispatcher.Invoke()。我敲了几个小时的头才意识到这一点。

更新

对于未来的读者来说,这一点在。net的新版本(4.0及以上版本)中似乎有所改变。现在,在VM中更新ui支持属性时,您不再需要担心正确的调度程序。WPF引擎将在正确的UI线程上编组跨线程调用。点击这里查看更多细节。感谢@aaronburro提供的信息和链接。你也可以在评论中阅读我们的对话。

更新2

Since this is a popular post now, I thought I'd share my experience that I had in the following years. The behavior seems to be that any property bindings will update correctly in cross-thread calls (no marshalling required; WPF will handle it for you). OTOH command bindings will need to be delegated to the UI dispatcher. I have tested it with both MVVM Light and the relatively new Community Toolkit and it seems to be the case with both the old Framework and the new .NET 5 and 6. AsyncRelayCommand fails to update the UI when invoked from non-UI thread (This happens when CanExecuteChanged is fired from a worker thread which updates, for example, button's Enabled property). The solution of course is to store UI dispatcher somewhere in the global space in your VM upon startup and then use it when updating the UI.

其他回答

我在从WPF控件中选择的第二个项目上奇怪地遇到了这个错误。

原因是我将数据加载到RX SourceCache中,加载的元素将observablecollection作为导航属性包装到CollectionView中。observablecollection连接到UIThread,数据由WorkerThread加载。由于CollectionView只在显示第一个元素时被填充,不同线程的问题只发生在被选择的第二个项目上。

解决方案是将子列表作为ReadOnlyObservableCollections移动到ViewModel中,并根据当前选择的主元素筛选子元素表的完整列表。

这是人们刚开始时遇到的一个普遍问题。每当你从主线程以外的线程更新UI元素时,你需要使用:

this.Dispatcher.Invoke(() =>
{
    ...// your code here.
});

你也可以使用control. dispatcher . checkaccess()来检查当前线程是否拥有该控件。如果它确实拥有它,那么您的代码看起来就正常。否则,使用上述图案。

另外,另一个解决方案是确保控件是在UI线程中创建的,而不是由后台工作线程创建的。

出于某种原因,坎迪德的回答并不可信。尽管如此,它还是很有帮助的,因为它让我发现了这个,它非常有效:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
   //your code here...
}));

如果你遇到这个问题,并且UI控件是在WPF中使用BitmapSource或ImageSource时在单独的工作线程上创建的,在将BitmapSource或ImageSource作为参数传递给任何方法之前,首先调用Freeze()方法。使用Application.Current.Dispatcher.Invoke()在这样的实例中不起作用