• 背水一战 Windows 10 (57)


    [源码下载]


    背水一战 Windows 10 (57) - 控件(集合类): ListViewBase - 增量加载, 分步绘制



    作者:webabcd


    介绍
    背水一战 Windows 10 之 控件(集合类 - ListViewBase)

    • 增量加载
    • 分步绘制(大数据量流畅滚动)



    示例
    1、ListViewBase 的增量加载
    Controls/CollectionControl/ListViewBaseDemo/MyIncrementalLoading.cs

    /*
     * 演示如何实现 ISupportIncrementalLoading 接口,以便为 ListViewBase 的增量加载提供数据
     * 
     * 
     * ISupportIncrementalLoading - 用于支持增量加载
     *     HasMoreItems - 是否还有更多的数据
     *     IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) - 异步加载指定数量的数据(增量加载)
     *    
     * LoadMoreItemsResult - 增量加载的结果
     *     Count - 实际已加载的数据量
     */
    
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Runtime.InteropServices.WindowsRuntime;
    using System.Threading.Tasks;
    using Windows.Foundation;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Data;
    
    namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
    {
        public class MyIncrementalLoading<T> : ObservableCollection<T>, ISupportIncrementalLoading
        {
            // 是否正在异步加载中
            private bool _isBusy = false;
    
            // 提供数据的 Func
            // 第一个参数:增量加载的起始索引;第二个参数:需要获取的数据量;第三个参数:获取到的数据集合
            private Func<int, int, List<T>> _funcGetData;
            // 最大可显示的数据量
            private uint _totalCount = 0;
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="totalCount">最大可显示的数据量</param>
            /// <param name="getDataFunc">提供数据的 Func</param>
            public MyIncrementalLoading(uint totalCount, Func<int, int, List<T>> getDataFunc)
            {
                _funcGetData = getDataFunc;
                _totalCount = totalCount;
            }
    
            /// <summary>
            /// 是否还有更多的数据
            /// </summary>
            public bool HasMoreItems
            {
                get { return this.Count < _totalCount; }
            }
    
            /// <summary>
            /// 异步加载数据(增量加载)
            /// </summary>
            /// <param name="count">需要加载的数据量</param>
            /// <returns></returns>
            public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
            {
                if (_isBusy)
                {
                    throw new InvalidOperationException("忙着呢,先不搭理你");
                }
                _isBusy = true;
    
                var dispatcher = Window.Current.Dispatcher;
    
                return AsyncInfo.Run
                (
                    (token) => Task.Run<LoadMoreItemsResult>
                    (
                        async () =>
                        {
                            try
                            {
                                // 模拟长时任务
                                await Task.Delay(1000);
    
                                // 增量加载的起始索引
                                var startIndex = this.Count;
    
                                await dispatcher.RunAsync
                                (
                                     CoreDispatcherPriority.Normal,
                                     () =>
                                     {
                                         // 通过 Func 获取增量数据
                                         var items = _funcGetData(startIndex, (int)count);
                                         foreach (var item in items)
                                         {
                                             this.Add(item);
                                         }
                                     }
                                 );
    
                                // Count - 实际已加载的数据量
                                return new LoadMoreItemsResult { Count = (uint)this.Count };
                            }
                            finally
                            {
                                _isBusy = false;
                            }
                        },
                        token
                    )
                );
            }
        }
    }

    Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo3.xaml

    <Page
        x:Class="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo3"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent" Margin="10 0 10 10">
    
            <TextBlock Name="lblMsg" />
    
            <ListView x:Name="listView" Width="300" Height="300" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0 30 0 0">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Border Background="Blue" Width="200" CornerRadius="3" HorizontalAlignment="Left">
                            <TextBlock Text="{Binding Name}" />
                        </Border>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    
            <TextBlock Name="lblLog" Margin="0 350 0 0" />
    
        </Grid>
    </Page>

    Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo3.xaml.cs

    /*
     * ListViewBase(基类) - 列表控件基类(继承自 Selector, 请参见 /Controls/SelectionControl/SelectorDemo.xaml)
     *     IncrementalLoadingTrigger - 增量加载的触发器
     *         Edge - 允许触发增量加载,默认值
     *         None - 禁止触发增量加载
     *     DataFetchSize - 预提数据的大小,默认值 3.0
     *         本例将此值设置为 4.0 ,其效果为(注:本例中的 ListView 每页可显示的数据量为 6 条或 7 条,以下计算需基于此)
     *         1、先获取 1 条数据,为的是尽量快地显示数据
     *         2、再获取 4.0 * 1 条数据
     *         3、再获取 4.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据
     *         4、以后每次到达阈值后,均增量加载 4.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据
     *     IncrementalLoadingThreshold - 增量加载的阈值,默认值 0.0
     *         本例将此值设置为 2.0 ,其效果为(注:本例中的 ListView 每页可显示的数据量为 6 条或 7 条)
     *         1、滚动中,如果已准备好的数据少于 2.0 * (6 或 7,如果 ListView 当前显示了 6 条数据则为 6,如果 ListView 当前显示了 7 条数据则为 7) 条数据,则开始增量加载  
     *         
     *         
     * 本例用于演示如何实现 ListViewBase 的增量加载(数据源需要实现 ISupportIncrementalLoading 接口,详见:MyIncrementalLoading.cs)
     */
    
    using Windows.UI.Xaml.Controls;
    using System.Linq;
    using System.Collections.Specialized;
    using System;
    using Windows10.Common;
    using Windows.UI.Xaml;
    
    namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
    {
        public sealed partial class ListViewBaseDemo3 : Page
        {
            // 实现了增量加载的数据源
            private MyIncrementalLoading<Employee> _employees;
    
            public ListViewBaseDemo3()
            {
                this.InitializeComponent();
    
                this.Loaded += ListViewBaseDemo3_Loaded;
            }
    
            private void ListViewBaseDemo3_Loaded(object sender, RoutedEventArgs e)
            {
                listView.IncrementalLoadingTrigger = IncrementalLoadingTrigger.Edge;
                listView.DataFetchSize = 4.0;
                listView.IncrementalLoadingThreshold = 2.0;
    
                _employees = new MyIncrementalLoading<Employee>(1000, (startIndex, count) =>
                {
                    lblLog.Text += string.Format("从索引 {0} 处开始获取 {1} 条数据", startIndex, count);
                    lblLog.Text += Environment.NewLine;
    
                    return TestData.GetEmployees().Skip(startIndex).Take(count).ToList();
                });
    
                _employees.CollectionChanged += _employees_CollectionChanged;
    
                listView.ItemsSource = _employees;
            }
    
            void _employees_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                lblMsg.Text = "已获取的数据量:" + _employees.Count.ToString();
            }
        }
    }


    2、ListViewBase 的分步绘制
    Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo4.xaml

    <Page
        x:Class="Windows10.Controls.CollectionControl.ListViewBaseDemo.ListViewBaseDemo4"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Windows10.Controls.CollectionControl.ListViewBaseDemo"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="Transparent">
    
            <!--
                ListViewBase(基类) - 列表控件基类
                    ContainerContentChanging - 数据虚拟化时,项容器的内容发生变化时触发的事件(仅 ItemsStackPanel 和 ItemsWrapGrid 有效)
            -->
            
            <GridView x:Name="gridView" Margin="10 0 10 10" ContainerContentChanging="gridView_ContainerContentChanging">
                <GridView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Width="80" Height="80" Background="Blue">
                            <Rectangle x:Name="placeholderRectangle" Fill="Red" Height="10" Opacity="0" />
                            <TextBlock x:Name="lblName" Text="{Binding Name}" Foreground="Yellow" />
                            <TextBlock x:Name="lblAge" Text="{Binding Age}" Foreground="Aqua" />
                            <TextBlock x:Name="lblIsMale" Text="{Binding IsMale}" Foreground="Gray" />
                        </StackPanel>
                    </DataTemplate>
                </GridView.ItemTemplate>
            </GridView>
            
        </Grid>
    </Page>

    Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo4.xaml.cs

    /*
     * ListViewBase(基类) - 列表控件基类(继承自 Selector, 请参见 /Controls/SelectionControl/SelectorDemo.xaml)
     *     ContainerContentChanging - 数据虚拟化时,项容器的内容发生变化时触发的事件(仅 ItemsStackPanel 和 ItemsWrapGrid 有效)
     * 
     * 
     * 当 ListViewBase 的一屏需要显示的数据量极大时(一屏的 item 多,且每个 item 中的 element 也多),由于每次滚动时需要绘制当前屏的每个 element,这需要占用大量的 ui 资源,所以就会有一些卡顿
     * 为了解决这个问题 uwp 给出了两种解决方案
     * 1、设置 ListViewBase 的 ShowsScrollingPlaceholders 属性为 true(默认值),每次显示 item 时先显示占位符(尚不清楚怎么修改这个占位符的背景色),然后再绘制内容
     *    相关演示请参见:/Controls/CollectionControl/ListViewBaseDemo/ListViewBaseDemo1.xaml
     * 2、通过 ListViewBase 的 ContainerContentChanging 事件,分步绘制 item 中的 element
     *    本例即介绍这种方法。注意在 uwp 中已经不用这么麻烦了,可以通过 x:Bind 和 x:Phase 来实现,请参见:/Bind/PhaseDemo.xaml
     * 
     * 
     * 本例用于演示如何实现 ListViewBase 的分步绘制(大数据量流畅滚动)
     * 
     * 
     * 注:
     * 虚拟化布局控件用于减少创建的 item 数量
     * 分步绘制用于在绘制 item 时,分阶段绘制 item 上的元素
     */
    
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Navigation;
    using Windows.UI.Xaml.Shapes;
    using Windows10.Common;
    
    namespace Windows10.Controls.CollectionControl.ListViewBaseDemo
    {
        public sealed partial class ListViewBaseDemo4 : Page
        {
            public ListViewBaseDemo4()
            {
                this.InitializeComponent();
            }
    
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                gridView.ItemsSource = TestData.GetEmployees(1000);
    
                // 默认值是 true,即为了保证流畅,每次显示 item 时先会显示占位符(application 级的背景色块),然后再绘制内容
                // 本例演示 ContainerContentChanging 事件的使用,所以不会用到这个
                gridView.ShowsScrollingPlaceholders = false;
            }
    
            private void gridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
            {
                // 交由我处理吧(不用系统再处理了)
                args.Handled = true;
    
                // 第 1 阶段绘制
                // args.Phase.ToString(); // 0
    
                StackPanel templateRoot = (StackPanel)args.ItemContainer.ContentTemplateRoot;
                Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
                TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");
                TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");
                TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale");
    
                // 显示自定义占位符(也可以不用这个,而是直接显示 item 的背景)
                placeholderRectangle.Opacity = 1;
    
                // 除了占位符外,所有 item 全部暂时不绘制
                lblName.Opacity = 0;
                lblAge.Opacity = 0;
                lblIsMale.Opacity = 0;
    
                // 开始下一阶段的绘制
                args.RegisterUpdateCallback(ShowName);
            }
    
            private void ShowName(ListViewBase sender, ContainerContentChangingEventArgs args)
            {
                // 第 2 阶段绘制
                // args.Phase.ToString(); // 1
    
                Employee employee = (Employee)args.Item;
                SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
                StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
                TextBlock lblName = (TextBlock)templateRoot.FindName("lblName");
    
                // 绘制第 2 阶段的内容
                lblName.Text = employee.Name;
                lblName.Opacity = 1;
    
                // 开始下一阶段的绘制
                args.RegisterUpdateCallback(ShowAge);
            }
    
            private void ShowAge(ListViewBase sender, ContainerContentChangingEventArgs args)
            {
                // 第 3 阶段绘制
                // args.Phase.ToString(); // 2
    
                Employee employee = (Employee)args.Item;
                SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
                StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
                TextBlock lblAge = (TextBlock)templateRoot.FindName("lblAge");
    
                // 绘制第 3 阶段的内容
                lblAge.Text = employee.Age.ToString();
                lblAge.Opacity = 1;
    
                // 开始下一阶段的绘制
                args.RegisterUpdateCallback(ShowIsMale);
            }
    
            private void ShowIsMale(ListViewBase sender, ContainerContentChangingEventArgs args)
            {
                // 第 4 阶段绘制
                // args.Phase.ToString(); // 3
    
                Employee employee = (Employee)args.Item;
                SelectorItem itemContainer = (SelectorItem)args.ItemContainer;
                StackPanel templateRoot = (StackPanel)itemContainer.ContentTemplateRoot;
                Rectangle placeholderRectangle = (Rectangle)templateRoot.FindName("placeholderRectangle");
                TextBlock lblIsMale = (TextBlock)templateRoot.FindName("lblIsMale");
    
                // 绘制第 4 阶段的内容
                lblIsMale.Text = employee.IsMale.ToString();
                lblIsMale.Opacity = 1;
    
                // 隐藏自定义占位符
                placeholderRectangle.Opacity = 0;
            }
        }
    }



    OK
    [源码下载]

  • 相关阅读:
    codechef: ADAROKS2 ,Ada Rooks 2
    codechef: BINARY, Binary Movements
    codechef : TREDEG , Trees and Degrees
    ●洛谷P1291 [SHOI2002]百事世界杯之旅
    ●BZOJ 1416 [NOI2006]神奇的口袋
    ●CodeForce 293E Close Vertices
    ●POJ 1741 Tree
    ●CodeForces 480E Parking Lot
    ●计蒜客 百度地图的实时路况
    ●CodeForces 549F Yura and Developers
  • 原文地址:https://www.cnblogs.com/webabcd/p/7144106.html
Copyright © 2020-2023  润新知