【问题标题】:ListBox scrolling event in MVVM WPFMVVM WPF 中的 ListBox 滚动事件
【发布时间】:2014-02-09 16:39:18
【问题描述】:

有没有办法以 MVVM 方式挂钩 WPF (Windows Phone7) 中的滚动事件?我想检测列表何时滚动到底部,然后做一些事情。我尝试过这样的事情,但显然它不起作用:

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

【问题讨论】:

    标签: c# wpf mvvm windows-phone windows-phone-7.1


    【解决方案1】:

    在这种情况下,我总是关注 attached 行为的方向,因为我们需要一个独立的解决方案,它可以在侧面 UI 和 MVVM 样式中工作。附加行为 - 是一个附加属性,它有一个事件处理程序来改变这个属性,所有的逻辑都在这个处理程序中实现。

    在这种情况下,您需要传递一个指示行为开始的布尔值,以及在我们的条件执行时执行的命令 - 滚动到 ListBox 的末尾。

    我创建了一个关键逻辑在这里的示例行为:

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

    使用示例:

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

    完整的示例项目可用here。

    【讨论】:

    • @Marek Małek:很遗憾,我没有在这个平台下测试过,如果不适合你,请告诉我。
    • Sooo... 不幸的是,它在 WP7.1 下不起作用 - ScrollViewer 没有 ScrollChanged 事件。有什么建议吗?
    • @Marek Małek: Here 和 here 它显示了ScrollChanged 事件的替代方案,我希望你自己找到了解决方案,因为我无法使用Windows Phone 7.1 下的解决方案.
    • 好的,我监听 LayoutUpdated 事件。谢谢你的链接!
    猜你喜欢
    • 1970-01-01
    • 2016-04-10
    • 2021-05-12
    • 1970-01-01
    • 2011-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多