【WP8】ScrollViewer滑动到底触发器(ListBox失效)

很多时候会有到底加载更多的需求,而ScrollViewer不支持继承,无法继承它进行扩展,只能通过触发器来控制到底的事件(当然,可以通过UserControl去扩展)

  思路:定义一个Trigger,自定义依赖属性,绑定到该属性到ScrollViewer的VerticalOffset属性上,然后监听属性的变化,就能监控到滚动事件了,然后判断滚动的位置从而判断出是否到底

  原理很简单,下面看实现

 

【WP8】ScrollViewer滑动到底触发器(ListBox失效)
// *************************************************

// 

// 作者:bomo

// 小组:WP开发组

// 创建日期:2014/7/9 0:10:32

// 版本号:V1.00

// 说明:

// 

// *************************************************

// 

// 修改历史: 

// Date                WhoChanges        Made 

// 2014/7/9 0:10:32            bomo         Initial creation 

//

// *************************************************



using System.Collections.Generic;

using System.Linq;

using System.Windows;

using System.Windows.Media;



namespace XTuOne.Common.Helpers

{

    /// <summary>

    /// 扩展依赖属性类

    /// </summary>

    public static class DependencyObjectExtend

    {

        public static IEnumerable<DependencyObject> GetDescendant(this DependencyObject element)

        {

            var list = new List<DependencyObject>();

            var count = VisualTreeHelper.GetChildrenCount(element);

            for (int i = 0; i < count; i++)

            {

                var child = VisualTreeHelper.GetChild(element, i);

                list.Add(child);

                list.AddRange(child.GetDescendant());

            }



            return list;

        }

     

        /// <summary>

        /// 查找子孙节点中符合类型的首个节点

        /// </summary>

        public static T GetFirstDescendantOfType<T>(this DependencyObject start) where T : DependencyObject

        {

            return start.GetDescendantsOfType<T>().FirstOrDefault();

        }



        /// <summary>

        /// 查找子孙节点中符合类型的节点

        /// </summary>

        public static IEnumerable<T> GetDescendantsOfType<T>(this DependencyObject start) where T : DependencyObject

        {

            return start.GetDescendants().OfType<T>();

        }



        /// <summary>

        /// 获取所有的子孙阶段

        /// </summary>

        public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject start)

        {

            if (start == null)

                yield break;



            var queue = new Queue<DependencyObject>();

            queue.Enqueue(start);

            yield return start;



            while (queue.Count > 0)

            {

                var parent = queue.Dequeue();

                var count2 = VisualTreeHelper.GetChildrenCount(parent);



                for (int i = 0; i < count2; i++)

                {

                    var child = VisualTreeHelper.GetChild(parent, i);

                    yield return child;

                    queue.Enqueue(child);

                }

            }

        }

    }

}
依赖属性扩展类

 

// *************************************************

// 

// 作者:bomo

// 小组:WP开发组

// 创建日期:2014/7/11 11:45:14

// 版本号:V1.00

// 说明:

// 

// *************************************************

// 

// 修改历史: 

// Date                WhoChanges        Made 

// 2014/7/11 11:45:14            bomo         Initial creation 

//

// *************************************************



using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Interactivity;

using XTuOne.Common.Helpers;



namespace XTuOne.Utility.Helpers

{

    /// <summary>

    /// ScrollViewer到底触发器

    /// </summary>

    public class ScrollViewerToBottomTrigger : TriggerBase<DependencyObject>

    {

        public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register(

            "VerticalOffset", typeof(double), typeof(ScrollViewerToBottomTrigger),

            new PropertyMetadata(0.0, VerticalOffsetPropertyChanged));



        private ScrollViewer scrollView;



        public double VerticalOffset

        {

            get { return (double)GetValue(VerticalOffsetProperty); }

            set { SetValue(VerticalOffsetProperty, value); }

        }



        public static void VerticalOffsetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            var behavior = d as ScrollViewerToBottomTrigger;

            if (behavior != null)

                behavior.OnVerticalOffsetChanged();

        }



        protected override void OnAttached()

        {

            base.OnAttached();



            if (AssociatedObject is FrameworkElement)

            {

                (AssociatedObject as FrameworkElement).SizeChanged += control_SizeChanged;

            }

        }



        private void control_SizeChanged(object sender, SizeChangedEventArgs e)

        {

            if (!(AssociatedObject is FrameworkElement))

                return;



            var scroll = AssociatedObject.GetFirstDescendantOfType<ScrollViewer>();

            if (scroll != null)

            {

                AttachedScroll(scroll);

                (AssociatedObject as FrameworkElement).SizeChanged -= control_SizeChanged;

            }

        }



        private void AttachedScroll(ScrollViewer scroll)

        {

            if (scroll == null)

                return;

            scrollView = scroll;



            var binding = new Binding { Source = scroll, Path = new PropertyPath("VerticalOffset") };

            BindingOperations.SetBinding(this, VerticalOffsetProperty, binding);

        }



        private void OnVerticalOffsetChanged()

        {

            var scroll = scrollView;

            if (scroll == null)

                return;



            if (scroll.ExtentHeight - scroll.VerticalOffset - scroll.ViewportHeight <= 5)

            {

                InvokeActions(null);

            }

        }

    }

}

通过Trigger实现,由于Trigger是一个附加元素,可以附加到任何一个符合的元素上(子孙元素包含ScrollViewer的控件),同时触发的事件可以支持绑定

 

使用(使用CM进行绑定)

<ScrollViewer x:Name="ScrollViewer">

    <i:Interaction.Triggers>

        <utilityHelpers:ScrollViewerToBottomTrigger>

            <micro:ActionMessage MethodName="LoadMessage"/>

        </utilityHelpers:ScrollViewerToBottomTrigger>

    </i:Interaction.Triggers>

</ScrollViewer>

一个触发器只能对应触发一个事件(用于绑定),如果需要多个扩展,比如:到顶触发,滑动触发等,就需要编写多个触发器,如果这样可以考虑在UserControl扩展

 

 

 发现一个问题(上面触发器在ListBox失效):

  ListBox中的ScrollViewer的ExtentHeight是数据项的个数,比如ListBox有20条数据,那么ExtentHeight就是20

  ListBox中的VerticalOffset是表示当前位置,如果当前屏幕中显示的第一条是第12条数据,那么VerticalOffset就是12.xxx

  ListBox中的ViewportHeight是根据第一屏的数据计算的,把List滑动到顶部,然后计算出页面内有几个项,这个值是假定List内所有的项的大小都是一样的,如果ListBox中的ItemTemplate的高度不是相同大小的,就会出现误差,导致下面三种情况都可能出现

    ExtentHeight == VerticalOffset + ViewportHeight

    ExtentHeight > VerticalOffset + ViewportHeight

    ExtentHeight < VerticalOffset + ViewportHeight

 如果要用在ListBox上的话,需要保证每一项的高度都相同,否则会有误差(有时提前触发,有时候不触发)

你可能感兴趣的:(scrollview)