我正在WP8上的LongListSelector中显示图像集合,并且已经利用LLS的ItemRealized事件实现了well known lazy loading pattern。
在下面的代码中,将对Pictures集合中的每个项目调用OnItemRealized-即使对于明显不在屏幕上的项目也是如此。在这种情况下,屏幕上可以容纳24个项目,但LLS可以实现40个项目,这将触发ViewModel的ResumeGetPictures()。当Pictures集合更改(INotifyCollectionChanged)时,LLS还将实现这些项目,直到项目用完,并触发下一个ResumeGetPictures()-直到ViewModel无法加载更多项目为止。
只要LLS在LayoutMode = List中,一切似乎都很好。但是,当我切换到网格时,该控件似乎吞没了列表中的每一项并立即实现了它。使任何形式的延迟加载都变得不可能。
我希望我所做的事情非常非常错误-尽管我对此表示怀疑,因为我已经仔细检查了所有内容,并且就像我说的那样切换到“列表”可以立即解决问题-不幸的是,对于某些类型的照相馆而言,不是一种选择。
ViewModel:
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,我可以获得所需的效果。我已经将该功能抽象为一个行为,以方便重用:
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;
}