Мой код как ниже
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);
}
}
Шаг objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
в получении данных сетки вызывает исключение
Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им.
Что здесь не так?
c#
wpf
multithreading
backgroundworker
Кунтады Нитеш
источник
источник
Ответы:
Это общая проблема с людьми, начинающими. Всякий раз, когда вы обновляете свои элементы пользовательского интерфейса из потока, отличного от основного потока, вам необходимо использовать:
Вы также можете использовать,
control.Dispatcher.CheckAccess()
чтобы проверить, принадлежит ли текущий поток элемент управления. Если он владеет им, ваш код выглядит как обычно. В противном случае используйте вышеуказанный шаблон.источник
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);
получить диспетчер, если не в потоке пользовательского интерфейса согласно этому ответуthis.Dispatcher.Invoke
.... вместо этого ...myControl.Dispatcher.Invoke
:) Мне нужно было вернуть объект обратно, поэтому я сделалmyControlDispatcher.Invoke<object>(() => myControl.DataContext)
;Еще одно хорошее использование для
Dispatcher.Invoke
немедленного обновления пользовательского интерфейса в функции, которая выполняет другие задачи:Я использую это, чтобы обновить текст кнопки на « Обработка ... » и отключить ее при выполнении
WebClient
запросов.источник
Чтобы добавить мои 2 цента, может возникнуть исключение, даже если вы вызываете свой код до конца
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.Дело в том , что вы должны вызвать
Invoke()
изDispatcher
-за контроля , который вы пытаетесь получить доступ , которые в некоторых случаях не может быть такой же , какSystem.Windows.Threading.Dispatcher.CurrentDispatcher
. Так что вместо этого вы должны использовать,YourControl.Dispatcher.Invoke()
чтобы быть в безопасности. Я несколько часов колотил головой, прежде чем понял это.Обновить
Для будущих читателей, похоже, что это изменилось в более новых версиях .NET (4.0 и выше). Теперь вам больше не нужно беспокоиться о правильном диспетчере при обновлении свойств поддержки пользовательского интерфейса в вашей виртуальной машине. Движок WPF будет маршалировать вызовы между потоками в правильном потоке пользовательского интерфейса. Смотрите подробности здесь . Спасибо @aaronburro за информацию и ссылку. Вы также можете прочитать наш разговор ниже в комментариях.
источник
Dispatcher
. В тех случаях (которые по общему признанию редки), вызовControl.Dispatcher
- безопасный подход. Для справки вы можете увидеть эту статью, а также этот пост (в частности, ответ Squidward).Если вы столкнулись с этой проблемой и элементы управления пользовательским интерфейсом были созданы в отдельном рабочем потоке при работе с
BitmapSource
илиImageSource
в WPF,Freeze()
сначала вызовите метод перед передачейBitmapSource
илиImageSource
в качестве параметра любому методу. ИспользованиеApplication.Current.Dispatcher.Invoke()
не работает в таких случаяхисточник
это случилось со мной , потому что я пытался
access UI
в компонентеanother thread insted of UI thread
как это
чтобы решить эту проблему, оберните любой вызов пользовательского интерфейса в то, что Кандид упомянул выше в своем ответе
источник
По какой-то причине ответ Кэндиды не получил. Это было полезно, однако, поскольку это привело меня к поиску этого, который работал отлично:
источник
System.Windows.Threading.Dispatcher.CurrentDispatcher
является диспетчером для текущего потока . Это означает, что если вы находитесь в фоновом потоке, он не будет диспетчером потока пользовательского интерфейса. Чтобы получить доступ к диспетчеру потока пользовательского интерфейса, используйтеSystem.Windows.Application.Current.Dispatcher
.Вам необходимо обновить интерфейс пользователя, поэтому используйте
источник
Это работает для меня.
источник
Я также обнаружил, что
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
не всегда диспетчер контроля цели, как писал dotNet в своем ответе. У меня не было доступа к собственному диспетчеру управления, поэтому я использовал,Application.Current.Dispatcher
и это решило проблему.источник
Проблема в том, что вы звоните
GetGridData
из фонового потока. Этот метод обращается к нескольким элементам управления WPF, которые связаны с основным потоком. Любая попытка доступа к ним из фонового потока приведет к этой ошибке.Чтобы вернуться к правильной теме, вы должны использовать
SynchronizationContext.Current.Post
. Однако в данном конкретном случае кажется, что большая часть работы, которую вы выполняете, основана на пользовательском интерфейсе. Следовательно, вы будете создавать фоновый поток, чтобы сразу вернуться к потоку пользовательского интерфейса и выполнить некоторую работу. Вам нужно немного реорганизовать свой код, чтобы он мог выполнять дорогостоящую работу в фоновом потоке, а затем публиковать новые данные в потоке пользовательского интерфейса.источник
Как уже упоминалось здесь ,
Dispatcher.Invoke
может заморозить UI. Следует использоватьDispatcher.BeginInvoke
вместо.Вот удобный класс расширения для упрощения проверки и вызова диспетчера вызовов.
Пример использования: (вызов из окна WPF)
Класс расширения:
источник
Кроме того, другое решение заключается в том, что ваши элементы управления создаются в потоке пользовательского интерфейса, а не, например, в фоновом рабочем потоке.
источник
Я продолжал получать сообщение об ошибке, когда я добавил каскадные списки в свое приложение WPF, и исправил ошибку с помощью этого API:
Для получения дополнительной информации см. 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); к (DevLang-CSharp) & й = TRUE
источник