【问题标题】:defer visibility of ItemsControl items延迟 ItemsControl 项的可见性
【发布时间】:2014-12-21 12:39:39
【问题描述】:

我有一个场景,我想在 ItemsControl 中显示很多项目。由于项目的布局方式(使用画布),我无法使用标准的虚拟化面板,因此加载控件需要很长时间。

与其一次性加载所有项目,我想知道如何批量加载项目?

例如,如果我使用 ListBox,它的 itemsSource 设置为某个大列表,我如何以 10 个项目为一组创建 ListBoxItems,将剩余的项目推迟到下一个 Dispatcher 事件(Background 或 AppIdle )?

这个问题可以很容易地从 ViewModel 解决,方法是让 ItemsSource 成为随每个新批次增长的 Observable 集合,但我想在 View 级别添加它。

我也不希望它使用 ItemContainers Visibility 属性来实现,因为它很可能已经被使用了。

【问题讨论】:

    标签: wpf visibility itemscontrol deferred


    【解决方案1】:

    这完全取决于数据的来源。但我可以告诉你,从视图加载只是一个糟糕的计划。

    1. 在 ViewModel 中将 ItemsSource 设置为 ObservableCollection。
    2. 从后台线程更新 ObservableCollection,使其不会影响您的 UI。
    3. 确保在更新时不要重新实例化实例,而是清除并重新填充您的集合。

    我相信 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) &amp;&amp; (!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
    }
    

    【讨论】:

    • 正如我在问题中提到的,我知道我可以通过 ViewModel 轻松完成此操作。
    猜你喜欢
    • 1970-01-01
    • 2011-11-27
    • 2015-08-31
    • 1970-01-01
    • 1970-01-01
    • 2014-06-29
    • 2016-06-17
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多