可观察集合/队列

ObservableCollection / Queue

我正在制作文件传输程序。用户可以添加任意数量的文件传输(均为 download/upload),这些文件传输将按 FIFO 顺序进行。第一个选择是使用 Queue<T>。这里的问题是,与 ObservableCollection 不同,它不会自动反映 UI 中的变化。我有一个 ListView 控件,它的源设置为当前 Queue<T>,并且在执行 Enqueue() 之后 UI 没有更新。

我的 best/only 选项是否只是像 Queue 一样使用 ObservableCollection,例如在文件传输完成后删除第一项?

public async void ExecuteRequestDownloadCommand(object commandParameter)
{
    if (!(commandParameter is ModularPacket packet))
    {
        return;
    }

    foreach (FileTransferModel transfer in this.ModularPacketManager.DownloadRequest(packet))
    {
        FileTransferQueue.Enqueue(transfer);
    }
}

<ListView x:Name="FileTransferListView" ItemsSource= "{Binding FileTransferQueue}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="Auto"/>
            <GridViewColumn Header="Type" DisplayMemberBinding="{Binding Type}" Width="Auto"/>
            <GridViewColumn Header="Size" DisplayMemberBinding="{Binding Size}" Width="Auto"/>
            <GridViewColumn Header="Progress" DisplayMemberBinding="{Binding Progress}" Width="Auto"/>
        </GridView>
    </ListView.View>
</ListView>

Queue<T>.Dequeue 的复杂度为 O(1),其中 Collection<T>.RemoveAtO(n)。这使得使用原生 Queue<T> 成为更好的选择。您可以扩展 Queue<T> 以添加集合更改通知。
您可以使用以下实现:

public class ObservableQueue<TItem> : Queue<TItem>, INotifyCollectionChanged, INotifyPropertyChanged
{
  public event PropertyChangedEventHandler? PropertyChanged;
  public event NotifyCollectionChangedEventHandler? CollectionChanged;

  new public void Enqueue(TItem item)
  {
    base.Enqueue(item);
    OnPropertyChanged();
    OnCollectionChanged(NotifyCollectionChangedAction.Add, item, this.Count - 1);
  }

  new public TItem Dequeue()
  {
    TItem removedItem = base.Dequeue();
    OnPropertyChanged();
    OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, 0);
    return removedItem;
  }

  new public bool TryDequeue(out TItem? result)
  {
    if (base.TryDequeue(out result))
    {
      OnPropertyChanged();
      OnCollectionChanged(NotifyCollectionChangedAction.Remove, result, 0);
      return true;
    }
    return false;
  }

  new public void Clear()
  {
    base.Clear();
    OnPropertyChanged();
    OnCollectionChangedReset();
  }

  private void OnCollectionChanged(NotifyCollectionChangedAction action, TItem item, int index)
    => this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action, item, index));

  private void OnCollectionChangedReset()
    => this.CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

  private void OnPropertyChanged() => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Count)));
}