zoukankan      html  css  js  c++  java
  • 【WP8】ScrollViewer滑动到底触发器(ListBox失效)

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

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

      原理很简单,下面看实现

    // *************************************************
    // 
    // 作者: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上的话,需要保证每一项的高度都相同,否则会有误差(有时提前触发,有时候不触发)

  • 相关阅读:
    “automation服务器不能创建对象”的问题的解决方案大全
    转载区分C#中的Abstract函数和Virtual函数
    DOS批处理
    数据库设计范式
    java 内存查看工具
    Java内存溢出详解
    Struts2 循环编辑指定次数
    Selenium 使用
    spring security 获取当前用户信息
    由MyEclipse内存不足谈谈JVM内存
  • 原文地址:https://www.cnblogs.com/bomo/p/3840820.html
Copyright © 2011-2022 走看看