3 votes

Chargement paresseux avec LongListSelector dans LayoutMode=Grid

J'affiche une collection d'images dans un LongListSelector sur WP8 et j'ai mis en œuvre la fonction modèle de chargement paresseux bien connu en utilisant l'événement ItemRealized de LLS.

Dans le code ci-dessous, OnItemRealized est appelé pour chaque élément de la collection d'images, même pour les éléments qui sont clairement hors écran. Dans ce scénario, 24 éléments tiennent à l'écran, mais le LLS en réalise 40, ce qui déclenche la fonction ResumeGetPictures() du ViewModel. Lorsque la collection d'images change (INotifyCollectionChanged), le LLS réalise également ces éléments jusqu'à ce qu'il n'en ait plus, ce qui déclenche la prochaine ResumeGetPictures() - et ce jusqu'à ce que le ViewModel soit incapable de charger d'autres éléments.

Tout semble bien se passer tant que le LLS est en LayoutMode=List. Mais lorsque je passe en mode Grille, le contrôle semble avaler chaque élément de la liste et le réaliser immédiatement. Ce qui rend impossible toute forme de chargement paresseux.

J'espère que j'ai juste fait quelque chose de très très mal - bien que j'en doute parce que j'ai tout vérifié trois fois et comme je l'ai dit, passer à "Liste" résout le problème immédiatement - malheureusement pas une option pour une galerie de photos de quelque sorte.

ViewModel :

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

Voir le Code-Behind :

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"/>

1voto

Oliver Weichhold Points 4600

J'ai pu obtenir l'effet désiré en observant la ScrollBar à l'intérieur du LLS. J'ai abstrait la fonctionnalité dans un comportement pour une réutilisation facile :

  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;
      }
    }
  }

Par souci d'exhaustivité :

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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X