Изменение Datagrid.Columns из любого потока в WPF
(Я использую.Net 4.5 с Visual Studio 2017. Минимальная примерная ссылка была добавлена в конце статьи и готова к запуску / аварийному завершению без каких-либо действий, если вы сами захотите увидеть все)
У меня есть UserControl, который содержит DataGrid в WPF. Он связан с Viewmodel, который содержит DataTable И пользовательский список столбцов, чтобы скрыть некоторые из них, когда я не могу, например, удалить их прямо сейчас.
DataGrid.Columns доступна только для чтения, поэтому в UserControl.DataContextChanged (я объясню ниже, почему) я получаю новый DataContext, очищаю свою коллекцию grid.Columns и добавляю в нее пользовательский список DataGridColums, которые я прочитал во время загрузки.
Первоначально я делал синхронизирующую загрузку. Все работало нормально (я загружаю много вещей, включая список этих предметов). Но я поместил загрузку в фоновый рабочий (который работал почти нормально), а затем заменил фоновый рабочий асинхронной загрузкой с помощью Task.Run().
Все остальные списки элементов в моем окне хорошо загружены, код работает нормально. НО в тот момент, когда я пытаюсь заменить my grid.Columns другими, у меня есть InvalidOperationException, в котором говорится, что вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им.
Я пробовал много вещей, много BeginInvoke из myGrid.Dispatcher, текущий Диспетчер приложения и так далее, и каждый фрагмент асинхронного кода, который я нашел, но я не могу понять, как добавить простой элемент в эту коллекцию ObservableCollection.
Я видел пользовательские asyncObservableCollection, но не могу их использовать, DataGrid.Columns только для чтения.
Я озадачен тем фактом, что я думал, что UserControl_DataContextChanged принадлежит потоку пользовательского интерфейса, поэтому он должен иметь возможность безопасно изменять пользовательские элементы управления.
Я обновил "минимальный пример" здесь => https://files.fm/u/k2srba6m. Проблема в ItemViewmodel.FilterColumns, как и в моем исходном коде (надеюсь, что решение этой проблемы будет работать и в исходном коде)
Любая помощь будет оценена (и извините за мой английский)
2 ответа
Я нашел ответ благодаря помощи на другом форуме. Я оставляю вопрос здесь, потому что ответа было трудно найти.
При добавлении в DataGrid.Columns DataGridColumn, созданный вручную в viewmodel, был неисправен, сам экземпляр должен был быть вызван в потоке пользовательского интерфейса. OC.Add выдал исключение, но в основном из-за аргумента (и в некоторых случаях сам вызов, да).
Извините, опять же, никаких цитат, полных кода, просто "беллетристический роман", но это может помочь.
Если вы используете .Net 4.5 или новее, вы можете попробовать использовать BindingOperations.EnableCollectionSynchronization
,
В вашем окне конструктор:
// Sync collection with UI
BindingOperations.CollectionRegistering += BindingOperations_CollectionRegistering;
И добавьте этот метод:
/// <summary>
/// Handles the CollectionRegistering event of the BindingOperations control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="CollectionRegisteringEventArgs"/> instance containing the event data.</param>
private void BindingOperations_CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
BindingOperations.EnableCollectionSynchronization(e.Collection, e.Collection);
}
Это будет автоматически вызывать метод EnableCollectionSynchronization каждый раз, когда создается ObservableCollection.
Я настроил его так, чтобы просто использовать саму коллекцию в качестве lockObject, не знаю, плохая ли это идея, но это сработало с моей стороны.
Если вы используете .Net 4.0, вам, возможно, придется заставить работать AsyncObservableCollection. Я не нашел другого способа сделать это в.Net 4.0.