UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(二)

上篇UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(一) 讲到该控件的需要和设计过程。

这篇讲讲开发过程中一些重要问题解决。

1.支持ISupportIncrementalLoading,实现HasMoreItems属性和LoadMoreItemsAsync方法

因为我们上篇里面讲过,需要把源数据分成一个一个的Group作为GirdView的源,

所以LoadMoreItemsAsync方法里面我做了以下的实现:

        public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
        {
            IAsyncOperation<LoadMoreItemsResult> result = rowAdapter.LoadMoreItemsAsync(count);
            if (rowAdapter.Count > 0)
            {
                for (int i = this.Count; i < rowAdapter.Count; i++)
                {
                    if (rowAdapter.SourceList.Count / rowAdapter.rowItemsCount > i)
                    {
                        var item = this.ElementAtOrDefault(i);
                        if (item == null)
                        {
                            this.Insert(i, rowAdapter[i]);
                        }
                    }
                }
            }

            return result;
        }

应该还是比较清楚的,当源数据里面的个数超过了RowItemsCount的时候,我们才插入Item。

也就是说。当满15个Item的时候我们才插入第1个Row Item,当满30个Item的时候我们插入第2个Row Item......

可能有人会说,如果源数据不是15个整数,那怎么办呢??嗯,我也遇到了这个问题,当然,HasMoreItems属性也需要作相应的实现

        public bool HasMoreItems
        {
            get
            {
                var hasMoreItems = rowAdapter.HasMoreItems;

                if (!hasMoreItems)
                {
                    if (rowAdapter.Count > 0 && this.Count < rowAdapter.Count)
                    {
                        for (int i = this.Count; i < rowAdapter.Count; i++)
                        {
                            //sometime it will miss some indexs in LoadMoreItemsAsync method,
                            //if hasMoreItems is false, that means not more items,
                            //so at that monment we should add the missed items.
                            //if (rowAdapter.SourceList.Count / rowAdapter.rowItemsCount <= i)
                            {
                                var item = this.ElementAtOrDefault(i);
                                if (item == null)
                                {
                                    this.Insert(i, rowAdapter[i]);
                                }
                            }
                        }
                    }
                }
                return hasMoreItems;
            }
        }

当HasMoreItems为false的时候,就是说这个源不会有更多的数据了,所以这时候我们应该把剩余的Item都加入到下一个Row Item里面去。

2.当Window size 改变的时候,实现不同的可变大小结构。

public class ResizeableItems : List<ResizeableItem>

每一种结构,我用一个ResizeableItem来表示。

比如window 最小的时候这种结构。

                list = new List<Resizable>();
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });

                var c1 = new ResizeableItem() { Columns = 2, Items = list,               Min = windowMinwidth + +1, Max = windowMinwidth + rangwidth * 1 };
                _resizeableItems.Add(c1);

设置GirdView里面的每个Item的宽高比2:1,设置ResizeableItem的最小和最大值,这个意思就是当window 到达MIn和Max的这个区间的时候就使用这个结构。

然后也设置出其它的结构样式。(PS:我这里还没找到Get 最小window 宽度的办法,好像只能设置,如果有办法的朋友请留言一下)

                 _resizeableItems = new ResizeableItems();

                //ApplicationView.GetForCurrentView().SetPreferredMinSize(new Windows.Foundation.Size(200, 200));

                double windowMinwidth = 500;
                double windowMaxwidth = DeviceInfo.DeviceScreenSize.Width;
                double rangwidth = (windowMaxwidth - windowMinwidth) / 4.0;

                #region 4
                var list = new List<Resizable>();
                list.Add(new Resizable() { Width = 2, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });

                var c4 = new ResizeableItem() { Columns = 4, Items = list, Min = windowMinwidth + rangwidth * 3 + 1, Max = double.PositiveInfinity };
                _resizeableItems.Add(c4);
                #endregion

                #region 3
                list = new List<Resizable>();
                list.Add(new Resizable() { Width = 2, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });

                var c3 = new ResizeableItem() { Columns = 3, Items = list, Min = windowMinwidth + rangwidth * 2 + 1, Max = windowMinwidth + rangwidth * 3 };
                _resizeableItems.Add(c3);
                #endregion

                #region 2
                list = new List<Resizable>();
                list.Add(new Resizable() { Width = 2, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 2 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });
                list.Add(new Resizable() { Width = 1, Height = 1 });

                var c2 = new ResizeableItem() { Columns = 2, Items = list, Min = windowMinwidth + rangwidth * 1 + 1, Max = windowMinwidth + rangwidth * 2 };
                _resizeableItems.Add(c2);
                #endregion

                #region 1
                list = new List<Resizable>();
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });
                list.Add(new Resizable() { Width = 2, Height = 1 });

                var c1 = new ResizeableItem() { Columns = 2, Items = list, Min = windowMinwidth + +1, Max = windowMinwidth + rangwidth * 1 };
                _resizeableItems.Add(c1);
                #endregion

