WPF ObservableCollection Thread Safety - PullRequest
       0

WPF ObservableCollection Thread Safety

0 голосов
/ 15 августа 2011

У меня есть настройка MVVM.

Моя модель периодически вызывает некоторую службу, а затем вызывает действие в ViewModel, которое затем обновляет некоторые переменные, доступные для просмотра.

ПеременнаяReadOnlyObservableCollection<T>, который имеет ObservableCollection<T>, который он прослушивает.

Проблема в том, что Модель вызывает обратный вызов из другого потока, и, таким образом, она не позволяет очистить ObservableCollection<T> вдругой поток.

Поэтому я подумал: используйте диспетчер, если мы не находимся в правильном потоке, вызовите его:

    private void OnNewItems(IEnumerable<Slot> newItems)
    {
        if(!Dispatcher.CurrentDispatcher.CheckAccess())
        {
            Dispatcher.CurrentDispatcher.Invoke(new Action(() => this.OnNewItems(newItems)));
            return;
        }

        this._internalQueue.Clear();
        foreach (Slot newItem in newItems)
        {
            this._internalQueue.Add(newItem);
        }
    }

Я думаю, код довольно прост.

Проблема в том, что, хотя я выполняю его в правильном потоке (я думаю), он все равно вызывает у меня исключение в .Clear();

Почему это происходит?Как я могу обойти это, не создавая свой пользовательский ObservableCollection<T>?

Ответы [ 2 ]

2 голосов
/ 15 августа 2011

Обычно я инициализирую диспетчер, используемый моими моделями представления, в базе моделей общего представления, чтобы гарантировать, что это диспетчер потоков пользовательского интерфейса, как упоминает Уилл.

#region ViewModelBase()
/// <summary>
/// Initializes a new instance of the <see cref="ViewModelBase"/> class.
/// </summary>
protected ViewModelBase()
{
    _dispatcher = Dispatcher.CurrentDispatcher;
}
#endregion

#region Dispatcher
/// <summary>
/// Gets the dispatcher used by this view model to execute actions on the thread it is associated with.
/// </summary>
/// <value>
/// The <see cref="System.Windows.Threading.Dispatcher"/> used by this view model to 
/// execute actions on the thread it is associated with. 
/// The default value is the <see cref="System.Windows.Threading.Dispatcher.CurrentDispatcher"/>.
/// </value>
protected Dispatcher Dispatcher
{
    get
    {
        return _dispatcher;
    }
}
private readonly Dispatcher _dispatcher;
#endregion

#region Execute(Action action)
/// <summary>
/// Executes the specified <paramref name="action"/> synchronously on the thread 
/// the <see cref="ViewModelBase"/> is associated with.
/// </summary>
/// <param name="action">The <see cref="Action"/> to execute.</param>
protected void Execute(Action action)
{
    if (this.Dispatcher.CheckAccess())
    {
        action.Invoke();
    }
    else
    {
        this.Dispatcher.Invoke(DispatcherPriority.DataBind, action);
    }
}
#endregion

Затем можно вызвать действие дляпосмотреть модель диспетчера вот так:

this.Execute(
    () =>
    {
        this.OnNewItems(newItems);
    }
);
0 голосов
/ 28 января 2013

Отличное решение этой проблемы, найденное в Codeproject- Многопоточная ObservableCollection и NotifyCollectionChanged Wrapper

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...