首页 文章

调用线程无法访问此对象,因为另一个线程拥有它

提问于
浏览
259

我的代码如下

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; 会引发异常

调用线程无法访问此对象,因为另一个线程拥有它 .

这有什么不对?

13 回答

  • 26

    我还发现 System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() 并不总是目标控制的调度程序,正如dotNet在他的回答中写道的那样 . 我没有't had access to control'自己的调度员,所以我使用 Application.Current.Dispatcher 它解决了问题 .

  • 3

    出于某种原因,Candide的答案没有 Build 起来 . 不过,这很有帮助,因为它让我找到了这个,它完美地运作了:

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

    Dispatcher.Invoke 的另一个好用途是立即更新执行其他任务的函数中的UI:

    // Force WPF to render UI changes immediately with this magic line of code...
    Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);
    

    我用它将按钮文本更新为“Processing ...”并在发出 WebClient 请求时禁用它 .

  • 12

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

  • 30

    要加上我的2美分,即使您通过 System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() 调用代码,也会发生异常 .
    关键是你必须调用 control that you're trying to accessDispatcher Dispatcher ,在某些情况下可能与 System.Windows.Threading.Dispatcher.CurrentDispatcher 不同 . 所以你应该使用 YourControl.Dispatcher.Invoke() 来保证安全 . 在我意识到这一点之前,我正在敲打我的头几个小时 .

  • 18

    当我将级联组合框添加到我的WPF应用程序时,我不断收到错误,并使用此API解决了错误:

    using System.Windows.Data;
    
        private readonly object _lock = new object();
        private CustomObservableCollection<string> _myUiBoundProperty;
        public CustomObservableCollection<string> MyUiBoundProperty
        {
            get { return _myUiBoundProperty; }
            set
            {
                if (value == _myUiBoundProperty) return;
                _myUiBoundProperty = value;
                NotifyPropertyChanged(nameof(MyUiBoundProperty));
            }
        }
    
        public MyViewModelCtor(INavigationService navigationService) 
        {
           // Other code...
           BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );
    
        }
    

    有关详细信息,请参阅https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)&rd=true

  • 2

    如上所述hereDispatcher.Invoke 可以冻结用户界面 . 应该使用 Dispatcher.BeginInvoke 代替 .

    这是一个方便的扩展类,用于简化检查和调用调度程序调用 .

    示例用法:(从WPF窗口调用)

    this Dispatcher.InvokeIfRequired(new Action(() =>
    {
        logTextbox.AppendText(message);
        logTextbox.ScrollToEnd();
    }));
    

    扩展类:

    using System;
    using System.Windows.Threading;
    
    namespace WpfUtility
    {
        public static class DispatcherExtension
        {
            public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
            {
                if (dispatcher == null)
                {
                    return;
                }
                if (!dispatcher.CheckAccess())
                {
                    dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
                    return;
                }
                action();
            }
        }
    }
    
  • 11

    这发生在我身上,因为我在 another thread insted of UI thread 试图 access UI 组件

    像这样

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

    要解决这个问题,请在what Candide mentioned above in his answer中包装任何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 );
    }
    
  • 0

    这是人们入门的常见问题 . 每当您从主线程以外的线程更新UI元素时,您需要使用:

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

    您还可以使用control.Dispatcher.CheckAccess()来检查当前线程是否拥有该控件 . 如果它拥有它,您的代码看起来很正常 . 否则,请使用上述模式 .

  • 1

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

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

  • 0

    这对我有用 .

    new Thread(() =>
            {
    
            Thread.CurrentThread.IsBackground = false;
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
    
              //Your Code here.
    
            }, null);
            }).Start();
    
  • 0

    您需要更新到UI,所以使用

    Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
    
  • 40

    如果有人试图在WPF和线程中使用 BitmapSource 并且具有相同的消息:在将 BitmapSource 作为线程参数传递之前,先调用 Freeze() 方法 .

相关问题