UWP FillRowViewPanel

来源:https://www.cnblogs.com/FaDeKongJian/archive/2018/07/27/9376569.html
-Advertisement-
Play Games

最近有童鞋有這種需求,說實話我不知道這個Panel怎麼起名字。 效果連接https://tuchong.com/tags/風光/ 下麵是我做成的效果,可以規定每個Row的Items個數 2個 3個 4個 代碼在:GitHub 下麵我來說一下我的思路 其實很早之前就寫過這種可變大小的控制項,但這次的跟這 ...


最近有童鞋有這種需求,說實話我不知道這個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 as FrameworkElement).Width = rect.Width;
                    (item as FrameworkElement).Height = 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

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • JAVA方法調用中的解析與分派 本文算是《深入理解JVM》的讀書筆記,參考書中的相關代碼示例,從位元組碼指令角度看看解析與分派的區別。 方法調用,其實就是要回答一個問題:JVM在執行一個方法的時候,它是如何找到這個方法的? 找一個方法,就需要知道 所謂的 地址。這個地址,從不同的層次看,對它的稱呼也不 ...
  • 題目描述: In a 2 dimensional array grid, each value grid[i][j] represents the height of a building located there. We are allowed to increase the height of ...
  • TIOBE 7月編程語言指數排行榜 TIOBE 7月編程語言指數排行榜 TIOBE編程社區指數是流行編程語言的一個指標,可以作為編程語言是選擇依據。索引每月更新一次。該評級是基於世界各地熟練工程師的數量,課程和第三方供應商。通過上表,我們可以很明顯的看到Python語言的排名呈現出上升的趨勢,相信小 ...
  • ...
  • 1.今天在寫12306查詢餘票時,想給定字典(dict)的值,從而得到字典(dict)的鍵,但好像字典(dict)方法中沒有與此相關的方法,只能退而求其次,反轉字典(dict),將原字典(dict)的鍵作為值,值值作為鍵。 下附12306的車站與其對應的簡稱: {'北京北': 'VAP', '北京東 ...
  • 眾所周知,電腦底層是二進位。而java作為一門電腦編程語言,也對二進位的位運算提供了完整的支持。 在java中,int是32位的,也就是說可以用來實現32位的位運算。方便起見,我們一般用16進位對它賦值,比如: 0011表示成16進位是 0x3, 110111表示成16進位是 0x37。 那麼什 ...
  • 最近把我之前學SpringCloud所涉及到的知識以及我寫的博客進行了比較系統的整理,目錄如下。 1. 單體架構和微服務架構的比較 2.微服務所要解決的主要問題 3.SOA和微服務的比較 初識微服務 4. 服務拆分 微服務的服務拆分 5. 數據一致性 微服務的數據一致性 6.服務間的通信—RestT ...
  • Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have ex ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...