我有一个场景,我想在ItemsControl中呈现很多项目。由于项目的布局方式(使用画布),我无法使用标准的虚拟化面板,因此控件加载需要很长时间。
我不想一次性承担所有物品的装载时间,而是想知道如何批量装载物品?
因此,例如,如果我使用ListBox,并将其itemsSource设置为某个大型列表,我怎么能分批创建10个项目的ListBoxItems,推迟剩余的以在下一个Dispatcher事件上运行(背景或AppIdle)?
通过让ItemsSource成为随每个新批次增长的Observable集合,可以很容易地从ViewModel解决这个问题,但我想在View级别添加它。
我也不希望使用ItemContainers Visibility属性实现它,因为很可能已经在使用它。
答案 0 :(得分:0)
这一切都取决于数据的来源。但我可以告诉你从视图加载只是一个糟糕的计划。
我相信WPF现在允许对ObservableCollection进行跨线程更新。如果没有,您可以随时使用像Caliburn这样的库 BindableCollection 。
您也可以创建自己的this article。
代码:
/// <summary>
/// Initializes a new instance of the
/// <see cref="ObservableCollectionEx{T}"/> class.
/// </summary>
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
#region Constructors
/// <summary>
/// Initializes a new instance of the
/// <see cref="ObservableCollectionEx{T}" /> class.
/// </summary>
public ObservableCollectionEx()
{
}
///
/// Initializes a new instance of the
/// class.
///
///The collection.
public ObservableCollectionEx(IEnumerable<T> collection) : this()
{
this.AddRange(collection);
}
#endregion
#region Events
/// <summary>
/// Source: New Things I Learned
/// Title: Have worker thread update ObservableCollection that is bound to a ListCollectionView
/// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx
/// Note: Improved for clarity and the following of proper coding standards.
/// </summary>
/// <param name="e"></param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// Use BlockReentrancy
using (BlockReentrancy())
{
var eventHandler = CollectionChanged;
if (eventHandler == null) return;
// Only proceed if handler exists.
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk through invocation list.
foreach (var @delegate in delegates)
{
var handler = (NotifyCollectionChangedEventHandler)@delegate;
var currentDispatcher = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread.
if ((currentDispatcher != null) && (!currentDispatcher.CheckAccess()))
{
// Invoke handler in the target dispatcher's thread.
currentDispatcher.Dispatcher.Invoke(
DispatcherPriority.DataBind, handler, this, e);
}
else
{
// Execute as-is
handler(this, e);
}
}
}
}
/// <summary>
/// Overridden NotifyCollectionChangedEventHandler event.
/// </summary>
public override event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
}