在 LayoutMode=Grid 中使用 LongListSelector 延迟加载 [英] Lazy Loading with LongListSelector in LayoutMode=Grid

查看:17
本文介绍了在 LayoutMode=Grid 中使用 LongListSelector 延迟加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 WP8 上的 LongListSelector 中显示一组图像,并且我已经实现了 众所周知的懒惰加载模式利用 LLS 的 ItemRealized 事件.

I'm displaying a collection of images in a LongListSelector on WP8 and I've implemented the well known lazy loading pattern utilizing LLS's ItemRealized event.

在下面的代码中,为图片集合中的每个项目调用 OnItemRealized - 即使对于明显不在屏幕上的项目也是如此.在这种情况下,屏幕上有 24 个项目,但 LLS 实现了 40 个项目,这会触发 ViewModel 的 ResumeGetPictures().当图片集合发生变化 (INotifyCollectionChanged) 时,LLS 也会意识到这些项目,直到它用完项目,触发下一个 ResumeGetPictures() - 这将持续到 ViewModel 无法加载更多项目.

In the code below OnItemRealized is called for each and every item in the Pictures collection - even for items which are clearly off-screen. In this scenario 24 items fit on screen yet the LLS realizes 40 items and this triggers the ResumeGetPictures() of the ViewModel. When the Pictures collection changes (INotifyCollectionChanged), the LLS will also realize those items until it runs out of items, triggering the next ResumeGetPictures() - this will go until the ViewModel is unable to load more items.

只要 LLS 处于 LayoutMode=List 中,一切似乎都很好.但是当我切换到 Grid 时,控件似乎吞下了列表中的每一项并立即实现.使任何类型的延迟加载都变得不可能.

All seems to be fine as long as the LLS is in LayoutMode=List. But when I switch to Grid, the control seems to swallow each and every item in the list and realizes it immediately. Making any sort of lazy loading impossible.

我希望我只是做了一些非常非常错误的事情——尽管我对此表示怀疑,因为我已经三重检查了所有内容,就像我说切换到列表"可以立即解决问题——不幸的是,这不是某种照片库的选项.

I hope I just did something very very wrong - although I doubt that because I've triple-checked everything and like I said switching to "List" resolves the problem immediately - unfortunately not an option for a photo gallery of some sort.

视图模型:

public IReactiveDerivedList<TPicture> Pictures
{
  get { return pictures; }
}

查看代码隐藏:

lls.ItemRealized += OnItemRealized;

private void OnItemRealized(object sender, ItemRealizationEventArgs e)
{
  var picture = e.Container.Content as Picture;

  if (picture != null)
  {
    // get index
    var pictureIndex = lls.ItemsSource.IndexOf(picture);

    if (pictureIndex >= lls.ItemsSource.Count * 0.95f)
      ViewModel.ResumeGetPictures();
  }
}

XAML:

<phone:LongListSelector Name="lls" Margin="13,-30,0,0"
  ItemsSource="{Binding Pictures}"
  Tap="OnListItemTapped"
  ItemTemplate="{StaticResource ItemTemplate}"           
  IsGroupingEnabled="False"
  LayoutMode="Grid" 
  GridCellSize="108,108"/>

推荐答案

通过观察 LLS 中的 ScrollBar,我能够获得预期的效果.我已将功能抽象为易于重用的行为:

I was able to get the desired effect by observing the ScrollBar inside the LLS. I've abstracted the functionality into a behavior for easy re-use:

  public class LLSIncrementalLoadingBehavior : Behavior<LongListSelector>
  {
    private ScrollBar llsScrollBar;

    #region Dependency Properties

    public static readonly DependencyProperty RequestMoreDataProperty = DependencyProperty.Register(
      "RequestMoreData", typeof(Action), typeof(LLSIncrementalLoadingBehavior), new PropertyMetadata(null, OnRequestMoreDataChanged));

    /// <summary>
    /// The action to invoke to initiate loading of more data
    /// </summary>
    public Action RequestMoreData
    {
      get { return (Action) this.GetValue(RequestMoreDataProperty); }
      set { this.SetValue(RequestMoreDataProperty, value); }
    }

    private static void OnRequestMoreDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((LLSIncrementalLoadingBehavior)d).RequestMoreData = (Action)e.NewValue;
    }

    public static readonly DependencyProperty ThresholdProperty = DependencyProperty.Register(
      "Threshold", typeof(double), typeof(LLSIncrementalLoadingBehavior), new PropertyMetadata(0.8, OnThresholdChanged));

    /// <summary>
    /// A value between 0 and 1 that controls how early more data is requested. Use 1 to only trigger it at the very end
    /// </summary>
    public double Threshold
    {
      get { return (double)this.GetValue(ThresholdProperty); }
      set { this.SetValue(ThresholdProperty, value); }
    }

    private static void OnThresholdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      ((LLSIncrementalLoadingBehavior)d).Threshold = (double)e.NewValue;
    }

    #endregion

    protected override void OnAttached()
    {
      base.OnAttached();
      AssociatedObject.Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
      llsScrollBar = VisualTreeHelperExtensions.FindFirstElementInVisualTree<ScrollBar>(AssociatedObject);

      llsScrollBar.ValueChanged += OnLlsScrollBarValueChanged;
    }

    private void OnLlsScrollBarValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
      var bottomEdge = (float)(e.NewValue + AssociatedObject.ActualHeight);
      var bottom = llsScrollBar.Maximum + AssociatedObject.ActualHeight;
      var threshold = bottom * Threshold;

      if (bottomEdge >= threshold)
        RequestMoreData();
    }

    protected override void OnDetaching()
    {
      base.OnDetaching();

      if (llsScrollBar != null)
      {
        llsScrollBar.ValueChanged -= OnLlsScrollBarValueChanged;
      }
    }
  }

为了完整起见:

public static T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
  if (parentElement != null)
  {
    var count = VisualTreeHelper.GetChildrenCount(parentElement);
    if (count == 0)
      return null;

    for (int i = 0; i < count; i++)
    {
      var child = VisualTreeHelper.GetChild(parentElement, i);

      if (child != null && child is T)
        return (T)child;
      else
      {
        var result = FindFirstElementInVisualTree<T>(child);
        if (result != null)
        {
          return result;
        }
      }
    }
  }
  return null;
}

这篇关于在 LayoutMode=Grid 中使用 LongListSelector 延迟加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