Fork me on GitHub

WPF ListBox滑动滚动条到底部时持续追加数据

背景

由于我们的数据展示中使用过的listBox,如果一下子展示过多的数据时,如果还需要加载图片等,可能会导致加载过程中等待时间过长,用户体验不不佳,因此我们采用分页的方式来实现更佳的用户体验,但是需要实现当滑动滚动条(这里是向底部滑动过程中)进行数据加载,也就是相当于点击下一页的效果。

实现思路

ListBox本身有一个ScrollView,ScrollView有几个属性可以标记是否当前已经滑到底部,然后进行数据的追加。

 

 

 

使用VisualTreeHelper来获取ListBox中的ScrollViewer 

获取方式: scrollViewer = FindSimpleVisualChild<ScrollViewer>(listBox);

复制代码
 T FindSimpleVisualChild<T>(DependencyObject element) where T : class
        {
            while (element != null)
            {

                if (element is T)
                    return element as T;

                element = VisualTreeHelper.GetChild(element, 0);
            }

            return null;
        }
复制代码

然后监听:  scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;当滑动到底部时去加载数据。

复制代码
 private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {

            if (isBootomScrollView(scrollViewer))
            {
                if (pageSize < Total)
                {
                    page++;
                    pageSize = page * 10;
                   // NextPaged?.Invoke( page,10);
           //追加数据的逻辑 } } }
private bool isBootomScrollView(ScrollViewer view) { bool isBottom = false; double dVer = view.VerticalOffset; double vViewport = view.ViewportHeight; double eextent = view.ExtentHeight; if (dVer != 0) { if (dVer + vViewport == eextent) { isBottom = true; } else { isBottom = false; } } else { isBottom = false; } return isBottom; }
复制代码

每次首页加载时需要注意,一定要将Scrollviewer设置滑动到顶部,否则数据会一直加载

完整代码如下:public event Action<object> SelectItemChanged;

复制代码
private int Total = 0;
        int pageSize = 0;
        int page = 1;
        ScrollViewer scrollViewer = null;
        /// <summary>
        /// 刷新数据
        /// </summary>
        /// <param name="total"></param>
        /// <param name="newlist"></param>
        public void ReFleshImage(int total, ObservableCollection<Data> newlist)
        {
            Task.Run(() =>
            {
                Total = total;
                if (newlist==null)
                {
                    pageSize = OldData.Count;
                }
                this.Dispatcher.InvokeAsync(() =>
                {
                    if (alreadyHookedScrollEvents)
                        return;
                    if (scrollViewer == null)//首次加载中去获取ScrollView可能会报异常,因此在这里再次判断获取
                    {
                        scrollViewer = FindSimpleVisualChild<ScrollViewer>(listBox);
                        if (scrollViewer != null)
                        {
                            scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
                            scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
                            alreadyHookedScrollEvents = true;
                        }
                    }
                    else
                    {
                        scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
                        scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
                        alreadyHookedScrollEvents = true;
                    }
                    alreadyHookedScrollEvents = true;//标注下次不需要再获取
                });
            });

            Task.Run(() =>
            {
                this.Dispatcher.InvokeAsync(() =>
                {
                    try
                    {
                        List<BitmapSource> list = new List<BitmapSource>();
                        if (newlist == null)
                        {
                            foreach (var item in OldData)
                            {
                                var img = ImageService.GetImageByUrl(item.imagePath);
                                list.Add(img);
                            }
                            for (int i = 0; i < list.Count; i++)
                            {
                               OldeData[i].ImageSource = list[i];
                            }
                        }
                        else
                        {
                            foreach (var item in newlist)
                            {
                                var img = ImageService.GetImagebyUrl(item.imagePath);
                                list.Add(img);
                            }
                            for (int i = 0; i < list.Count; i++)
                            {
                                newlist[i].ImageSource = list[i];
                            }
                            List<Data> oldlist = OldData.ToList();
                            oldlist.AddRange(newlist);
                           OlData = oldlist.ToObservableCollection();
                        }
                       
                    }
                    catch (Exception ex)
                    {

                       
                    }
                });
            });
        }
        /// <summary>
        /// 再次加载
        /// </summary>
        public void ResetPage() {
            try
            {
                page = 1;
                if (scrollViewer != null)
                {
                    scrollViewer.ScrollToHome();
                }
            }
            catch (Exception ex)
            {
                CommonLib.LogClass.SystemErrLog("", ex.ToString());
            }
        }
        public event Action<int,int> NextPaged;

        bool alreadyHookedScrollEvents = false;
        void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Task.Run(() =>
            {
                this.Dispatcher.InvokeAsync(() =>
                {
                    try
                    {
                        scrollViewer = FindSimpleVisualChild<ScrollViewer>(listBox);
                    }
                    catch (Exception ex)
                    {

                     
                    }
                });
            });    
        }

        T FindSimpleVisualChild<T>(DependencyObject element) where T : class
        {
            while (element != null)
            {

                if (element is T)
                    return element as T;

                element = VisualTreeHelper.GetChild(element, 0);
            }

            return null;
        }

       private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {

            if (isBootomScrollView(scrollViewer))
            {
                if (pageSize < Total)
                {
                    page++;
                    pageSize = page * 10;
                    NextPaged?.Invoke( page,10);
} } }
private bool isBootomScrollView(ScrollViewer view) { bool isBottom = false; double dVer = view.VerticalOffset; double vViewport = view.ViewportHeight; double eextent = view.ExtentHeight; if (dVer != 0) { if (dVer + vViewport == eextent) { isBottom = true; } else { isBottom = false; } } else { isBottom = false; } return isBottom; } }
复制代码

 

以上只是个人见解,应该有更好的方式。希望大家指点一下。

 

posted @   黄高林  阅读(2885)  评论(2编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示