Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им - PullRequest
293 голосов
/ 16 марта 2012

Мой код, как показано ниже

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 ]

609 голосов
/ 16 марта 2012

Это обычная проблема для людей, начинающих. Всякий раз, когда вы обновляете свои элементы пользовательского интерфейса из потока, отличного от основного потока, вам необходимо использовать:

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

Вы также можете использовать control.Dispatcher.CheckAccess(), чтобы проверить, принадлежит ли текущий поток элемент управления. Если он владеет им, ваш код выглядит как обычно. В противном случае используйте вышеуказанный шаблон.

46 голосов
/ 17 июня 2014

Другое хорошее использование для Dispatcher.Invoke - для немедленного обновления пользовательского интерфейса в функции, выполняющей другие задачи:

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

Я использую это для обновления текста кнопки до " Обработка ..."и отключите его при выполнении WebClient запросов.

37 голосов
/ 21 октября 2015

Чтобы добавить мои 2 цента, может возникнуть исключение, даже если вы наберете свой код через System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke().Дело в том, что вам нужно вызвать Invoke() из Dispatcher элемента управления , к которому вы пытаетесь обратиться , что в некоторых случаях может не совпадать с System.Windows.Threading.Dispatcher.CurrentDispatcher.Так что вместо этого вы должны использовать YourControl.Dispatcher.Invoke(), чтобы быть в безопасности.Я несколько часов колотил головой, прежде чем понял это.

Обновление

Для будущих читателей, похоже, что это изменилось в более новых версиях .NET (4.0 и выше).Теперь вам больше не нужно беспокоиться о правильном диспетчере при обновлении свойств поддержки пользовательского интерфейса в вашей виртуальной машине.Механизм WPF будет маршалировать вызовы между потоками в правильном потоке пользовательского интерфейса.Подробнее здесь .Спасибо @aaronburro за информацию и ссылку.Вы также можете прочитать нашу беседу ниже в комментариях.

30 голосов
/ 25 ноября 2015

Если вы столкнулись с этой проблемой, и элементы управления пользовательского интерфейса были созданы в отдельном рабочем потоке при работе с BitmapSource или ImageSource в WPF, сначала вызовите метод Freeze(), прежде чем передавать BitmapSource илиImageSource как параметр для любого метода.Использование Application.Current.Dispatcher.Invoke() не работает в таких случаях

22 голосов
/ 27 июня 2016

это случилось со мной, потому что я пытался 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);
}

, чтобы решить эту проблему, оберните любой вызов пользовательского интерфейса в что Кандид упомянул выше в своем ответе

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 );
}
14 голосов
/ 12 июля 2015

По какой-то причине ответ Кэндидэ не получил.Тем не менее, это было полезно, поскольку привело меня к поиску этого, который отлично работал:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
    {
       //your code here...
    }));
13 голосов
/ 05 марта 2013

Вам необходимо обновить интерфейс пользователя, поэтому используйте

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 
3 голосов
/ 11 мая 2017

Я также обнаружил, что System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() не всегда является диспетчером контроля цели, как писал dotNet в своем ответе. У меня не было доступа к собственному диспетчеру управления, поэтому я использовал Application.Current.Dispatcher, и это решило проблему.

2 голосов
/ 16 марта 2012

Проблема в том, что вы звоните GetGridData из фонового потока. Этот метод обращается к нескольким элементам управления WPF, которые связаны с основным потоком. Любая попытка доступа к ним из фонового потока приведет к этой ошибке.

Чтобы вернуться к правильной теме, вы должны использовать SynchronizationContext.Current.Post. Однако в данном конкретном случае кажется, что большая часть работы, которую вы выполняете, основана на пользовательском интерфейсе. Следовательно, вы будете создавать фоновый поток, чтобы сразу вернуться к потоку пользовательского интерфейса и выполнить некоторую работу. Вам нужно немного изменить код, чтобы он мог выполнять дорогостоящую работу в фоновом потоке, а затем публиковать новые данные в потоке пользовательского интерфейса

1 голос
/ 15 июня 2018

Это работает для меня.

new Thread(() =>
        {

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

          //Your Code here.

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