WPF DataGrid 和 IAsyncEnumerable 项目
WPF DataGrid and IAsyncEnumerable items
可能吗?
怎么样?
我有一个异步方法从我的数据库中获取数据行。
我的结果是 IAsyncEnumerable<TItem>
。
它应该是一种快速响应的完美数据类型 UI,但是 - 当我将它设置为我的网格的数据源时,它不起作用并且什么都不显示。
对此我有 2 个解决方法。首先显然是来自我的异步任务的 return 数组。第二个类似 - 我可以只对结果使用 ToArrayAsync()
(来自 System.Linq.Async
)。
我怀疑使用 ToArrayAsync()
比仅从任务中 returning 数组慢,所以它没有多大意义。
如果IAsyncEnumerable
类型可以直接用作控件的数据源,那将很有意义。但是我可能需要实现某种 IObservable
接口或类似的东西。有人试过吗?
整个事情都很新,我找不到任何例子,甚至找不到一篇文章说它是可能的。我的意思是 - 如果您不能将它用于 UI,那么 IAsyncEnumerable
有什么用?
更新:我正在寻找的是 IAsyncEnumerable<TItem>
和 ObservableCollection<TItem>
之间的转换。我找到了示例,但它们都完全按照我提到的解决方法工作 - 因此数据被放入数组中,然后添加到集合中。这会破坏所有 IAsyncEnumerable<T>
个好处。
引入 IAsyncEnumerable
以允许异步数据 generators/data 流。通常像 ItemsControl
这样显示数据项集合的控件是基于索引的。和 ICollectionView
基础。
通常控件通过原始集合的 ICollectionView
获取项目。由其在源集合 ICollectionView
中的索引标识的数据项被包装到 UIElement
容器中以进行呈现。
数据 stream/generator 不是基于索引的。 Enumerator
(调用 IEnumerable.GetEnumerator
的结果)用于通过调用 IEnumerator.MoveNext
从一个元素步进到另一个元素(foreach
是一种语言结构,为了方便封装了实际的枚举涉及 IEnumerator
).
的逻辑
除了异步数据流引入的潜在线程问题之外,基于索引的 ItemsControl
需要提前知道它的所有项目——这就是为什么你需要一个实现 INotifyCollectionChanged
的集合才能拥有 ItemsControl
更新其 Items
更改:视图被认为是被动的 - 它仅显示数据而不生成数据。
虽然不是强制性的,但 WPF 在设计时就考虑到了 MVVM。活动部分通常是模型或视图模型。数据生成通常发生在模型中。因此,将 IAsyncEnumerable
作为直接绑定源没有意义,因为这意味着被动 UI 元素必须直接 generate/consume 数据流。您可能希望这是视图模型/Binding.Source
的责任。同样,UI 旨在用于 ICollectionView
实现,而不是直接用于集合及其迭代器。
您可以做的是枚举 Binding.Source
中的 IAsyncEnumerable
并用生成的结果更新 ObservableCollection
:
public ObservableCollection<DataItem> ItemsSourceForDataGrid { get; }
private async Task GenerateDataItemsAsync(IAsyncEnumerable asyncDataStream)
{
// Keep the GUI responsive while consuming an e.g. CPU intensive generator
await foreach (DataItem item in asyncDataStream)
{
this.ItemsSourceForDataGrid.Add(item);
}
}
另请注意,ToListAsync
和 ToArrayAsync
等扩展方法可能会锁定应用程序。如果您不知道数据源,即生成器,您可能会在 ToListAsync
尝试完成数据流时锁定您的应用程序。但是根据定义,数据流可以是无限的,这将导致 ToListAsync
永远不会 return 或完成 returned Task
对象。如果您发现自己需要 ToArrayAsync
并且您是数据生成器的作者,那么您可能一开始就不应该使用 IAsyncEnumerable
。像 ToArrayAsync
这样的异步终结器违背了 IAsyncEnumerable
.
的想法
可能吗? 怎么样?
我有一个异步方法从我的数据库中获取数据行。
我的结果是 IAsyncEnumerable<TItem>
。
它应该是一种快速响应的完美数据类型 UI,但是 - 当我将它设置为我的网格的数据源时,它不起作用并且什么都不显示。
对此我有 2 个解决方法。首先显然是来自我的异步任务的 return 数组。第二个类似 - 我可以只对结果使用 ToArrayAsync()
(来自 System.Linq.Async
)。
我怀疑使用 ToArrayAsync()
比仅从任务中 returning 数组慢,所以它没有多大意义。
如果IAsyncEnumerable
类型可以直接用作控件的数据源,那将很有意义。但是我可能需要实现某种 IObservable
接口或类似的东西。有人试过吗?
整个事情都很新,我找不到任何例子,甚至找不到一篇文章说它是可能的。我的意思是 - 如果您不能将它用于 UI,那么 IAsyncEnumerable
有什么用?
更新:我正在寻找的是 IAsyncEnumerable<TItem>
和 ObservableCollection<TItem>
之间的转换。我找到了示例,但它们都完全按照我提到的解决方法工作 - 因此数据被放入数组中,然后添加到集合中。这会破坏所有 IAsyncEnumerable<T>
个好处。
IAsyncEnumerable
以允许异步数据 generators/data 流。通常像 ItemsControl
这样显示数据项集合的控件是基于索引的。和 ICollectionView
基础。
通常控件通过原始集合的 ICollectionView
获取项目。由其在源集合 ICollectionView
中的索引标识的数据项被包装到 UIElement
容器中以进行呈现。
数据 stream/generator 不是基于索引的。 Enumerator
(调用 IEnumerable.GetEnumerator
的结果)用于通过调用 IEnumerator.MoveNext
从一个元素步进到另一个元素(foreach
是一种语言结构,为了方便封装了实际的枚举涉及 IEnumerator
).
的逻辑
除了异步数据流引入的潜在线程问题之外,基于索引的 ItemsControl
需要提前知道它的所有项目——这就是为什么你需要一个实现 INotifyCollectionChanged
的集合才能拥有 ItemsControl
更新其 Items
更改:视图被认为是被动的 - 它仅显示数据而不生成数据。
虽然不是强制性的,但 WPF 在设计时就考虑到了 MVVM。活动部分通常是模型或视图模型。数据生成通常发生在模型中。因此,将 IAsyncEnumerable
作为直接绑定源没有意义,因为这意味着被动 UI 元素必须直接 generate/consume 数据流。您可能希望这是视图模型/Binding.Source
的责任。同样,UI 旨在用于 ICollectionView
实现,而不是直接用于集合及其迭代器。
您可以做的是枚举 Binding.Source
中的 IAsyncEnumerable
并用生成的结果更新 ObservableCollection
:
public ObservableCollection<DataItem> ItemsSourceForDataGrid { get; }
private async Task GenerateDataItemsAsync(IAsyncEnumerable asyncDataStream)
{
// Keep the GUI responsive while consuming an e.g. CPU intensive generator
await foreach (DataItem item in asyncDataStream)
{
this.ItemsSourceForDataGrid.Add(item);
}
}
另请注意,ToListAsync
和 ToArrayAsync
等扩展方法可能会锁定应用程序。如果您不知道数据源,即生成器,您可能会在 ToListAsync
尝试完成数据流时锁定您的应用程序。但是根据定义,数据流可以是无限的,这将导致 ToListAsync
永远不会 return 或完成 returned Task
对象。如果您发现自己需要 ToArrayAsync
并且您是数据生成器的作者,那么您可能一开始就不应该使用 IAsyncEnumerable
。像 ToArrayAsync
这样的异步终结器违背了 IAsyncEnumerable
.