1

Is there a way to hook to scroll event in WPF (Windows Phone7), in a MVVM manner? I'd like to detect when the list is scrolled to the bottom, and then do something. I tried something like this, but obviously it won't work:

<ListBox ItemsSource="{Binding Places}" SelectedItem="{Binding SelectedPlace, Mode=TwoWay}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Tap">
            <i:InvokeCommandAction Command="{Binding ListBoxClick}"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="Scroll">
            <i:InvokeCommandAction Command="{Binding ListBoxScroll}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    (...)
</ListBox>

1 Answer 1

4

In this situation, I always look in the direction of attached behavior, because we need an independent solution that would work on the side UI and in the MVVM style. Attached behavior - is an attached property, which has an event handler to change this property and all the logic is implemented in this handler.

In this case you need to pass a Boolean value that indicates the beginning of behavior, and the command, what be executed when our condition is perform - scroll to the end of ListBox.

I created an example behavior where the key logic is here:

private static void scrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
    var scrollViewer = sender as ScrollViewer;

    if (scrollViewer != null)
    {
        // Here we determine if the bottom reached
        if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
        {
            command = GetCommand(listBox);

            // Execute the command
            command.Execute(listBox);
        }
    }
}

Example of using:

XAML

<Window.DataContext>
    <local:TestViewModel />
</Window.DataContext>

<Window.Resources>
    <x:Array x:Key="TestArray" Type="{x:Type sys:String}">
        <sys:String>Test1</sys:String>
        <sys:String>Test2</sys:String>
        <sys:String>Test3</sys:String>
        <sys:String>Test4</sys:String>
        <sys:String>Test5</sys:String>
        <sys:String>Test6</sys:String>
        <sys:String>Test7</sys:String>
        <sys:String>Test8</sys:String>
        <sys:String>Test9</sys:String>
        <sys:String>Test10</sys:String>
    </x:Array>
</Window.Resources>

<Grid>
    <ListBox Name="TestListBox"
             AttachedBehaviors:ScrollingToBottomBehavior.IsEnabled="True"
             AttachedBehaviors:ScrollingToBottomBehavior.Command="{Binding TestButtonCommand}"
             ItemsSource="{StaticResource TestArray}"
             Height="50" />
</Grid>

TestViewModel

public class TestViewModel
{
    private ICommand _testButtonCommand = null;

    public ICommand TestButtonCommand
    {
        get
        {
            if (_testButtonCommand == null)
            {
                _testButtonCommand = new RelayCommand(param => this.TestButton(), null);
            }

            return _testButtonCommand;
        }
    }

    private void TestButton()
    {
        MessageBox.Show("Test command execute");
    }
}

ScrollingToBottomBehavior

public class ScrollingToBottomBehavior
{
    #region Private Section

    private static ListBox listBox = null;
    private static ICommand command = null;

    #endregion

    #region IsEnabledProperty

    public static readonly DependencyProperty IsEnabledProperty;

    public static void SetIsEnabled(DependencyObject DepObject, string value)
    {
        DepObject.SetValue(IsEnabledProperty, value);
    }

    public static bool GetIsEnabled(DependencyObject DepObject)
    {
        return (bool)DepObject.GetValue(IsEnabledProperty);
    }

    #endregion

    #region CommandProperty

    public static readonly DependencyProperty CommandProperty;

    public static void SetCommand(DependencyObject DepObject, ICommand value)
    {
        DepObject.SetValue(CommandProperty, value);
    }

    public static ICommand GetCommand(DependencyObject DepObject)
    {
        return (ICommand)DepObject.GetValue(CommandProperty);
    }

    static ScrollingToBottomBehavior()
    {
        IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled",
                                                            typeof(bool),
                                                            typeof(ScrollingToBottomBehavior),
                                                            new UIPropertyMetadata(false, IsFrontTurn));

        CommandProperty = DependencyProperty.RegisterAttached("Command",
                                                            typeof(ICommand),
                                                            typeof(ScrollingToBottomBehavior));
    }

    #endregion

    private static void IsFrontTurn(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        listBox = sender as ListBox;

        if (listBox == null)
        {
            return;
        }

        if (e.NewValue is bool && ((bool)e.NewValue) == true)
        {
            listBox.Loaded += new RoutedEventHandler(listBoxLoaded);
        }
        else 
        {
            listBox.Loaded -= new RoutedEventHandler(listBoxLoaded);
        }
    }

    private static void listBoxLoaded(object sender, RoutedEventArgs e)
    {
        var scrollViewer = GetFirstChildOfType<ScrollViewer>(listBox);

        if (scrollViewer != null)
        {
            scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewerScrollChanged);
        }
    }

    #region GetFirstChildOfType

    private static T GetFirstChildOfType<T>(DependencyObject dependencyObject) where T : DependencyObject
    {
        if (dependencyObject == null)
        {
            return null;
        }

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
        {
            var child = VisualTreeHelper.GetChild(dependencyObject, i);

            var result = (child as T) ?? GetFirstChildOfType<T>(child);

            if (result != null)
            {
                return result;
            }
        }

        return null;
    }

    #endregion

    private static void scrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        var scrollViewer = sender as ScrollViewer;

        if (scrollViewer != null)
        {
            if (scrollViewer.VerticalOffset == scrollViewer.ScrollableHeight)
            {
                command = GetCommand(listBox);
                command.Execute(listBox);
            }
        }
    }
}

Complete sample project is available here.

Sign up to request clarification or add additional context in comments.

4 Comments

@Marek Małek: Unfortunately, I have not tested it under this platform, if will not work for you, let me know.
Sooo... Unfortunately it won't work under WP7.1 - ScrollViewer doesn't have the ScrollChanged event. Any advice?
@Marek Małek: Here and here it shows the an alternative of ScrollChanged event, I hope you did find a solution himself, since I can not use the solution under Windows Phone 7.1.
Ok, I listen to the LayoutUpdated event. Thank you for the links!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.