zoukankan      html  css  js  c++  java
  • UWP FillRowViewPanel

    最近有童鞋有这种需求,说实话我不知道这个Panel怎么起名字。

    效果连接https://tuchong.com/tags/风光/

    下面是我做成的效果,可以规定每个Row的Items个数

    2个

    3个

    4个

    代码在:GitHub

    下面我来说一下我的思路

    其实很早之前就写过这种可变大小的控件,但这次的跟这个需求有点变化,这个每一行个数一定,大小根据图片的大小进行填充。

    微软默认的VariableSizedWrapGrid和Toolikt里面的StaggeredPanel都会导致ListView失去一些特性(虚拟化,增量加载)

    之前做另一种。其实现在这个差不多。就是ListViewItem 就是每一行,那么每一行里面相当于一个水平的ListView。

    我只需要做一个Panel来布局填充行就可以了。。垂直上面还是ListView自带的效果

    除此之后,还需要一个数据源来把 一维的数据改为了 二维的(根据每一行的个数)

    直接上代码:

    FillRowViewSource这个类是把一个 一维的数据源改为了 二维的。主要方法是UpdateRowItems根据RowItemsCount把集合分割

            private void UpdateRowItems()
            {
                if (sourceList == null)
                {
                    return;
                }
                int i = 0;
                var rowItems = sourceList.Skip(i * RowItemsCount).Take(RowItemsCount);
                while (rowItems != null && rowItems.Count() != 0)
                {
                    var rowItemsCount = rowItems.Count();
                    var item = this.ElementAtOrDefault(i);
                    if (item == null)
                    {
                        item = new ObservableCollection<T>();
                        this.Insert(i, item);
                    }
    
                    for (int j = 0; j < rowItemsCount; j++)
                    {
                        var rowItem = rowItems.ElementAt(j);
                        var temp = item.ElementAtOrDefault(j);
                        if (temp==null || !temp.Equals(rowItem))
                        {
                            item.Insert(j, rowItem);
                        }
                    }
    
                    while (item.Count > rowItemsCount)
                    {
                        item.RemoveAt(item.Count - 1);
                    }
                    i++;
                    rowItems = sourceList.Skip(i * RowItemsCount).Take(RowItemsCount);
                }
    
                var rowCount = sourceList.Count / RowItemsCount + 1;
                while (this.Count > rowCount)
                {
                    this.RemoveAt(this.Count - 1);
                }
    
            }
    View Code

    FillRowViewPanel 最早的时候希望使用item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));方式来获得每个元素的大小,但是像Image这些控件不是开始就有大小的,需要等待图片加载完毕,而且每个Item都进行Measure的话。性能也不佳。

    最后还是按照老控件的方式IResizable 写了个接口。绑定的数据源对象必须继承这个。你需要告诉我你的每个Item的大小尺寸。这样计算起来就方便多了而且有效多了

     public class FillRowViewPanel : Panel
        {
            public int MinRowItemsCount
            {
                get { return (int)GetValue(MinRowItemsCountProperty); }
                set { SetValue(MinRowItemsCountProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for MinRowItemsCount.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty MinRowItemsCountProperty =
                DependencyProperty.Register("MinRowItemsCount", typeof(int), typeof(FillRowViewPanel), new PropertyMetadata(0));
    
    
    
            protected override Size MeasureOverride(Size availableSize)
            {
                var size = base.MeasureOverride(availableSize);
                return size;
            }
    
            protected override Size ArrangeOverride(Size finalSize)
            {
                double childrenWidth = 0;
                //double maxheight = double.MinValue;
                foreach (var item in Children)
                {
                    if (item is ContentControl cc && cc.Content is IResizable iResizable)
                    {
                        //item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                        var elementSize = iResizable;
                        var width = elementSize.Width * finalSize.Height / elementSize.Height;
                        //maxheight = Math.Max(elementSize.Height, maxheight);
                        childrenWidth += width;
                    }
                }
    
                double ratio = childrenWidth / finalSize.Width;
                double x = 0;
                var count = Children.Count;
                foreach (var item in Children)
                {
                    if (item is ContentControl cc && cc.Content is IResizable iResizable)
                    {
                        var elementSize = iResizable;
                        var width = elementSize.Width * finalSize.Height / elementSize.Height;
                        //if children count is less than MinRowItemsCount and chidren total width less than finalwidth
                        //it don't need to stretch children
                        if (count < MinRowItemsCount && ratio < 1)
                        {
                            //to nothing
                        }
                        else
                        {
                            width /= ratio;
                        }
    
                        var rect = new Rect(x, 0, width, finalSize.Height);
                        item.Measure(new Size(rect.Width, finalSize.Height));
                        item.Arrange(rect);
                        x += width;
                    }
    
                }
                return base.ArrangeOverride(finalSize);
            }
        }
    View Code

    在Sample页面

    原理就是Listview的Item其实是个ListView。而做为Item的ListView的Panel是FillRowViewPanel

    记住给FillRowViewPanel的高度进行设置。相当于每个元素的高度

            <ct:PullToRefreshGrid RefreshThreshold="100"  PullToRefresh="PullToRefreshGrid_PullToRefresh">
                <ListView x:Name="FillRowView" SizeChanged="FillRowView_SizeChanged">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="ListViewItem">
                            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                            <Setter Property="Margin" Value="0"/>
                            <Setter Property="Padding" Value="0"/>
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ListView  ScrollViewer.HorizontalScrollMode="Disabled" ItemsSource="{Binding}">
                                <ListView.ItemContainerStyle>
                                    <Style TargetType="ListViewItem">
                                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                                        <Setter Property="VerticalContentAlignment" Value="Stretch"/>
                                        <Setter Property="Margin" Value="0"/>
                                        <Setter Property="Padding" Value="0"/>
                                    </Style>
                                </ListView.ItemContainerStyle>
                                <ListView.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <ct:FillRowViewPanel x:Name="fillRowViewPanel" MinRowItemsCount="2" Height="300"/>
                                    </ItemsPanelTemplate>
                                </ListView.ItemsPanel>
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <!--<msct:ImageEx Margin="5" IsCacheEnabled="True" Source="{Binding ImageUrl}" Stretch="Fill"/>-->
                                        <Image Margin="5" Source="{Binding ImageUrl}" Stretch="Fill"/>
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </ct:PullToRefreshGrid>
    View Code

    在整个ListViewSizechanged的时候我们再根据自己的需求调整RowItemsCount。

             private void FillRowView_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                if (e.NewSize.Width < 600)
                {
                    source.UpdateRowItemsCount(2);
                }
                else if (e.NewSize.Width >= 600 && e.NewSize.Width < 900)
                {
                    source.UpdateRowItemsCount(3);
                }
                else
                {
                    source.UpdateRowItemsCount(4);
                }
            }
    View Code

    整个就差不多这样了。做的比较简单。一些东西也没有全部去考虑。比如SourceList_CollectionChanged的时候。只考虑了Reset这种方式。

    算是比较针对这个需求做的东东吧,如果有其他需求。可以提出来。大家一起撸撸。

    老规矩 开源有益:Github

  • 相关阅读:
    Leetcode 171. Excel Sheet Column Number
    Leetcode 206 Reverse Linked List
    Leetcode 147. Insertion Sort List
    小明一家人过桥
    Leetcode 125. Valid Palindrome
    Leetcode 237. Delete Node in a Linked List
    Leetcode 167 Two Sum II
    张老师的生日
    Leetcode 27. Remove Element
    Leetcode 283. Move Zeroes
  • 原文地址:https://www.cnblogs.com/FaDeKongJian/p/9376569.html
Copyright © 2011-2022 走看看