这样我们就设置好了,到达多少size的时候使用什么结构模板了。。

最后我们只需要在控件的MeasureOverrid中去设置,GridView的Item的样式就好了.

        protected override Size MeasureOverride(Size availableSize)
        {
            if (!PlatformIndependent.IsWindowsPhoneDevice)
            {
                OnMeasureOverride(availableSize);
            }
            return base.MeasureOverride(availableSize);
        }

         private void OnMeasureOverride(Size availableSize)
        {
            if (ItemsSource != null && ItemsSource is IResizeableItems && availableSize != Size.Empty)
            {
                var resizeableItem = ResizeableItems.GetItem(availableSize.Width);

                if (resizeableItem != null)
                {
                    resizeableItem.ItemWidth = (int)(availableSize.Width / resizeableItem.Columns - 7);

                    foreach (var item in this.Items)
                    {
                        var gridviewItem = this.ContainerFromItem(item) as ListViewItem;
                        //not null, it‘s in viewport, so it need to update.
                        if (gridviewItem != null && gridviewItem.ContentTemplateRoot != null)
                        {
                            var gridview = gridviewItem.ContentTemplateRoot as VariableSizedGridView;
                            gridview.ResizeableItem = null;
                            gridview.ResizeableItem = resizeableItem;

                        }
                    }
                }

            }
        }

3.ListViewItem默认模板的修改
在使用这个的时候因为其实是个ListView,所以当你点击到GridView外面的时候就点击到了ListLViewItem上面,会有些你不想要的效果,比如PointerDown。

第一次用VS拿到ListViewItem的模板的时候发现没地方可能修改这个动画呢???

再查了下MSDN,发现原来,ListViewItem是有2套模板的,

Default style

When the ListView‘s ItemsPanel is an ItemsStackPanel (the default) or ItemsWrapGrid, this template is used to show the data items. This template uses aListViewItemPresenter instead of a UIElement tree to improve grid performance.

这个模板跟我从VS里面拿到的是一样的。

<!-- Default style for Windows.UI.Xaml.Controls.ListViewItem -->
<Style TargetType="ListViewItem">
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="TabNavigation" Value="Local"/>
    <Setter Property="IsHoldingEnabled" Value="True"/>
    <Setter Property="Margin" Value="0,0,18,2"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListViewItem">
                <ListViewItemPresenter
                    ContentTransitions="{TemplateBinding ContentTransitions}"
                    Padding="{TemplateBinding Padding}"
                    SelectionCheckMarkVisualEnabled="True"
                    CheckHintBrush="{ThemeResource ListViewItemCheckHintThemeBrush}"
                    CheckSelectingBrush="{ThemeResource ListViewItemCheckSelectingThemeBrush}"
                    CheckBrush="{ThemeResource ListViewItemCheckThemeBrush}"
                    DragBackground="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
                    DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
                    FocusBorderBrush="{ThemeResource ListViewItemFocusBorderThemeBrush}"
                    PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
                    PointerOverBackground="{ThemeResource ListViewItemPointerOverBackgroundThemeBrush}"
                    SelectedBorderThickness="{ThemeResource ListViewItemCompactSelectedBorderThemeThickness}"
                    SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"
                    SelectedForeground="{ThemeResource ListViewItemSelectedForegroundThemeBrush}"
                    SelectedPointerOverBackground="{ThemeResource ListViewItemSelectedPointerOverBackgroundThemeBrush}"
                    SelectedPointerOverBorderBrush="{ThemeResource ListViewItemSelectedPointerOverBorderThemeBrush}"
                    DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
                    DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
                    ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                    PointerOverBackgroundMargin="1"
                    ContentMargin="4" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

When the ListView‘s ItemsPanel is not an ItemsStackPanel (the default) or ItemsWrapGrid, this template is used to show the data items. This template uses aUIElement tree and visual states instead of a ListViewItemPresenter.

这个模板我就不贴了,比较多,MSDN地址

总结:

虽然说这个控件已经满足了Boss的需求,但是我还是觉得有一些需要改进的。

1.UI Virtualized

如果说GridView里面的Item个数被用户设置的很多,这个必定是很占用内存的。

实际Debug,也发现,Live Visual Tree里面有3个ListViewItem.

感觉内存中不需要这么多个ListViewItem 来循环回收利用,我的猜想是微软做了上下各一个Item的缓存来提高Scrolling的流畅。

但对于这个控件来说,内存里面的UI Item 会有3*15个,因为我们知道VariableSizedWrapGrid是不支持UI虚拟化的。

希望有好想法的童鞋能留言,大家讨论共同进步。。

为了部落!

开源有益,GitHub源代码地址

时间: 2024-10-11 13:42:05

UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(二)的相关文章

UWP VirtualizedVariableSizedGridView 支持可虚拟化可变大小Item的View(一)

