Dispatcher.Invoke внутренне вызывает CheckAccess? - PullRequest
0 голосов
/ 04 января 2019

У меня есть настраиваемая одновременная наблюдаемая коллекция, которую я использую как ItemsSource в настольном приложении WPF.

Чтобы коллекция была "наблюдаемой", я реализовал INotifyCollectionChanged.Поскольку он «параллельный», то есть может быть изменен из нескольких потоков, я вызываю событие CollectionChanged, используя System.Windows.Threading.Dispatcher (как предложено в документах).

Поскольку я хочу, чтобы элементы пользовательского интерфейса былиобновленный в реальном времени, например, пересортировать список при изменении свойства (он же «лайв шейпинг»), я также реализовал ICollectionViewFactory для создания требуемого представления с его настройками, например, SortDescriptions.

Рассмотримследующий поток кода - все в потоке пользовательского интерфейса / диспетчера:

  • Я создаю коллекцию.
  • Я добавляю элементы и вызываю соответствующие события CollectionChanged.
  • Я загружаю Window с ListBox и связываю его с коллекцией.

У меня есть три версии функции, которая вызывается всякий раз, когда изменяется внутренний список (моей пользовательской коллекции):

Версия 1 CheckAccess и InvokeAsync)

    private void _notify(NotifyCollectionChangedEventArgs args)
    {
        if (_dispatcher.CheckAccess())
        {
            CollectionChanged?.Invoke(this, args);
        }
        else
        {
            _dispatcher.InvokeAsync(() => CollectionChanged?.Invoke(this, args));
        }
    }

Версия 2 (без CheckAccess и InvokeAsync)

    private void _notify(NotifyCollectionChangedEventArgs args)
    {
        _dispatcher.InvokeAsync(() => CollectionChanged?.Invoke(this, args));
    }

Версия 3 (без CheckAccess и Invoke)

    private void _notify(NotifyCollectionChangedEventArgs args)
    {
        _dispatcher.Invoke(() => CollectionChanged?.Invoke(this, args));
    }

Версии 1 и 3 работают нормально, но в Version 2 все элементы отображаются дважды в «Списке».

Это выглядит примерно так:

  • Если я нахожусь в потоке пользовательского интерфейса и звоню Dispatcher.InvokeAsync, вызов добавляется в «конец сообщения пользовательского интерфейса».pump "- без потока, ожидающего результата.
  • Элемент пользовательского интерфейса привязывает себя к коллекции, создает представление и заполняет его внутренний источник добавленными элементами.
  • " Позже ", затемКогда насос обрабатывается далее, отправленные события вызываются и прослушиваются, и CollectionView добавляет элементы к его источнику, создавая дублированные записи.

И я (думаю, я) понимаючто в версии 1 события запускаются (и ожидаются) до того, как элемент UI существует, поэтому нет никаких проблем, касающихся CollectionView.

Но почему / как делает версию 3 (с Invoke) работаешь?То, как код ведет себя по-другому, чем при использовании InvokeAsync, заставляет меня думать, что он должен заблокироваться, потому что он ожидает вызова, который должен быть обработан «дальше по своей собственной рассылке сообщений», но, очевидно, это не так.Invoke внутренне делает что-то вроде CheckAccess?

1 Ответ

0 голосов
/ 04 января 2019

Внутренний ли Dispatcher.Invoke вызывает CheckAccess?

Да, можно узнать подробности здесь

public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
{
   if(callback == null)
   {
      throw new ArgumentNullException("callback");
   }
   ValidatePriority(priority, "priority");

   if( timeout.TotalMilliseconds < 0 &&
       timeout != TimeSpan.FromMilliseconds(-1))
   {
      throw new ArgumentOutOfRangeException("timeout");
   }

   // Fast-Path: if on the same thread, and invoking at Send priority,
   // and the cancellation token is not already canceled, then just
   // call the callback directly.
   if(!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
...