【问题标题】:Lazy loading of non-visible elements延迟加载不可见元素
【发布时间】:2011-04-08 18:53:37
【问题描述】:

我有一个案例,我有一个gridview/listbox/任何类型的项目控件,并且绑定到该控件的项目数量很大(很容易大约 5000+ 标记)。

这些项目中的每一个都需要具有从各种 Web 服务加载的各种属性。显然,使用 Web 服务一次性处理这么多的元素是不可能的。

我的问题是,是否可以推迟加载,直到这些项目实际显示给用户?例如,用户向下滚动,尽管项目一直存在于集合中,但只有在实际物理渲染时才会处理它们。

我以前看过它,但我不记得具体在哪里。在这种情况下,很多股票报价都在绑定到网格视图的集合中,但它们的属性(价格等)在第一次显示之前是空的(通过滚动到它们各自的位置)。

希望这是有道理的。

关于如何实现它的任何想法?

【问题讨论】:

    标签: c# wpf xaml silverlight visibility


    【解决方案1】:

    我会尝试结合使用延迟加载和异步加载:
    使用虚拟化列表控件。为您的项目创建一个 ViewModel 并用 ViewModel 的实例填充您的列表(每行一个)。

    在您的 ViewModel 中,创建具有默认值的属性,该默认值向用户显示数据尚未加载。第一次访问这些属性中的一个时,触发异步加载数据并在收到真实数据时触发INotifyPropertyChanged

    这将为用户提供良好的体验,并且大部分棘手的工作将通过虚拟化列表完成(在 WPF 中,这是ListBoxListViewDataGrid...)。希望这会有所帮助。

    class LineItemVM : INotifyPropertyChanged{
    
      bool   m_loadingTriggered;
      string m_name="Loading...";
      string m_anotherProperty="Loading...";
    
    
      public string Name{
         get{
           TriggerLoadIfNecessary(); // Checks if data must be loaded
           return m_name;
         }
      }
    
      public string AnotherProperty{
         get{
           TriggerLoadIfNecessary(); // Checks if data must be loaded
           return m_anotherProperty;
         }
      }
    
    
      void TriggerLoadIfNecessary(){        
         if(!m_loadingTriggered){
           m_loadingTriggered=true;
    
           // This block will called before your item will be displayed
           //  Due to the m_loadingTriggered-member it is called only once.
           // Start here the asynchronous loading of the data
           // In virtualizing lists, this block is only called if the item
           //  will be visible to the user (he scrolls to this item)
    
           LoadAsync();
         }
      }
    
      ...
    

    附加逻辑 作为一个想法,您还可以创建一个外部异步加载线程,该线程在后台加载所有数据,但有一个应该以更高优先级加载的项目列表。这个概念和上面的例子是一样的,但是TriggerLoadIfNecessary-方法不是从你的ViewModel-item加载数据,而是只在高优先级列表中添加这个项目,以便首先加载潜在的可见元素。哪个版本更适合的问题取决于列表的使用。如果用户可能使用完整列表并且没有快速导航离开,则此扩展版本更好。否则原版可能会更好。

    【讨论】:

    • 我明白你在说什么。这是处理服务调用和所有事情的好方法。但是,我对如何处理第一次呈现元素的事件很感兴趣。换句话说,我想知道如何处理正在显示的元素。我以前看过它……但我不确定。有什么活动吗?
    • 不确定这是否是您的意思,但是通过上述在虚拟化列表(DataBound)中使用 ViewModel-items 的解决方案,您的 line-items 的属性只会在用户滚动时调用到相应的行。这意味着我的示例中的 TriggerLoadIfNecessary 方法仅在显示该行之前被调用。对于不在视图中的所有行,将不会调用任何属性,因此不会触发数据加载。这就是为什么我在我的帖子中写道“大部分棘手的工作将通过虚拟化列表完成”。我已经扩展了我的例子......
    • 我现在明白了,谢谢。另一个不错的解决方案是使用 Loaded 事件处理程序,正如我稍后发现的那样。
    【解决方案2】:

    当用户滚动到数据的最后一屏时,这是一个通知的事件:

    using System.Windows;
    using System.Windows.Controls;
    
    public static class ScrollViewer
    {
        public static readonly RoutedEvent LastPageEvent = EventManager.RegisterRoutedEvent(
            "LastPage",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler),
            typeof(ScrollViewer));
    
        private static readonly RoutedEventArgs EventArgs = new RoutedEventArgs(LastPageEvent);
    
        static ScrollViewer()
        {
            EventManager.RegisterClassHandler(
                typeof(System.Windows.Controls.ScrollViewer),
                System.Windows.Controls.ScrollViewer.ScrollChangedEvent,
                new ScrollChangedEventHandler(OnScrollChanged));
        }
        public static void AddLastPageHandler(UIElement e, RoutedEventHandler handler)
        {
            e.AddHandler(LastPageEvent, handler);
        }
    
        public static void RemoveLastPageHandler(UIElement e, RoutedEventHandler handler)
        {
            e.RemoveHandler(LastPageEvent, handler);
        }
    
        private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            if (e.ViewportHeight == 0 || e.VerticalOffset == 0)
            {
                return;
            }
    
            var verticalSpaceLeft = e.ExtentHeight - e.VerticalOffset;
            if (verticalSpaceLeft < 2 * e.ViewportHeight)
            {
                var scrollViewer = (System.Windows.Controls.ScrollViewer)sender;
                scrollViewer.RaiseEvent(EventArgs);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-10
      • 2012-07-10
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 1970-01-01
      • 2018-02-19
      • 2021-05-30
      • 1970-01-01
      相关资源
      最近更新 更多