traves subprocesos seguras realizar para otro llamar llamadas formularios desde controles control como checkforillegalcrossthreadcalls acceder c# wpf multithreading backgroundworker

c# - subprocesos - El subproceso que llama no puede acceder a este objeto porque un subproceso diferente lo posee



dispatcher c# (13)

Mi código es el siguiente

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

El paso objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null; objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null; en obtener datos de cuadrícula arroja excepción

El subproceso que llama no puede acceder a este objeto porque otro subproceso lo posee.

¿Qué está mal aquí?


Además, otra solución es garantizar que sus controles se creen en el subproceso de la interfaz de usuario, no por ejemplo, en un subproceso de trabajo en segundo plano.


Como se mencionó here , Dispatcher.Invoke podría congelar la interfaz de usuario. Debería usar Dispatcher.BeginInvoke lugar.

Aquí hay una clase de extensión útil para simplificar la comprobación y la invocación del despachador de llamadas.

Ejemplo de uso: (llamar desde la ventana WPF)

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

Clase de extension:

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


El problema es que está llamando a GetGridData desde un subproceso en segundo plano. Este método accede a varios controles WPF que están vinculados al hilo principal. Cualquier intento de acceder a ellos desde un subproceso en segundo plano llevará a este error.

Para volver al hilo correcto, debe usar SynchronizationContext.Current.Post . Sin embargo, en este caso particular, parece que la mayoría del trabajo que está haciendo está basado en la interfaz de usuario. Por lo tanto, estaría creando un hilo de fondo para volver inmediatamente al hilo de la interfaz de usuario y hacer algún trabajo. Necesita refactorizar su código un poco para que pueda hacer el trabajo costoso en el hilo de fondo y luego publicar los nuevos datos en el hilo de la interfaz de usuario después


Este es un problema común con las personas que comienzan. Cada vez que actualice sus elementos de UI desde un subproceso que no sea el subproceso principal, debe utilizar:

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

También puede usar control.Dispatcher.CheckAccess() para verificar si el hilo actual posee el control. Si es de su propiedad, su código parece normal. De lo contrario, utilice el patrón anterior.


Esto funciona para mi

new Thread(() => { Thread.CurrentThread.IsBackground = false; Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate { //Your Code here. }, null); }).Start();


Necesita actualizar en la interfaz de usuario, así que use

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


Otro buen uso para Dispatcher.Invoke es para actualizar inmediatamente la interfaz de usuario en una función que realiza otras tareas:

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

Lo utilizo para actualizar el texto del botón " Procesando ... " y deshabilitarlo al realizar solicitudes de WebClient .


Para agregar mis 2 centavos, la excepción puede ocurrir incluso si llama a su código a través de System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() .
El punto es que tiene que llamar a Invoke() del Dispatcher del control al que intenta acceder , que en algunos casos puede no ser el mismo que System.Windows.Threading.Dispatcher.CurrentDispatcher . Entonces, en lugar de eso, debes usar YourControl.Dispatcher.Invoke() para estar seguro. Me golpeé la cabeza durante un par de horas antes de darme cuenta de esto.


Por alguna razón, la respuesta de Candide no se construyó. Sin embargo, fue útil, ya que me llevó a encontrar esto, que funcionó perfectamente:

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


Seguí recibiendo el error cuando agregué comboboxes en cascada a mi aplicación WPF y resolví el error usando esta 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 ); }

Para obtener más información, consulte 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


Si alguien intenta trabajar con BitmapSource en WPF e hilos y tiene este mismo mensaje: simplemente llame al método Freeze() antes de pasar un BitmapSource como parámetro de hilo.


También encontré que System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke() no siempre es despachador del control de destino, tal como escribió dotNet en su respuesta. No tuve acceso al controlador de control, así que utilicé Application.Current.Dispatcher y resolví el problema.


esto sucedió conmigo porque intenté access UI componente access UI en another thread insted of UI thread

Me gusta esto

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

para resolver este problema, envuelva cualquier llamada ui dentro de lo que Candide mencionó anteriormente en su respuesta

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