• 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。

    运行效果:

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

  • 相关阅读:
    启动redis时报错:FATAL CONFIG FILE ERROR :Bad directive or wrong number of arguments
    转载Redis的三个框架:Jedis,Redisson,Lettuce
    转载Dubbo详解(一):Dubbo介绍和SpringBoot整合Dubbo+ZooKeeper
    Redisson如何实现类似incr的自增操作
    转载解决 com.alibaba.fastjson.JSONException: autoType is not support.
    转载springboot+mybatis实现数据库的读写分离
    转载dubbo服务被重复调用三次的原因
    js中实现函数防抖跟函数节流
    网站项目后台的目录命名为admin后,网页莫名其妙的变样了
    createreactapp不支持less的解决方式
  • 原文地址:https://www.cnblogs.com/yang-fei/p/4718325.html
Copyright © 2020-2023  润新知