Le thread appelant ne peut pas accéder à cet objet parce qu'un thread différent le possède

mon code est comme ci-dessous

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

L'étape objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null; dans la grille de données throws exception

le thread appelant ne peut pas accéder à cet objet car un autre elle appartient à thread.

Qu'est-ce qui ne va pas ici?

249
demandé sur Christos 2012-03-16 10:07:07

13 réponses

il s'agit d'un problème courant lorsque les gens commencent. Chaque fois que vous mettez à jour vos éléments UI à partir d'un thread autre que le thread principal, vous devez utiliser:

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

vous pouvez également utiliser control.Dispatcher.CheckAccess() pour vérifier si le thread actuel possède le contrôle. Si c'est le cas, votre code a l'air normal. Sinon, utilisez le schéma ci-dessus.

519
répondu Candide 2017-02-12 00:18:55

une autre bonne utilisation de Dispatcher.Invoke est de mettre à jour immédiatement L'UI dans une fonction qui exécute d'autres tâches:

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

j'utilise ceci pour mettre à jour le texte de bouton au traitement " ... " et de désactiver lors de la prise de WebClient les demandes.

40
répondu computerGuyCJ 2018-01-12 04:37:16

pour ajouter mes 2 cents, l'exception peut se produire même si vous appelez votre code par System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() .

le point est que vous devez appeler Invoke() du Dispatcher du contrôle que vous essayez d'accéder à , qui dans certains cas peut ne pas être le même que System.Windows.Threading.Dispatcher.CurrentDispatcher . Vous devez donc utiliser YourControl.Dispatcher.Invoke() pour être sûr. Je me cognais la tête quelques heures avant de réaliser ça.

28
répondu dotNET 2018-04-23 09:11:57

si quelqu'un essaie de travailler avec BitmapSource dans WPF et threads et a ce même message: il suffit d'appeler Freeze() méthode avant de passer un BitmapSource comme un paramètre de thread.

26
répondu juFo 2018-04-23 08:59:50

c'est arrivé avec moi parce que j'ai essayé de access UI composant dans another thread insted of UI thread

comme ceci

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

pour résoudre ce problème, envelopper tout appel d'ui à l'intérieur ce que Candide mentionné ci-dessus dans sa réponse

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 );
}
18
répondu Basheer AL-MOMANI 2017-05-23 12:02:56

vous devez mettre à jour à L'interface utilisateur, alors utilisez

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 
12
répondu VikramBose 2013-03-05 12:02:21

pour une raison quelconque, la réponse de Candide n'a pas fonctionné. Il était utile, cependant, car il m'a conduit à trouver ceci, qui a fonctionné parfaitement:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
    {
       //your code here...
    }));
11
répondu Sarah 2015-07-12 10:44:12

le problème est que vous appelez GetGridData à partir d'un thread de fond. Cette méthode accède à plusieurs contrôles WPF qui sont liés au thread principal. Toute tentative d'y accéder à partir d'un thread d'arrière-plan va entraîner cette erreur.

pour revenir au bon fil, vous devez utiliser SynchronizationContext.Current.Post . Toutefois, dans ce cas précis, il semble que la majorité du travail que vous faites est fondée sur L'assurance-chômage. Par conséquent, vous créeriez un fil d'arrière-plan juste pour retourner immédiatement au fil UI et faire un peu de travail. Vous devez remanier un peu votre code pour qu'il puisse faire le travail coûteux sur le thread d'arrière-plan, puis postez les nouvelles données sur le thread D'UI après

2
répondu JaredPar 2012-03-16 06:18:08

j'ai aussi constaté que System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() n'est pas toujours le répartiteur du contrôle de la cible, comme l'a écrit dotNet dans sa réponse. Je n'avais pas accès au répartiteur de control, donc j'ai utilisé Application.Current.Dispatcher et ça a résolu le problème.

2
répondu Paulus Limma 2017-05-11 11:45:02

ça me va.

        new Thread(() =>
        {

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

          //Your Code here.

        }, null);
        }).Start();
1
répondu sony vizio 2018-06-15 15:41:52

de plus, une autre solution consiste à s'assurer que vos commandes sont créées dans un thread UI, et non par un thread worker background par exemple.

0
répondu FindOutIslamNow 2017-02-23 08:27:08

comme mentionné ici , Dispatcher.Invoke pourrait geler L'UI. Devrait utiliser Dispatcher.BeginInvoke à la place.

Voici une classe d'extension pratique pour simplifier la vérification et l'appel de l'invocation du répartiteur.

Exemple d'utilisation: (appel de fenêtre WPF)

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

Extension de la classe:

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();
        }
    }
}
0
répondu Jeson Martajaya 2018-03-29 12:53:24

j'ai continué à obtenir l'erreur lorsque j'ai ajouté des ComboBox en cascade à mon application WPF, et j'ai résolu l'erreur en utilisant cette 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 );

    }

pour plus de détails, veuillez consulter 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=vrai

0
répondu user8128167 2018-06-15 19:00:40