El hilo que llama no puede acceder a este objeto porque lo posee un hilo diferente
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;
para obtener datos de la cuadrícula genera una excepción
El hilo que llama no puede acceder a este objeto porque lo posee un hilo diferente.
¿Qué pasa aquí?
Este es un problema común entre las personas que comienzan. Siempre que actualice los elementos de su interfaz de usuario desde un hilo que no sea el hilo principal, debe usar:
this.Dispatcher.Invoke(() =>
{
...// your code here.
});
También puede utilizar control.Dispatcher.CheckAccess()
para comprobar si el hilo actual posee el control. Si lo posee, su código se verá normal. De lo contrario, utilice el patrón anterior.
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 tienes que llamar Invoke()
al Dispatcher
control al que estás intentando acceder , que en algunos casos puede no ser el mismo System.Windows.Threading.Dispatcher.CurrentDispatcher
. Entonces, en lugar de eso, deberías usar YourControl.Dispatcher.Invoke()
para estar seguro. Estuve golpeándome la cabeza durante un par de horas antes de darme cuenta de esto.
Actualizar
Para futuros lectores, parece que esto ha cambiado en las versiones más recientes de .NET (4.0 y superiores). Ahora ya no tendrá que preocuparse por el despachador correcto al actualizar las propiedades de respaldo de la interfaz de usuario en su máquina virtual. El motor WPF reunirá las llamadas entre subprocesos en el subproceso de interfaz de usuario correcto. Ver más detalles aquí . Gracias a @aaronburro por la información y el enlace. Quizás también quieras leer nuestra conversación a continuación en los comentarios.
Actualización 2
Dado que esta es una publicación popular ahora, pensé en compartir mi experiencia que tuve en los años siguientes. El comportamiento parece ser que cualquier enlace de propiedad se actualizará correctamente en llamadas entre subprocesos (no se requiere clasificación; WPF lo manejará por usted). Las vinculaciones de comandos de OTOH deberán delegarse al despachador de UI. Lo he probado tanto con MVVM Light como con el Community Toolkit relativamente nuevo y parece ser el caso tanto con el Framework antiguo como con los nuevos .NET 5 y 6. AsyncRelayCommand
No se puede actualizar la interfaz de usuario cuando se invoca desde un subproceso que no es de UI (esto sucede cuando CanExecuteChanged
se activa desde un hilo de trabajo que actualiza, por ejemplo, la propiedad del botón Enabled
). Por supuesto, la solución es almacenar el despachador de UI en algún lugar del espacio global de su VM al iniciar y luego usarlo al actualizar la UI.
Si encuentra este problema y los controles de la interfaz de usuario se crearon en un subproceso de trabajo independiente cuando trabaja con BitmapSource
o ImageSource
en WPF, llame Freeze()
primero al método antes de pasar BitmapSource
o ImageSource
como parámetro a cualquier método. El uso Application.Current.Dispatcher.Invoke()
no funciona en tales casos.
Esto me pasó a mí porque traté de access UI
participaranother thread insted of UI thread
como 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, incluya cualquier llamada de interfaz de usuario 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 );
}
Debes hacerlo en el hilo de la interfaz de usuario. Usar:
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));