Boss的需要时这样的,Item是可变大小的,同时根据不同的Window size,来确定Item的结构和大小Window 小的时候是 大的时候是这样的: 当然这size变化的过程中也允许其他结构,我这里只是举了最大和最小时候的样子. 当拿到需求的时候,相信大家肯定第一想到的是,将GirdView的ItemsPanel改成VariableSizedWrapGrid.VariableSizedWrapGrid是怎么样用的,不知道的童鞋点击先行脑补下.官方文档  diederik的sample 嗯,

可变大小结构体如何定义详解

一.背景 工作中,在通信协议中常常看到TLV格式数据,不同的type id对应的字符串长度大小不一样.那么该怎么去定义一个结构体去管理这些数据呢?怎么去定义一种可变大小的结构体?本文将讲解如何定义可变大小结构体. 二.定义可变大小结构体 1.方法一:使用指针 1 typedef struct _S_HB_TIME_REPORT_INFO 2 { 3 uint16_t msg_id; 4 uint16_t msg_buf_len; 5 char *p_msg_buf; 6 }__attribute

NET Framework 4.5新特性 (三)64位平台支持大于2 GB大小的数组

64位平台.NET Framework数组限制不能超过2GB大小.这种限制对于需要使用到大型矩阵和向量计算的工作人员来说,是一个非常大问题. 无论RAM容量有多大有多少,一旦你使用大型矩阵和向量计算工作的时候,经常会抛出一个System.OutOfMemoryException异常,如下图所示: 参考程序 class Program { private static void Main(string[] args) { int arrysize = 150000000; var large=ne

【翻译自mos文章】怎么检查服务器主板BIOS支持的最大内存大小?

怎么检查服务器主板BIOS支持的最大内存大小? 来源于: How to check Maximum Supported Memory by Motherboard Bios (文档 ID 1680534.1) 适用于: Linux OS - Version Oracle Linux 5.0 to Oracle Linux 6.5 [Release OL5 to Ol6U5] Linux x86-64 Linux x86 目标: 获得服务器主板BIOS支持的最大内存大小 解决方案: 简单的dmid

让Proxmox VE支持嵌套虚拟化

目前公司的测试环境使用Proxmox VE(PVE),PVE虚拟出来的主机CPU默认不支持vmx,即不支持嵌套虚拟化,在虚拟机中使用egrep "vmx|svm" /proc/cpuinfo验证,无输出,那么如何让他支持呢?其实PVE的内核还是采用了KVM+Qemu的方式模拟,那么参照如何让KVM支持嵌套虚拟化的方法操作,开启nested即可 nested是一个可通过内核参数来启用的功能.它能够使一台虚拟机具有物理机CPU特性,支持vmx或者svm(AMD)硬件虚拟化.Proxmox

Xamarin.Forms 现已开启对 UWP 的支持

Xamarin.Forms 现已升级到 2.0.0.6482 , 正式开启了对 UWP 的支持. 要创建 UWP 项目, 必须是 VS2015, WIN8.1 下也可以, 但是只有 Windows 10 Mobile 的模拟器可用, Windows 10 的模拟器, 必须在 WIN 10 下. 以下简称 Xamarin.Forms 为 XF, Caliburn.Micro 为 CM 创建 XF支持的 UWP 项目 XF的项目模板, 当前没有加入 UWP , 需要手动创建 UWP 项目. 过程如下

WinSCP登陆服务器提示收到了太大的SFTP包 支持的最大包大小1024000B

前情回顾: 每次用rm都心惊胆战,于是5月7号晚上,找资料把rm替换为mv命令,模拟成了WINDOWS下面的回收站. 组里面有好几个人一起使用服务器,回收站的文件夹就设置到了每个用户起始目录下. 把每个用户的.bashrc修改了一下,一时心好,想提示登录的用户现在可以找回文件: 在每个用户的~/.bashrc中增加了一行: ? 1 echo "using the order ur +filename  to recover your file" 5月8号早上来实验室,大师姐给我说,不能

解决VMware下64位linux系统不支持全虚拟化(vmx)

一.前提: cpu支持Inter VT-X或AMD虚拟化技术,具体参考官网说明. 二.问题描述: 物理机下,vmware workstation能安装64位CentOS系统,处理器Inter i3 M390,且支持Inter VT-X虚拟化技术,物理机BIOS中已打开Inter虚拟化开关(自己找),既然能安装64位系统,说明物理机BIOS中支持虚拟化开关已打开. 半虚拟化为pae 全虚拟化为vmx(Inter).svm(AMD) # uname -r 2.6.32-358.el6.x86_64

intel或amd,vmware开启支持kvm虚拟化

vmware --> 左侧选择具体的计算机 --> 右键选择 设置 --> 处理器 --> 把 虚拟化 Intel VT-x/EPT 或 AMD-V.RVI(V) 勾上 --> 重启虚拟机. [[email protected] ~]# egrep "lm|vmx|svm" /proc/cpuinfo 如果匹配到内容即表示已支持kvm虚拟化. 原文地址:http://blog.51cto.com/12555197/2309687