|
|
It's a no-brainer in WPF but how can I do that in Silverlight/WP7? In WPF the ListBox contains a ScrollViewer which raises the ScrollChanged RoutedEvent. With a custom RoutedEventTrigger its easy to fire the message. But you cannot do that in Silverlight/WP7.
There is no ScrollChanged event and I guess the missing support for attached events does not improve the situation.
I want to load data dynamically when the user scrolls down the list.
I found two solutions to that problem which I both don't like.
I can use the Silverlight Commanding support and an attached property
http://danielvaughan.orpius.com/post/Scroll-Based-Data-Loading-in-Windows-Phone-7.aspx
Or I can use a derived control
http://blogs.msdn.com/b/ptorr/archive/2010/10/12/procrastination-ftw-lazylistbox-should-improve-your-scrolling-performance-and-responsiveness.aspx
But I would prefer a solution solely based on Caliburn.
Any ideas?
|
|
Coordinator
Jan 20, 2012 at 1:55 PM
|
You might try writing a custom Trigger based on the code that Daniel shows in the first article.
|
|
|
|
Brilliant! That works like a charm :)
Thanks!
|
|
|
|
Weberse - any chance you could post your solution?
|
|
Apr 4, 2012 at 6:38 AM
Edited Apr 4, 2012 at 6:39 AM
|
I was playing around with that topic and ended up with two different samples. Can't remember which one worked better.
public class ScrollingStateChangedTrigger : EventTriggerBase<ListBox>
{
private bool alreadyHookedScrollEvents;
protected override string GetEventName()
{
return "ScrollingStateChanged";
}
protected override void OnAttached()
{
var element = (FrameworkElement)this.AssociatedObject;
element.Loaded += OnLoaded;
}
private static VisualStateGroup FindVisualState(FrameworkElement element, string name)
{
if (element == null)
return null;
IList groups = VisualStateManager.GetVisualStateGroups(element);
return groups.OfType<VisualStateGroup>().FirstOrDefault(@group => @group.Name == name);
}
private static T FindSimpleVisualChild<T>(DependencyObject element) where T : class
{
while (element != null)
{
if (element is T)
{
return element as T;
}
element = VisualTreeHelper.GetChild(element, 0);
}
return null;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
if (alreadyHookedScrollEvents)
return;
alreadyHookedScrollEvents = true;
ScrollViewer viewer = FindSimpleVisualChild<ScrollViewer>(this.AssociatedObject);
if (viewer != null)
{
// Visual States are always on the first child of the control template
FrameworkElement element = VisualTreeHelper.GetChild(viewer, 0) as FrameworkElement;
if (element != null)
{
VisualStateGroup group = FindVisualState(element, "ScrollStates");
if (group != null)
{
group.CurrentStateChanging += OnStateChanging;
}
}
}
}
private void OnStateChanging(object s, VisualStateChangedEventArgs args)
{
base.OnEvent(args);
}
}
and this one
public class ScrollingTrigger : EventTriggerBase<ListBox>
{
/// <summary>
/// ScrollChanged
/// </summary>
private const string ScrollChangedEventName = "ScrollChanged";
/// <summary>
/// VerticalOffset
/// </summary>
private const string VerticalOffsetPropertyName = "VerticalOffset";
protected override string GetEventName()
{
return ScrollChangedEventName;
}
protected override void OnAttached()
{
FrameworkElement element = (FrameworkElement)this.AssociatedObject;
element.Loaded += this.OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.Loaded -= this.OnLoaded;
ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>(element);
if (scrollViewer == null)
{
throw new InvalidOperationException("ScrollViewer not found.");
}
var listener = new DependencyPropertyListener();
listener.Changed += (sender1, e1) =>
{
var scea = new ScrollChangedEventArgs
{
ExtentHeight = scrollViewer.ExtentHeight,
ExtentWidth = scrollViewer.ExtentWidth,
HorizontalOffset = scrollViewer.HorizontalOffset,
VerticalOffset = scrollViewer.VerticalOffset,
ViewportHeight = scrollViewer.ViewportHeight,
ViewportWidth = scrollViewer.ViewportWidth
};
this.OnEvent(scea);
};
Binding binding = new Binding(VerticalOffsetPropertyName) { Source = scrollViewer };
listener.Attach(scrollViewer, binding);
}
private static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
}
If you need some more information you can find the WP7 sample in the source code of the
TecX project here on codeplex. Its in the TecX.Agile.Phone solution.
|
|