代码改变世界

WP7 ListBox 滚动探测 以及 分页加载

2012-01-02 23:24  F-sea  阅读(2619)  评论(2编辑  收藏  举报

好吧 WP7 里面 的那个 LIST BOX 真心有点蛋疼。。。  滚来滚去 你就是不知掉 他滚在那里 。。。 对于 在移动 设备上经常需要使用 列表 并且 滚动加载  这可蛋疼 。
编辑过 Listbox 的template 人肯定知道  ListBox 里面的主体是个ScrollView构成的 ,我们来看看 这个 ScrollView 。。。 

唔。。。 里面 额 。。 有两个东西 
VerticalOffset
HorizontalOffset

这两个分别可以获取 垂直和水平方向上的滚动位置 。。。 

(其实 ScrollView 里面是有两个 ScrollBar ScrollBar有Value Change 事件 ,可以挖出 ScrollBar 找到 事件 并订阅的,, 。。。。)

现在我们第一定出我们的策略 就是 挖出 ScrollView 然后 监听 VerticalOffset  当 VeritcalOffset 到最大值的时候 就可以发出一个滚动到底部的通知?! 事件?! 无所谓了。。。 

好吧 直接上代码

public class ScrollBehavior
    {
        public static DependencyProperty AtEndCommandProperty
            = DependencyProperty.RegisterAttached(
                "AtEndCommand", typeof(ICommand),
                typeof(ScrollBehavior),
                new PropertyMetadata(null, OnCommandChanged));

        public static ICommand GetAtEndCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(AtEndCommandProperty);
        }

        public static void SetAtEndCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(AtEndCommandProperty, value);
        }

        public static DependencyProperty AtTopCommandProperty
           = DependencyProperty.RegisterAttached(
               "AtTopCommand", typeof(ICommand),
               typeof(ScrollBehavior),
               new PropertyMetadata(null, OnCommandChanged));

        public static ICommand GetAtTopCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(AtTopCommandProperty);
        }

        public static void SetAtTopCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(AtTopCommandProperty, value);
        }

        public static DependencyProperty AtScrollCommandProperty
        = DependencyProperty.RegisterAttached(
       "AtScrollCommand", typeof(ICommand),
       typeof(ScrollBehavior),
       new PropertyMetadata(null, OnCommandChanged));

        public static ICommand GetAtScrollCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(AtScrollCommandProperty);
        }

        public static void SetAtScrollCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(AtScrollCommandProperty, value);
        }



        public static void OnCommandChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)d;
            if (element != null && element is ListBox)
            {
                element.Loaded -= element_Loaded;
                element.Loaded += element_Loaded;
            }
        }

        static void element_Loaded(object sender, RoutedEventArgs e)
        {
            ((FrameworkElement)sender).Loaded -= element_Loaded;
            ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>((FrameworkElement)sender);
            if (scrollViewer != null)
            {

                var listener = new Listener();
                listener.Changed += delegate
                {


                    if (scrollViewer.ScrollableHeight != 0)
                    {
                        //System.Diagnostics.Debug.WriteLine((scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight).ToString());
                        if ((scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) >= -2)
                        {

                            CommandArg endCommandArg = new CommandArg();
                            endCommandArg.Key = "IsEnd";
                            endCommandArg.Value = true;

                            var endCommand = GetAtEndCommand((FrameworkElement)sender);

                            if (endCommand != null && endCommand.CanExecute(endCommandArg))
                            {
                                //System.Diagnostics.Debug.WriteLine("Scroll End");
                                endCommand.Execute(endCommandArg);
                            }
                        }

                        //System.Diagnostics.Debug.WriteLine(scrollViewer.VerticalOffset.ToString());
                        if (scrollViewer.VerticalOffset < 2)
                        {
                            CommandArg topCommandArg = new CommandArg();
                            topCommandArg.Key = "IsEnd";
                            topCommandArg.Value = false;

                            var topCommand = GetAtTopCommand((FrameworkElement)sender);
                            if (topCommand != null && topCommand.CanExecute(topCommandArg))
                            {

                                //System.Diagnostics.Debug.WriteLine("Scroll Top");
                                topCommand.Execute(topCommandArg);
                            }
                        }

                        CommandArg scrollCommandArg = new CommandArg();
                        scrollCommandArg.Key = "Sender";
                        scrollCommandArg.Value = scrollViewer;

                        var scrollCommand = GetAtScrollCommand((FrameworkElement)sender);
                        if (scrollCommand != null && scrollCommand.CanExecute(scrollCommandArg))
                        {
                            //System.Diagnostics.Debug.WriteLine("Scroll");
                            scrollCommand.Execute(scrollCommandArg);
                        }
                    }

                };

                Binding binding = new Binding("VerticalOffset");
                binding.Source = scrollViewer;
                listener.Attach(scrollViewer, binding);

            }


        }


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

    public class Listener
    {
        public readonly DependencyProperty property;
        ScrollViewer target;

        public Listener()
        {
            property = DependencyProperty.RegisterAttached(
                "ListenValue",
                typeof(object),
                typeof(Listener),
                new PropertyMetadata(null, HandleValueChanged));
        }

        public event EventHandler<ValueChangedEventArgs> Changed;

        void HandleValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {

            OnChanged(new ValueChangedEventArgs(e));
        }

        protected void OnChanged(ValueChangedEventArgs e)
        {

            if (Changed != null)
            {
                Changed(target, e);
            }
        }

        public void Attach(ScrollViewer element, Binding binding)
        {
            if (target != null)
            {
                throw new Exception(
                    "Cannot attach an already attached listener");
            }

            target = element;
            if (target.GetBindingExpression(property) == null)
            {
                target.SetBinding(property, binding);
            }

        }

        public void Detach()
        {
            target.ClearValue(property);
            target = null;
        }

    }

    public class ValueChangedEventArgs : EventArgs
    {
        public ValueChangedEventArgs(DependencyPropertyChangedEventArgs e)
        {
            EventArgs = e;
        }

        public DependencyPropertyChangedEventArgs EventArgs { get; private set; }
    }

  我先做了三个ICommand  类型的动态 属性 分别是 滚动到顶部 底部 和滚动中 所调用的Command

然后在动态属性绑定时 订阅控件 Loaded事件 ,这样 空间一旦Loaded 就我们就去翻出ScrollView 然后的 Listener 去监听 他的 VerticalOffset

监听器 其实就是做了个动态属性的Listener ,我们只需要把 ScrollView.VerticalOffset 绑定到 这个 Listener 的动态属性上 ,这样当值一旦变化 就会调用 方法HandleValueChanged

剩下的事情就很简单了 。。。 

判断下 VerticalOffset  是在顶上还是在低

不过判断的是有要注意点。。。  滚动到顶部的时候 值不一定是 0 也许是 0.X  在底部的时候 也不顶是最大值 也许是 也差个 0.X   (不是不很蛋疼  像不像问候鲍大爷?) 

 

PS: 怎么放附件?

 

本文章同步发表在wp7 开发论坛 wpdevn :http://www.wpdevn.com/showtopic-25.aspx