ObservableCollection, созданная в пуле потоков. Поток не фильтруется.

Приложение, над которым я работаю, основано на WPF и MVVMLightToolkit. Я знаю, что не предоставляю вам mcve, но все приложение действительно сложное, и трудно привести такой пример. Я надеюсь, что кто-то сможет помочь с полной картиной.

В этом приложении одно действие занимает много времени (инициализация), поэтому я запускаю его в задаче, чтобы не заморозить интерфейс:

public class MainViewModel : ViewModelBase
{
    public ICommand HeavyActionCommand {get; private set;}
    public MainViewModel()
    {
        this.HeavyActionCommand = new RelayCommand(this.HeavyAction);
    }
    private async void HeavyAction()
    {
        var subViewModel = new SubViewModel();
        await Task.Run(async () => await subViewModel.ActualHeavyAction());
    }
}

Если я не заверну ActualHeavyAction в Task.Run метод, пользовательский интерфейс заморозить. Делая это, насколько я понимаю, ActualHeavyAction запускается не в потоке пользовательского интерфейса, а в потоке пула потоков (исправьте меня, если я ошибаюсь).

Среди прочего ActualHeavyAction инициализирует ObservableCollection что мне нужно отфильтровать в отношении некоторых пользовательских входных данных (в следующем классе, свойство UserInput связан с TextBox). У меня было что-то вроде:

public class SubViewModel: ViewModelBase
{
    private _userInput;
    public string UserInput
    {
        get { return _userInput; }
        set
        {
            if (_userInput != value)
            {
                _userInput = value;
                this.RaisePropertyChanged();

                // Run the filter on the collection when the user enters new inputs
                CollectionViewSource.GetDefaultView(this.MyCollection).Refresh();
            }
        }
    }

    public ObservableCollection MyCollection {get; private set;}

    public async Task ActualHeavyAction()
    {
        /// lots of heavy stuff

        var myCollection = await _context.Objects.GetCollectionAsync();
        this.MyCollection = new ObservableCollection(myCollection);

        this.RaisePropertyChanged(nameof(this.MyCollection));

        CollectionViewSource.GetDefaultView(this.MyCollection).Filter = MyFilter;

        /// some other heavy stuff
    }

    public bool MyFilter(object obj)
    {
        // Blah blah blah
    }
}

До этого у меня нет никаких проблем. Проблема возникает позже, когда в потоке пользовательского интерфейса выполняется другое действие, изменяющее эту коллекцию. Я получаю рецидив:

Этот тип CollectionView не поддерживает изменения в его SourceCollection из потока, отличного от потока Dispatcher

Чтобы исправить это, я пытаюсь добавить.NET 4.5 EnableCollectionSynchronization особенность:

BindingOperations.EnableCollectionSynchronization(this.MyCollection, lockObject); // lockObject is a static new object() defined in the SubViewModel class

Я пытаюсь добавить это сразу после и непосредственно перед звонком: CollectionViewSource.GetDefaultView

При этом я не получаю исключение при изменении MyCollection, но зовет Refresh() на CollectionView не запускается MyFilter метод (точно такой же код работает на других ViewModels, которые не инициализируются в потоке пула потоков).

Ты хоть представляешь, что не так с моим кодом?

1 ответ

Решение

BindingOperations.EnableCollectionSynchronization метод должен быть вызван в потоке пользовательского интерфейса. Так что вам нужно создать ObservableCollection и вызовите этот метод в потоке пользовательского интерфейса, прежде чем пытаться получить доступ к коллекции из фонового потока.

Но единственный метод, который должен вызываться в фоновом потоке в вашем ActualHeavyAction() это GetCollectionAsync() метод.

Как только задача, вызывающая этот метод, будет завершена, вы можете создать ObservableCollection и примените фильтр обратно к потоку пользовательского интерфейса. Или только что вернул уже отфильтрованный список из задачи.

Фильтрация ICollectionView с использованием Filter Свойство является гибкой, но довольно медленной операцией, поэтому, если ваша исходная коллекция содержит много элементов, это может быть не лучшим вариантом для реализации фильтрации.

Другие вопросы по тегам