'ObservableCollection / Queue

I am making a file transfer program. The user can add as many file transfers (both download/upload), and those will go in FIFO order. The first choice would be to use a Queue<T>. The problem here is the fact that unlike ObservableCollection, it does not automatically reflect the changes in the UI. I have a ListView control which has the source set to what is currently the Queue<T>, and after doing Enqueue() the UI does not update.

Is my best/only option just to use the ObservableCollection like a Queue, such as removing the first item after a file transfer is complete?

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>


Solution 1:[1]

The complexity of Queue<T>.Dequeue is O(1) where Collection<T>.RemoveAt is O(n). This makes using a native Queue<T> the better option. You can extend Queue<T> to add collection changed notifications.
You can use the following implementation:

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)));
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 BionicCode