Meu código é como abaixo
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);
}
}
A etapa objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
em obter dados da grade gera exceção
O encadeamento de chamada não pode acessar esse objeto porque ele possui um encadeamento diferente.
O que há de errado aqui?
c#
wpf
multithreading
backgroundworker
Kuntady Nithesh
fonte
fonte
Respostas:
Esse é um problema comum nas pessoas que estão começando. Sempre que você atualiza seus elementos da interface do usuário a partir de um thread diferente do thread principal, é necessário usar:
Você também pode usar
control.Dispatcher.CheckAccess()
para verificar se o segmento atual possui o controle. Se possuir, seu código parecerá normal. Caso contrário, use o padrão acima.fonte
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);
para obter o despachante se não no segmento interface do usuário de acordo com esta respostathis.Dispatcher.Invoke
.... em vez disso ...myControl.Dispatcher.Invoke
:) Eu precisava retornar um objeto de volta, então o fizmyControlDispatcher.Invoke<object>(() => myControl.DataContext)
;Outro bom uso
Dispatcher.Invoke
é para atualizar imediatamente a interface do usuário em uma função que executa outras tarefas:Eu uso isso para atualizar o texto do botão para " Processando ... " e desativá-lo ao fazer
WebClient
solicitações.fonte
Para adicionar meus 2 centavos, a exceção pode ocorrer mesmo se você chamar seu código
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.O ponto é que você tem que chamar
Invoke()
oDispatcher
do controle que você está tentando acessar , o que em alguns casos pode não ser o mesmo queSystem.Windows.Threading.Dispatcher.CurrentDispatcher
. Então, em vez disso, você deve usarYourControl.Dispatcher.Invoke()
para estar seguro. Eu estava batendo minha cabeça por algumas horas antes de perceber isso.Atualizar
Para futuros leitores, parece que isso mudou nas versões mais recentes do .NET (4.0 e superior). Agora você não precisa mais se preocupar com o expedidor correto ao atualizar as propriedades de backup da interface do usuário na sua VM. O mecanismo WPF empacotará as chamadas de thread cruzado no thread de interface do usuário correto. Veja mais detalhes aqui . Obrigado a @aaronburro pela informação e link. Você também pode ler nossa conversa abaixo nos comentários.
fonte
Dispatcher
. Nesses casos (que são reconhecidamente raros), chamarControl.Dispatcher
é a abordagem segura. Para referência, você pode ver este artigo e também esta postagem do SO (principalmente a resposta do Lula Molusco).Se você encontrar esse problema e os Controles da UI foram criados em um thread de trabalho separado ao trabalhar com
BitmapSource
ouImageSource
no WPF, chame oFreeze()
método primeiro antes de passar oBitmapSource
ouImageSource
como parâmetro para qualquer método. O usoApplication.Current.Dispatcher.Invoke()
não funciona nesses casosfonte
isso aconteceu comigo porque eu tentei
access UI
no componenteanother thread insted of UI thread
como isso
Para resolver esse problema, envolva qualquer chamada de interface do usuário dentro do que Candide mencionou acima em sua resposta
fonte
Por alguma razão, a resposta de Candide não foi boa. Foi útil, porém, pois me levou a encontrar isso, que funcionou perfeitamente:
fonte
System.Windows.Threading.Dispatcher.CurrentDispatcher
é o expedidor para o encadeamento atual . Isso significa que, se você estiver em um encadeamento em segundo plano, não será o expedidor do encadeamento da interface do usuário. Para acessar o distribuidor da thread da interface do usuário, useSystem.Windows.Application.Current.Dispatcher
.Você precisa atualizar para a interface do usuário, então use
fonte
Isso funciona para mim.
fonte
Também descobri que
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
nem sempre é despachante do controle de destino, assim como o dotNet escreveu em sua resposta. Como não tinha acesso ao despachante do controle, useiApplication.Current.Dispatcher
e resolvi o problema.fonte
O problema é que você está chamando
GetGridData
de um thread em segundo plano. Este método acessa vários controles WPF que estão vinculados ao thread principal. Qualquer tentativa de acessá-los a partir de um encadeamento em segundo plano levará a esse erro.Para voltar ao segmento correto, você deve usar
SynchronizationContext.Current.Post
. No entanto, nesse caso em particular, parece que a maioria do trabalho que você está fazendo é baseada na interface do usuário. Portanto, você criaria um encadeamento em segundo plano apenas para retornar imediatamente ao encadeamento da interface do usuário e fazer algum trabalho. Você precisa refatorar um pouco o código para que ele possa fazer o trabalho caro no encadeamento em segundo plano e depois postar os novos dados no encadeamento da interface do usuário posteriormentefonte
Como mencionado aqui ,
Dispatcher.Invoke
pode congelar a interface do usuário. Deve usar em seuDispatcher.BeginInvoke
lugar.Aqui está uma classe de extensão útil para simplificar a chamada de verificação e chamada do expedidor.
Exemplo de uso: (chamada da janela WPF)
Classe de extensão:
fonte
Além disso, outra solução é garantir que seus controles sejam criados no thread da interface do usuário, não por um thread de trabalho em segundo plano, por exemplo.
fonte
Continuei recebendo o erro ao adicionar caixas de combinação em cascata ao meu aplicativo WPF e resolvi o erro usando esta API:
Para obter detalhes, 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
fonte