WPF MVVM模式下实现ListView下拉显示更多内容

简介: 原文:WPF MVVM模式下实现ListView下拉显示更多内容在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容。这样有两个好处,提高程序性能,减少网络流量。
原文: WPF MVVM模式下实现ListView下拉显示更多内容

在手机App中,如果有一个展示信息的列表,通常会展示很少一部分,当用户滑动到列表底部时,再加载更多内容。这样有两个好处,提高程序性能,减少网络流量。这篇博客中,将介绍如何在WPF ListView中实现这个功能。

实现思路:为ListView新增一个附加属性,用来绑定当下拉到底部时触发增加列表内容的功能。

XAML:

    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Window.Resources>
    <Grid>
        <ListView ItemsSource="{Binding Items}" Height="150" Width="80" local:ScrollViewerMonitor.AtEndCommand="{Binding FetchMoreDataCommand}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding}"/>
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

        <UserControl Opacity=".85" Background="Gray" Height="150" Width="80" Visibility="{Binding Busy, Converter={StaticResource BooleanToVisibilityConverter}}">
            <TextBlock Text="Loading..." Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </UserControl>
    </Grid>

ScrollViewerMonitor:

public class ScrollViewerMonitor
    {
        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 readonly DependencyProperty AtEndCommandProperty =
            DependencyProperty.RegisterAttached("AtEndCommand", typeof(ICommand), 
                typeof(ScrollViewerMonitor), new PropertyMetadata(OnAtEndCommandChanged));

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

        private static void element_Loaded(object sender, RoutedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)sender;

            element.Loaded -= element_Loaded;

            ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>(element);

            if(scrollViewer == null)
            {
                throw new InvalidOperationException("ScrollViewer not found.");
            }

            scrollViewer.ScrollChanged += delegate 
            {
                bool atBottom = scrollViewer.VerticalOffset 
                                 >= scrollViewer.ScrollableHeight;

                if(atBottom)
                {
                    var atEnd = GetAtEndCommand(element);
                    if(atEnd != null)
                    {
                        atEnd.Execute(null);
                    }
                }
            };
        }

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

MainViewModel:

public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            _busy = false;

            AddMoreItems();

            fetchMoreDataCommand = new DelegateCommand(() => {

                ThreadPool.QueueUserWorkItem(
                    delegate
                    {
                        Busy = true;

                        Thread.Sleep(3000);

                        App.Current.Dispatcher.BeginInvoke(new Action(()=> {

                            AddMoreItems();

                            Busy = false;

                        }));
                    });
            });
        }

        private void AddMoreItems()
        {
            int start = items.Count;
            int end = start + 10;
            for (int i = start; i < end; i++)
            {
                items.Add("Item " + i);
            }
        }

        readonly DelegateCommand fetchMoreDataCommand;

        public ICommand FetchMoreDataCommand
        {
            get
            {
                return fetchMoreDataCommand;
            }
        }

        private ObservableCollection<string> items = new ObservableCollection<string>();

        public ObservableCollection<string> Items
        {
            get
            {
                return items;
            }
        }

        private bool _busy;

        public bool Busy
        {
            get
            {
                return _busy;
            }

            set
            {
                if(_busy != value)
                {
                    _busy = value;

                    OnPropertyChanged("Busy");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if(PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Busy属性用来决定是否显示Loading。

运行效果:

感谢您的阅读!代码点击这里下载。

目录
相关文章
|
2月前
|
设计模式 前端开发 C#
WPF 项目中 MVVM模式 的简单例子说明
本文通过WPF项目中的加法操作示例,讲解了MVVM模式的结构和实现方法,包括数据模型、视图、视图模型的创建和数据绑定,以及命令的实现和事件通知机制。
|
3月前
|
C# 微服务 Windows
模块化革命:揭秘WPF与微服务架构的完美融合——从单一职责原则到事件聚合器模式,构建高度解耦与可扩展的应用程序
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中借鉴微服务架构思想,实现模块化设计。通过将WPF应用分解为独立的功能模块,并利用事件聚合器实现模块间解耦通信,可以有效提升开发效率和系统可维护性。文中还提供了具体示例代码,展示了如何使用事件聚合器进行模块间通信,以及如何利用依赖注入进一步提高模块解耦程度。此方法不仅有助于简化复杂度,还能使应用更加灵活易扩展。
100 0
|
3月前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
98 0
|
3月前
|
前端开发 开发者 C#
WPF开发者必读:MVVM模式实战,轻松实现现代桌面应用架构,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离应用程序的逻辑和界面,提高了代码的可维护性和可扩展性。本文介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定和逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种方式,开发者可以构建更加高效和可扩展的桌面应用程序。
166 0
|
3月前
|
前端开发 C# 开发者
WPF开发者必读:MVVM模式实战,轻松构建可维护的应用程序,让你的代码更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,MVVM(Model-View-ViewModel)模式通过分离关注点,提高了代码的可维护性和可扩展性。本文详细介绍了MVVM模式的三个核心组件:Model(数据模型)、View(用户界面)和ViewModel(处理数据绑定与逻辑),并通过示例代码展示了如何在WPF项目中实现MVVM模式。通过这种模式,开发者可以更高效地构建桌面应用程序。希望本文能帮助你在WPF开发中更好地应用MVVM模式。
183 0
|
3月前
|
C#
WPF/C#:程序关闭的三种模式
WPF/C#:程序关闭的三种模式
65 0
|
3月前
|
设计模式 前端开发 C#
WPF/C#:理解与实现WPF中的MVVM模式
WPF/C#:理解与实现WPF中的MVVM模式
202 0
|
5月前
|
C#
WPF/C#:程序关闭的三种模式
WPF/C#:程序关闭的三种模式
81 3
|
数据处理 C# 索引
WPF技术之ListView控件
WPF ListView控件是一个用来显示集合数据的控件,它以表格形式呈现数据,并支持对数据进行排序、筛选和操作。
329 0
|
设计模式 开发框架 前端开发
深入理解WPF中MVVM的设计思想
近些年来,随着WPF在生产,制造,工业控制等领域应用越来越广发,很多企业对WPF开发的需求也逐渐增多,使得很多人看到潜在机会,不断从Web,WinForm开发转向了WPF开发,但是WPF开发也有很多新的概念及设计思想,如:数据驱动,数据绑定,依赖属性,命令,控件模板,数据模板,MVVM等,与传统WinForm,ASP.NET WebForm开发,有很大的差异,今天就以一个简单的小例子,简述WPF开发中MVVM设计思想及应用。
104 0