444 votes

Le thread appelant ne peut pas accéder à cet objet car un autre thread le possède.

Mon code est le suivant

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)</param>
    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 get grid data throws exception "The calling thread cannot access this object because a different thread owns it". Qu'est-ce qui ne va pas ici ?

873voto

Candide Points 12137

C'est un problème fréquent chez les personnes qui débutent. Chaque fois que vous mettez à jour vos éléments d'interface utilisateur à partir d'un thread autre que le thread principal, vous devez utiliser :

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

Vous pouvez également utiliser control.Dispatcher.CheckAccess() pour vérifier si le thread actuel possède le contrôle. S'il le possède, votre code est normal. Sinon, utilisez le modèle ci-dessus.

3 votes

J'ai le même problème que l'OP ; mon problème est que l'événement provoque maintenant un débordement de la pile :\N

2 votes

J'ai repris mon ancien projet et j'ai résolu le problème. De plus, j'avais oublié de mettre +1 à ce sujet. Cette méthode fonctionne très bien ! Elle améliore le temps de chargement de mon application de 10 secondes ou même plus, simplement en utilisant des threads pour charger nos ressources localisées. Je vous remercie !

4 votes

Si je ne me trompe pas, vous ne pouvez même pas lire un objet de l'interface utilisateur à partir d'un thread non propriétaire ; cela m'a un peu surpris.

57voto

computerGuyCJ Points 11

Pas assez de réputation pour commenter ou +1, mais je voulais promouvoir la réponse de Candide.

Une autre bonne utilisation de Dispatcher.Invoke est la mise à jour immédiate de l'interface utilisateur dans une fonction qui effectue d'autres tâches :

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

Je l'utilise pour mettre à jour le texte du bouton en "Processing..." et le désactiver lors des requêtes WebClient.

5 votes

Cette réponse est discutée sur Meta. meta.stackoverflow.com/questions/361844/

0 votes

Cela a empêché mon système de contrôle d'obtenir des données sur Internet ?

22voto

VikramBose Points 53

Vous devez vous mettre à jour dans l'interface utilisateur, donc utiliser

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

3voto

JaredPar Points 333733

Le problème est que vous appelez GetGridData à partir d'un fil 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 entraînera cette erreur.

Pour revenir au fil correct, vous devez utiliser SynchronizationContext.Current.Post . Cependant, dans ce cas particulier, il semble que la majorité du travail que vous effectuez soit basé sur l'interface utilisateur. Par conséquent, vous créeriez un thread d'arrière-plan juste pour revenir immédiatement au thread de l'interface utilisateur et effectuer un travail. Vous devez remanier un peu votre code pour qu'il puisse effectuer le travail coûteux sur le thread d'arrière-plan et envoyer ensuite les nouvelles données au thread de l'interface utilisateur.

1voto

brewmanz Points 89

(pas assez de réputation pour ajouter un commentaire)

VikramBose suggère d'utiliser BeginInvoke, mais ne mentionne pas EndInvoke. La bonne pratique est que "chaque BeginInvoke a un EndInvoke correspondant" et il faut certainement une certaine protection contre les conditions de course (pensez à ce qui se passe avec plusieurs BeginInvoke de code mais aucun n'a encore terminé le traitement).

Il est facile d'oublier, et j'ai vu cette erreur (et, oui, elle est une erreur) dans les exemples MSDN et les livres publiés sur WinForms.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X