可观察集合/队列
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>.RemoveAt
为 O(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)));
}
我正在制作文件传输程序。用户可以添加任意数量的文件传输(均为 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>.RemoveAt
为 O(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)));
}