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?
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.
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.
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.
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.
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 );
}
vous devez mettre à jour à L'interface utilisateur, alors utilisez
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
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...
}));
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
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.
ça me va.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = false;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
//Your Code here.
}, null);
}).Start();
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.
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();
}
}
}
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