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;
d'obtention des données de grille lève une exception
Le thread appelant ne peut pas accéder à cet objet car un thread différent le possède.
Qu'est-ce qui ne va pas ici?
c#
wpf
multithreading
backgroundworker
Kuntady Nithesh
la source
la source
Réponses:
Il s'agit d'un problème courant chez les personnes qui commencent. Chaque fois que vous mettez à jour vos éléments d'interface utilisateur à partir d'un thread autre que le thread principal, vous devez utiliser:
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 semble normal. Sinon, utilisez le modèle ci-dessus.la source
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);
pour obtenir le répartiteur sinon sur le thread d'interface utilisateur selon cette réponsethis.Dispatcher.Invoke
.... à la place ...myControl.Dispatcher.Invoke
:) Je devais retourner un objet alors je l'ai faitmyControlDispatcher.Invoke<object>(() => myControl.DataContext)
;Une autre bonne utilisation pour
Dispatcher.Invoke
est de mettre à jour immédiatement l'interface utilisateur dans une fonction qui effectue d'autres tâches:Je l'utilise pour mettre à jour le texte du bouton en " Traitement ... " et le désactiver lors de la
WebClient
demande.la source
Pour ajouter mes 2 cents, l'exception peut se produire même si vous appelez votre code via
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.Le point est que vous devez appeler
Invoke()
deDispatcher
la commande que vous tentez d'accéder , dans certains cas , ne peut pas être le même queSystem.Windows.Threading.Dispatcher.CurrentDispatcher
. Donc, à la place, vous devez utiliserYourControl.Dispatcher.Invoke()
pour être en sécurité. Je me cognais la tête pendant quelques heures avant de m'en rendre compte.Mettre à jour
Pour les futurs lecteurs, il semble que cela ait changé dans les nouvelles versions de .NET (4.0 et supérieures). Désormais, vous n'avez plus à vous soucier du bon répartiteur lors de la mise à jour des propriétés de sauvegarde de l'interface utilisateur dans votre machine virtuelle. Le moteur WPF va rassembler les appels inter-threads sur le thread d'interface utilisateur correct. Voir plus de détails ici . Merci à @aaronburro pour l'info et le lien. Vous pouvez également lire notre conversation ci-dessous dans les commentaires.
la source
Dispatcher
. Dans ces cas (qui sont certes rares), appelerControl.Dispatcher
est l'approche sûre. Pour référence, vous pouvez voir cet article ainsi que cet article SO (en particulier la réponse de Squidward).Si vous rencontrez ce problème et que des contrôles d'interface utilisateur ont été créés sur un thread de travail distinct lorsque vous travaillez avec WPF
BitmapSource
ouImageSource
dans WPF, appelez d'Freeze()
abord la méthode avant de passer leBitmapSource
ouImageSource
comme paramètre à une méthode. L'utilisationApplication.Current.Dispatcher.Invoke()
ne fonctionne pas dans de tels casla source
ce qui est arrivé avec moi parce que j'essayé de
access UI
dans le composantanother thread insted of UI thread
comme ça
pour résoudre ce problème, enveloppez tout appel ui dans ce que Candide a mentionné ci-dessus dans sa réponse
la source
Pour une raison quelconque, la réponse de Candide n'a pas été construite. Cela a été utile, cependant, car cela m'a conduit à trouver cela, qui fonctionnait parfaitement:
la source
System.Windows.Threading.Dispatcher.CurrentDispatcher
est le répartiteur du thread actuel . Cela signifie que si vous êtes sur un thread d'arrière-plan, ce ne sera pas le répartiteur du thread d'interface utilisateur. Pour accéder au répartiteur du thread d'interface utilisateur, utilisezSystem.Windows.Application.Current.Dispatcher
.Vous devez mettre à jour l'interface utilisateur, alors utilisez
la source
Cela fonctionne pour moi.
la source
J'ai également constaté que ce
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
n'est pas toujours le répartiteur du contrôle des cibles, comme l'a écrit dotNet dans sa réponse. Je n'avais pas accès au propre répartiteur du contrôle, j'ai donc utiliséApplication.Current.Dispatcher
et cela a résolu le problème.la source
Le problème est que vous appelez à
GetGridData
partir d'un thread d'arrière-plan. Cette méthode accède à plusieurs contrôles WPF qui sont liés au thread principal. Toute tentative pour y accéder à partir d'un fil d'arrière-plan entraînera cette erreur.Afin de revenir au bon thread, vous devez utiliser
SynchronizationContext.Current.Post
. Cependant, dans ce cas particulier, il semble que la majorité du travail que vous effectuez soit basée sur l'interface utilisateur. Par conséquent, vous créez un thread d'arrière-plan pour revenir immédiatement au thread d'interface utilisateur et effectuer un travail. Vous devez refactoriser un peu votre code afin qu'il puisse effectuer le travail coûteux sur le thread d'arrière-plan, puis publier les nouvelles données sur le thread d'interface utilisateur par la suitela source
Comme mentionné ici ,
Dispatcher.Invoke
pourrait geler l'interface utilisateur. Devrait utiliser à laDispatcher.BeginInvoke
place.Voici une classe d'extension pratique pour simplifier la vérification et l'appel du répartiteur d'appel.
Exemple d'utilisation: (appel depuis la fenêtre WPF)
Classe d'extension:
la source
En outre, une autre solution consiste à garantir que vos contrôles sont créés dans le thread d'interface utilisateur, et non par un thread de travail d'arrière-plan par exemple.
la source
J'ai continué à recevoir l'erreur lorsque j'ai ajouté des zones de liste déroulante en cascade à mon application WPF et j'ai résolu l'erreur en utilisant cette API:
Pour plus d'informations, consultez https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization) ;k( TargetFrameworkMoniker - .NETFramework,V % 3Dv4.7); k (DevLang-csharp) & rd = true
la source