我的代码如下

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;在获取网格数据时抛出异常

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

这里出了什么问题?


当前回答

这对我很有用。

new Thread(() =>
        {

        Thread.CurrentThread.IsBackground = false;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {

          //Your Code here.

        }, null);
        }).Start();

其他回答

补充一点,即使你通过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.

有时,抛出异常的可能是您创建的对象,而不是我明显看到的目标。

在我的代码中:

xaml文件:

<Grid Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
    <TextBlock x:Name="tbScreenLog" VerticalAlignment="Stretch" Background="Black" FontSize="12" Foreground="#FF919191" HorizontalAlignment="Stretch"/>
</Grid>

xaml.cs文件:

System.Windows.Documents.Run rnLine = new System.Windows.Documents.Run(Message.Item2 + "\r\n");
rnLine.Foreground = LineAlternate ? Brushes.Green : Brushes.Orange;

Dispatcher.Invoke(()=> {
    tbScreenLog.Inlines.Add(rnLine);
});
LineAlternate = !LineAlternate;

我得到了关于从不同线程访问对象的异常,但我在UI线程上调用它??

过了一段时间,它吓倒了我,这不是关于TextBlock对象,而是关于运行对象我创建之前调用。

把代码改成这样就解决了我的问题:

Dispatcher.Invoke(()=> {
    Run rnLine = new Run(Message.Item2 + "\r\n");
    rnLine.Foreground = LineAlternate ? Brushes.Green : Brushes.Orange;
    tbScreenLog.Inlines.Add(rnLine);
});
LineAlternate = !LineAlternate;

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

问题是你从后台线程调用GetGridData。这个方法访问几个绑定到主线程的WPF控件。任何试图从后台线程访问它们的尝试都会导致此错误。

为了回到正确的线程,你应该使用SynchronizationContext.Current.Post。然而,在这种特殊情况下,你所做的大部分工作似乎都是基于UI的。因此,你将创建一个后台线程,只是为了立即回到UI线程并做一些工作。您需要稍微重构一下代码,以便它能够在后台线程上完成昂贵的工作,然后将新数据发布到UI线程

我还发现System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()并不总是目标控制的调度程序,就像dotNet在他的回答中所写的那样。我没有访问控件自己的调度程序,所以我使用Application.Current.Dispatcher,它解决了这个问题。