Mein Code ist wie folgt
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);
}
}
Der Schritt objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
zum Abrufen von Rasterdaten löst eine Ausnahme aus
Der aufrufende Thread kann nicht auf dieses Objekt zugreifen, da es einem anderen Thread gehört.
Was ist hier los?
c#
wpf
multithreading
backgroundworker
Kuntady Nithesh
quelle
quelle
Antworten:
Dies ist ein häufiges Problem bei den ersten Schritten. Wann immer Sie Ihre UI-Elemente von einem anderen Thread als dem Haupt-Thread aktualisieren, müssen Sie Folgendes verwenden:
Sie können auch
control.Dispatcher.CheckAccess()
überprüfen, ob der aktuelle Thread das Steuerelement besitzt. Wenn es es besitzt, sieht Ihr Code wie gewohnt aus. Verwenden Sie andernfalls das obige Muster.quelle
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);
um den Dispatcher zu erhalten, wenn nicht im UI-Thread gemäß dieser Antwortthis.Dispatcher.Invoke
... stattdessen ...myControl.Dispatcher.Invoke
:) Ich musste ein Objekt zurückgeben, also tat ich esmyControlDispatcher.Invoke<object>(() => myControl.DataContext)
;Eine weitere gute Verwendung
Dispatcher.Invoke
ist das sofortige Aktualisieren der Benutzeroberfläche in einer Funktion, die andere Aufgaben ausführt:Ich verwende dies, um den Schaltflächentext auf " Verarbeitung ... " zu aktualisieren und ihn zu deaktivieren, während ich
WebClient
Anfragen stelle.quelle
Um meine 2 Cent hinzuzufügen, kann die Ausnahme auch dann auftreten, wenn Sie Ihren Code durchrufen
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.Der Punkt ist , dass Sie anrufen müssen
Invoke()
von derDispatcher
der Kontrolle , dass Sie zugreifen möchten , die in einigen Fällen als nicht gleich seinSystem.Windows.Threading.Dispatcher.CurrentDispatcher
. Also sollten Sie stattdessen verwendenYourControl.Dispatcher.Invoke()
, um sicher zu sein. Ich schlug mir ein paar Stunden lang den Kopf, bevor ich das merkte.Aktualisieren
Für zukünftige Leser scheint sich dies in den neueren Versionen von .NET (4.0 und höher) geändert zu haben. Jetzt müssen Sie sich nicht mehr um den richtigen Dispatcher kümmern, wenn Sie die UI-Backing-Eigenschaften in Ihrer VM aktualisieren. Die WPF-Engine führt Cross-Thread-Aufrufe für den richtigen UI-Thread durch. Weitere Details finden Sie hier . Danke an @aaronburro für die Infos und den Link. Vielleicht möchten Sie auch unser Gespräch unten in Kommentaren lesen.
quelle
Dispatcher
. In diesen Fällen (die zugegebenermaßen selten sind) ist das AnrufenControl.Dispatcher
der sichere Ansatz. Als Referenz können Sie diesen Artikel sowie diesen SO-Beitrag sehen (insbesondere die Antwort von Thaddäus).Wenn Sie auf dieses Problem stoßen und UI-Steuerelemente bei der Arbeit mit oder in WPF in einem separaten Arbeitsthread erstellt wurden , rufen Sie zuerst die Methode auf, bevor Sie das oder als Parameter an eine Methode übergeben. Die Verwendung funktioniert in solchen Fällen nicht
BitmapSource
ImageSource
Freeze()
BitmapSource
ImageSource
Application.Current.Dispatcher.Invoke()
quelle
Das ist mir passiert, weil ich versucht habe, mich
access UI
einzumischenanother thread insted of UI thread
so was
Um dieses Problem zu lösen, wickeln Sie jeden UI-Anruf in das ein, was Candide oben in seiner Antwort erwähnt hat
quelle
Aus irgendeinem Grund wurde Candides Antwort nicht erstellt. Es war jedoch hilfreich, da es mich dazu brachte, dies zu finden, was perfekt funktionierte:
quelle
System.Windows.Threading.Dispatcher.CurrentDispatcher
ist der Dispatcher für den aktuellen Thread . Das heißt, wenn Sie sich in einem Hintergrund-Thread befinden, ist dies nicht der Dispatcher des UI-Threads. Verwenden Sie, um auf den Dispatcher des UI-Threads zuzugreifenSystem.Windows.Application.Current.Dispatcher
.Sie müssen in der Benutzeroberfläche aktualisieren, also verwenden
quelle
Das funktioniert bei mir.
quelle
Ich fand auch, dass dies
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
nicht immer der Dispatcher der Zielkontrolle ist, so wie dotNet in seiner Antwort schrieb. Ich hatte keinen Zugriff auf den eigenen Dispatcher der Steuerung, also habe ich verwendetApplication.Current.Dispatcher
und das Problem gelöst.quelle
Das Problem ist, dass Sie
GetGridData
von einem Hintergrund-Thread aus aufrufen . Diese Methode greift auf mehrere WPF-Steuerelemente zu, die an den Hauptthread gebunden sind. Jeder Versuch, über einen Hintergrund-Thread auf sie zuzugreifen, führt zu diesem Fehler.Um zum richtigen Thread zurückzukehren, sollten Sie verwenden
SynchronizationContext.Current.Post
. In diesem speziellen Fall scheint der Großteil der Arbeit, die Sie ausführen, auf der Benutzeroberfläche zu basieren. Daher würden Sie einen Hintergrund-Thread erstellen, um sofort zum UI-Thread zurückzukehren und einige Arbeiten auszuführen. Sie müssen Ihren Code ein wenig umgestalten, damit er die teure Arbeit am Hintergrund-Thread erledigen und anschließend die neuen Daten an den UI-Thread senden kannquelle
Wie hier erwähnt ,
Dispatcher.Invoke
könnte die Benutzeroberfläche einfrieren. SollteDispatcher.BeginInvoke
stattdessen verwenden.Hier ist eine praktische Erweiterungsklasse, um das Überprüfen und Aufrufen des Dispatcher-Aufrufs zu vereinfachen.
Beispielnutzung: (Aufruf aus dem WPF-Fenster)
Erweiterungsklasse:
quelle
Eine andere Lösung besteht darin, sicherzustellen, dass Ihre Steuerelemente im UI-Thread erstellt werden, nicht beispielsweise von einem Hintergrund-Worker-Thread.
quelle
Ich habe immer wieder den Fehler erhalten, als ich meiner WPF-Anwendung kaskadierende Comboboxen hinzugefügt und den Fehler mithilfe dieser API behoben habe:
Weitere Informationen finden Sie unter https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Vers % 3Dv4.7); k (DevLang-csharp) & rd = true
quelle