Question

I have a WPF Datagrid that is displaying a structured log from a back-end system. The logs can be gigantic, so I only fetch a few hundred entries at time. I want to trigger retrieving more entries when the scrollbar thumb hits the 'bottom' of the scroll area.

I found this code-behind solution for sensing the end of the scroll, but I'm using Caliburn Micro. So I tried hooking up the ScrollChanged event, so I could process it in the view model (not my favorite solution, but I appear to be running out of options). The "obvious" implicit caliburn binding of

cal:Message.Attach="[Event ScrollViewer.ScrollChanged] = [Action DoScrollAction($eventArgs)]"

doesn't work, and neither did the explicit

        <i:Interaction.Triggers> 
            <i:EventTrigger EventName="ScrollViewer.ScrollChanged"> 
                <cal:ActionMessage MethodName="DoScrollAction"> 
                   <cal:Parameter Value="$eventargs" />
                </cal:ActionMessage> 
            </i:EventTrigger> 
        </i:Interaction.Triggers> 

approach. Is there something baked into Caliburn Micro that addresses these attached events? Is there a better event to use for sensing the end of the scroll area in a Datagrid that doesn't have me processing ScrollChanged events in a view model?

Was it helpful?

Solution

So I discovered Joymon's solution for addressing the attached events after posting my question, and used his RoutedEventTrigger class, combined with the code sensing the end-of-scroll condition in my view model.

For the record, here are the pieces of the solution:

xmlns:wpfCommon="clr-namespace:WPFCommon;assembly=WPFCommon"
...
<DataGrid x:Name="SCPLog">
        <i:Interaction.Triggers>
            <wpfCommon:RoutedEventTrigger RoutedEvent="ScrollViewer.ScrollChanged">
                <cal:ActionMessage MethodName="DoScroll">
                    <cal:Parameter Value="$eventargs" />
                </cal:ActionMessage>
            </wpfCommon:RoutedEventTrigger>
        </i:Interaction.Triggers>
</DataGrid>

And a variation on Joymon's RoutedEventTrigger, which I placed in my own WPFCommon library:

public class RoutedEventTrigger : EventTriggerBase<DependencyObject>
{
    public RoutedEvent RoutedEvent { get; set; }
    protected override void OnAttached()
    {
        var behavior = base.AssociatedObject as Behavior;
        var associatedElement = base.AssociatedObject as FrameworkElement;
        if (behavior != null)
            associatedElement = ((IAttachedObject)behavior).AssociatedObject as FrameworkElement;
        if (associatedElement == null)
            throw new ArgumentException("Routed Event trigger can only be associated to framework elements");
        if (RoutedEvent != null)
            associatedElement.AddHandler(RoutedEvent, new RoutedEventHandler(this.OnRoutedEvent));
    }
    void OnRoutedEvent(object sender, RoutedEventArgs args) { base.OnEvent(args); }
    protected override string GetEventName() { return RoutedEvent.Name; }
}

With the end-of-scroll detection in the view model:

public void DoScroll(ScrollChangedEventArgs e)
    {
        var scrollViewer = e.OriginalSource as ScrollViewer;
        if (scrollViewer != null && // Do we have a scroll bar?
            scrollViewer.ScrollableHeight > 0 && // Avoid firing the event on an empty list.
            scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight && // Are we at the end of the scrollbar?
        {
           // Do your end-of-scroll code here...
        }
    }

If someone knows a better way to handle the end-of-scroll event, e.g. doing it in XAML, I'd love to hear it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top