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;
en obtener la cuadrícula de datos arroja una excepción
El hilo de llamada no puede acceder a este objeto porque lo posee un hilo diferente.
¿Qué pasa aquí?
c#
wpf
multithreading
backgroundworker
Kuntady Nithesh
fuente
fuente
Respuestas:
Este es un problema común con las personas que comienzan. Cada vez que actualice sus elementos de la interfaz de usuario desde un hilo que no sea el hilo principal, debe usar:
También puede usar
control.Dispatcher.CheckAccess()
para verificar si el hilo actual posee el control. Si lo posee, su código se ve normal. De lo contrario, use el patrón anterior.fuente
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);
para obtener el despachador si no está en el hilo de la interfaz de usuario según esta respuestathis.Dispatcher.Invoke
... en cambio ...myControl.Dispatcher.Invoke
:) Necesitaba devolver un objeto, así que lo hicemyControlDispatcher.Invoke<object>(() => myControl.DataContext)
;Otro buen uso para
Dispatcher.Invoke
es actualizar inmediatamente la interfaz de usuario en una función que realiza otras tareas:Lo uso para actualizar el texto del botón a " Procesando ... " y deshabilitarlo al hacer
WebClient
solicitudes.fuente
Para agregar mis 2 centavos, la excepción puede ocurrir incluso si llama a su código
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.El punto es que usted tiene que llamar
Invoke()
de laDispatcher
del control que estás intentando acceder , lo que en algunos casos puede no ser el mismo queSystem.Windows.Threading.Dispatcher.CurrentDispatcher
. Por lo tanto, debería usarYourControl.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 superior). Ahora ya no tiene que preocuparse por el despachador correcto al actualizar las propiedades de respaldo de la interfaz de usuario en su VM. El motor WPF organizará las llamadas de subprocesos cruzados en el subproceso de interfaz de usuario correcto. Ver más detalles aquí . Gracias a @aaronburro por la información y el enlace. También puede leer nuestra conversación a continuación en los comentarios.
fuente
Dispatcher
. En esos casos (que son ciertamente raros), llamarControl.Dispatcher
es el enfoque seguro. Como referencia, puede ver este artículo , así como esta publicación SO (particularmente la respuesta de Calamardo).Si encuentra este problema y los controles de la IU se crearon en un subproceso de trabajo independiente al trabajar con
BitmapSource
oImageSource
en WPF, llameFreeze()
primero al método antes de pasar elBitmapSource
oImageSource
como parámetro a cualquier método. UsarApplication.Current.Dispatcher.Invoke()
no funciona en tales casosfuente
esto sucedió conmigo porque intenté
access UI
componer enanother thread insted of UI thread
Me gusta esto
para resolver este problema, envuelva cualquier llamada de interfaz de usuario dentro de lo que Candide mencionó anteriormente en su respuesta
fuente
Por alguna razón, la respuesta de Candide no se desarrolló. Sin embargo, fue útil, ya que me llevó a encontrar esto, que funcionó perfectamente:
fuente
System.Windows.Threading.Dispatcher.CurrentDispatcher
es el despachador para el hilo actual . Eso significa que si está en un subproceso en segundo plano, no será el despachador del subproceso de interfaz de usuario. Para acceder al despachador del hilo de la IU, useSystem.Windows.Application.Current.Dispatcher
.Necesita actualizar a la interfaz de usuario, así que use
fuente
Esto funciona para mi.
fuente
También descubrí que
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
no siempre se despacha el control de destino, tal como escribió dotNet en su respuesta. No tenía acceso al propio despachador de control, así que lo uséApplication.Current.Dispatcher
y resolvió el problema.fuente
El problema es que está llamando
GetGridData
desde un hilo de fondo. Este método accede a varios controles WPF que están vinculados al hilo principal. Cualquier intento de acceder a ellos desde un hilo de fondo dará lugar a este error.Para volver al hilo correcto que 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 IU. Por lo tanto, crearía un subproceso en segundo plano solo para volver inmediatamente al subproceso de la interfaz de usuario y hacer algo de trabajo. Debe refactorizar un poco su código para que pueda hacer el trabajo costoso en el subproceso en segundo plano y luego publicar los nuevos datos en el subproceso de la interfaz de usuariofuente
Como se mencionó aquí ,
Dispatcher.Invoke
podría congelar la interfaz de usuario. Debería usarDispatcher.BeginInvoke
en su lugar.Aquí hay una práctica clase de extensión para simplificar la invocación del despachador de cheques y llamadas.
Ejemplo de uso: (llamada desde la ventana de WPF)
Clase de extensión:
fuente
Además, otra solución es garantizar que sus controles se creen en un subproceso de interfaz de usuario, no por un subproceso de trabajo en segundo plano, por ejemplo.
fuente
Seguí recibiendo el error cuando agregué cuadros combinados en cascada a mi aplicación WPF y resolví el error usando esta API:
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,VersFramework,VersFramework,Vers % 3Dv4.7); k (DevLang-csharp) & rd = true
fuente