it-swarm.com.de

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

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

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

Was ist hier los?

296
Kuntady Nithesh

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

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

Sie können auch control.Dispatcher.CheckAccess() verwenden, um zu überprüfen, ob der aktuelle Thread das Steuerelement besitzt. Wenn es ihm gehört, sieht Ihr Code wie gewohnt aus. Ansonsten obiges Muster verwenden.

618
Candide

Eine weitere gute Verwendung für Dispatcher.Invoke dient zum sofortigen Aktualisieren der Benutzeroberfläche in einer Funktion, die andere Aufgaben ausführt:

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

Ich verwende dies, um den Schaltflächentext auf "Processing ..." zu aktualisieren und ihn zu deaktivieren, während ich WebClient Anfragen stelle.

46
computerGuyCJ

Um meine 2 Cent hinzuzufügen, kann die Ausnahme auch dann auftreten, wenn Sie Ihren Code über System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() aufrufen.
Der Punkt ist, dass Sie Invoke() des Dispatcher des Steuerelements aufrufen müssen, auf das Sie zugreifen möchten , was in manchen Fällen nicht mit System.Windows.Threading.Dispatcher.CurrentDispatcher identisch sein kann. Verwenden Sie stattdessen YourControl.Dispatcher.Invoke(), um sicher zu gehen. Ich habe ein paar Stunden lang mit dem Kopf geschlagen, bevor ich das merkte.

Aktualisieren

Für zukünftige Leser sieht es 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 die UI-Backing-Eigenschaften in Ihrer VM aktualisieren. Die WPF-Engine ruft Cross-Thread-Aufrufe auf dem richtigen UI-Thread ab. Sehen Sie mehr Details hier . Danke an @aaronburro für die Infos und den Link. Vielleicht möchten Sie auch unser Gespräch unten in Kommentaren lesen.

37
dotNET

Wenn dieses Problem auftritt und Benutzeroberflächen-Steuerelemente auf einem separater Worker -Thread erstellt wurden, während Sie mit BitmapSource oder ImageSource in WPF arbeiten, rufen Sie die Methode Freeze() auf zuerst, bevor Sie BitmapSource oder ImageSource als Parameter an eine Methode übergeben. Die Verwendung von Application.Current.Dispatcher.Invoke() funktioniert in solchen Fällen nicht

30
juFo

das ist mir passiert, weil ich versucht habe, access UI Komponente in another thread insted of UI thread

so was

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, wickeln Sie jeden ui-Aufruf in was Candide oben in seiner Antwort erwähnt hat

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

Aus irgendeinem Grund hat sich Candides Antwort nicht bewährt. Es war jedoch hilfreich, da es mich dazu brachte, dies zu finden, was perfekt funktionierte:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
    {
       //your code here...
    }));
14
Sarah

Sie müssen auf der Benutzeroberfläche aktualisieren, verwenden Sie also

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

Ich habe auch festgestellt, dass System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() nicht immer der Dispatcher der Zielsteuerung ist, so wie es dotNet in seiner Antwort schrieb. Ich hatte keinen Zugriff auf den Dispatcher der Steuerung, also benutzte ich Application.Current.Dispatcher und es löste das Problem.

3
Paulus Limma

Das Problem ist, dass Sie GetGridData von einem Hintergrundthread aufrufen. Diese Methode greift auf mehrere WPF-Steuerelemente zu, die an den Hauptthread gebunden sind. Jeder Versuch, von einem Hintergrund-Thread aus darauf zuzugreifen, führt zu diesem Fehler.

Um zum richtigen Thread zurückzukehren, sollten Sie SynchronizationContext.Current.Post. In diesem speziellen Fall scheint es jedoch so, als ob der Großteil Ihrer Arbeit auf der Benutzeroberfläche basiert. Daher würden Sie einen Hintergrund-Thread erstellen, nur um sofort zum UI-Thread zurückzukehren und etwas zu arbeiten. Sie müssen Ihren Code ein wenig überarbeiten, damit er die teure Arbeit am Hintergrundthread erledigt und anschließend die neuen Daten an den UI-Thread sendet

2
JaredPar

Das funktioniert bei mir.

new Thread(() =>
        {

        Thread.CurrentThread.IsBackground = false;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {

          //Your Code here.

        }, null);
        }).Start();
1
nPcomp

Wie erwähnt hier , Dispatcher.Invoke konnte die Benutzeroberfläche einfrieren. Sollte nutzen Dispatcher.BeginInvoke stattdessen.

Hier finden Sie eine praktische Erweiterungsklasse, die das Überprüfen und Aufrufen des Dispatcher-Aufrufs vereinfacht.

Anwendungsbeispiel: (Aufruf aus dem WPF-Fenster)

this Dispatcher.InvokeIfRequired(new Action(() =>
{
    logTextbox.AppendText(message);
    logTextbox.ScrollToEnd();
}));

Erweiterungsklasse:

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();
        }
    }
}
1
Jeson Martajaya

Ich erhielt den Fehler immer wieder, wenn ich meiner WPF-Anwendung kaskadierende Kombinationsfelder hinzufügte, und behebte den Fehler mithilfe der folgenden 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 );

    }

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, Version% 3Dv4.7); k (DevLang-csharp) & rd = true

0
user8128167

Eine andere Lösung besteht darin, sicherzustellen, dass Ihre Steuerelemente im UI-Thread erstellt werden, nicht beispielsweise von einem Hintergrund-Worker-Thread.

0
FindOutIslamNow