445 Stimmen

Der aufrufende Thread kann auf dieses Objekt nicht zugreifen, da es einem anderen Thread gehört

Mein Code lautet 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; in get grid data löst eine Ausnahme aus

Der aufrufende Thread kann auf dieses Objekt nicht zugreifen, da ein anderer Thread es besitzt.

Was ist hier los?

875voto

Candide Punkte 29756

Dies ist ein häufiges Problem bei Neueinsteigern. Wann immer Sie Ihre UI-Elemente von einem anderen Thread als dem Hauptthread aktualisieren, müssen Sie verwenden:

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

Sie können auch Folgendes verwenden control.Dispatcher.CheckAccess() um zu prüfen, ob der aktuelle Thread das Steuerelement besitzt. Wenn er es besitzt, sieht Ihr Code normal aus. Andernfalls verwenden Sie das obige Muster.

64voto

dotNET Punkte 30863

Um meine 2 Cent hinzuzufügen, kann die Ausnahme auftreten, auch wenn Sie Ihren Code durch aufrufen System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() .
Der Punkt ist, dass Sie die Invoke() der Dispatcher der Kontrolle, auf die Sie zugreifen wollen , was in manchen Fällen nicht dasselbe ist wie System.Windows.Threading.Dispatcher.CurrentDispatcher . Daher sollten Sie stattdessen YourControl.Dispatcher.Invoke() um sicher zu sein. Ich habe mir ein paar Stunden lang den Kopf zerbrochen, bevor mir das klar wurde.

Update

Für zukünftige Leser: Es sieht so aus, als hätte sich dies in den neueren Versionen von .NET (4.0 und höher) geändert. Jetzt müssen Sie sich nicht mehr um den richtigen Dispatcher kümmern, wenn Sie UI-Backing-Eigenschaften in Ihrer VM aktualisieren. WPF-Engine wird Marschall Cross-Thread-Aufrufe auf die richtige UI-Thread. Siehe weitere Details aquí . Danke an @aaronburro für die Info und den Link. Vielleicht möchten Sie auch unser Gespräch unten in den Kommentaren lesen.

Aktualisierung 2

Da dies nun ein beliebter Beitrag ist, dachte ich, ich teile meine Erfahrungen, die ich in den folgenden Jahren gemacht habe. Das Verhalten scheint so zu sein, dass jeder Eigenschaftsbindungen wird korrekt in Cross-Thread-Aufrufe aktualisieren (kein Marshalling erforderlich; WPF wird es für Sie behandeln). OTOH Befehlsbindungen muss an den UI-Dispatcher delegiert werden. Ich habe es sowohl mit MVVM Light als auch mit dem relativ neuen Community Toolkit getestet und es scheint der Fall zu sein, sowohl mit dem alten Framework als auch mit dem neuen .NET 5 und 6. AsyncRelayCommand kann die Benutzeroberfläche nicht aktualisieren, wenn sie von einem Nicht-UI-Thread aufgerufen wird (Dies geschieht, wenn CanExecuteChanged von einem Worker-Thread abgefeuert wird, der zum Beispiel die Schaltfläche Enabled Eigenschaft). Die Lösung ist natürlich, UI Dispatcher irgendwo im globalen Raum in Ihrer VM beim Start zu speichern und dann bei der Aktualisierung der Benutzeroberfläche zu verwenden.

52voto

juFo Punkte 16511

Wenn Sie auf dieses Problem stoßen und die UI Controls auf einer separater Mitarbeiter Thread bei der Arbeit mit BitmapSource o ImageSource in WPF, rufen Sie Freeze() vor der Übergabe der Methode BitmapSource o ImageSource als Parameter einer beliebigen Methode. Verwendung von Application.Current.Dispatcher.Invoke() funktioniert in solchen Fällen nicht

34voto

Basheer AL-MOMANI Punkte 13273

Das ist bei mir passiert, weil ich versucht habe access UI Bauteil in another thread insted of UI thread

wie diese

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

Um dieses Problem zu lösen, verpacken Sie jeden UI-Aufruf in was Candide oben in seiner Antwort erwähnte

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

22voto

VikramBose Punkte 283

Sie müssen dies im UI-Thread tun. Verwenden:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X