El hilo que llama no puede acceder a este objeto porque lo posee un hilo diferente

Resuelto Kuntady Nithesh asked hace 12 años • 15 respuestas

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í?

Kuntady Nithesh avatar Mar 16 '12 13:03 Kuntady Nithesh
Aceptado

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.

Roland Mai avatar Mar 16 '2012 06:03 Roland Mai

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 Dispatchercontrol 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. AsyncRelayCommandNo se puede actualizar la interfaz de usuario cuando se invoca desde un subproceso que no es de UI (esto sucede cuando CanExecuteChangedse 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.

dotNET avatar Oct 21 '2015 04:10 dotNET

Si encuentra este problema y los controles de la interfaz de usuario se crearon en un subproceso de trabajo independiente cuando trabaja con BitmapSourceo ImageSourceen WPF, llame Freeze()primero al método antes de pasar BitmapSourceo ImageSourcecomo parámetro a cualquier método. El uso Application.Current.Dispatcher.Invoke()no funciona en tales casos.

juFo avatar Nov 25 '2015 12:11 juFo

Esto me pasó a mí porque traté de access UIparticiparanother 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 );
}
Basheer AL-MOMANI avatar Jun 27 '2016 07:06 Basheer AL-MOMANI

Debes hacerlo en el hilo de la interfaz de usuario. Usar:

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 
VikramBose avatar Mar 05 '2013 11:03 VikramBose