Xamarin.Forms自定义GridView

Xamarin.Forms自定义GridView

在开发中,咱们常常用到以格子的形式来展现咱们的数据,在不少平台的控件中咱们叫作GridView,async

在Xamarin.Forms中没有原生的GridView,这里简单介绍一种利用Xamarin.Forms中的Grid来实现ide

GridView的方法。ui

原理就是对Grid动态添加RowDefinition和ColumnDefinition。spa

 

代码以下:code

  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 using System.Diagnostics;
  5 using System.Linq;
  6 using System.Text;
  7 using System.Threading.Tasks;
  8 using System.Windows.Input;
  9 using Xamarin.Forms;
 10 using Xamarin.Forms.Xaml;
 11 
 12 namespace XFPractice.CustomView
 13 {
 14     [XamlCompilation(XamlCompilationOptions.Compile)]
 15     public partial class GridView : Grid
 16     {
 17         
 18         public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(GridView));
 19         public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),typeof(ICommand), typeof(GridView));
 20         public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate),typeof(DataTemplate),typeof(GridView),null);
 21         
 22         public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(GridView), default(IEnumerable<object>), propertyChanged: OnItemsSourcePropertyChanged);
 23         
 24 
 25         private int _maxColumns = 2;
 26         private float _tileHeight = 100;
 27 
 28         public GridView()
 29         {
 30             InitializeComponent();
 31             for (var i = 0; i < MaxColumns; i++)
 32             {
 33                 ColumnDefinitions.Add(new ColumnDefinition());
 34             }
 35         }
 36 
 37 
 38 
 39         public static void OnItemsSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
 40         {
 41             ((GridView)bindable).UpdateTiles();
 42         }
 43 
 44 
 45 
 46         public DataTemplate ItemTemplate
 47         {
 48             get { return (DataTemplate)GetValue(ItemTemplateProperty); }
 49             set { SetValue(ItemTemplateProperty, value); }
 50         }
 51         
 52         public IEnumerable ItemsSource
 53         {
 54             get { return (IEnumerable)GetValue(ItemsSourceProperty); }
 55             set {SetValue(ItemsSourceProperty, value);}
 56         }
 57 
 58         public int MaxColumns
 59         {
 60             get { return _maxColumns; }
 61             set { _maxColumns = value; }
 62         }
 63         
 64         public float TileHeight
 65         {
 66             get { return _tileHeight; }
 67             set { _tileHeight = value; }
 68         }
 69 
 70         public object CommandParameter
 71         {
 72             get { return GetValue(CommandParameterProperty); }
 73             set { SetValue(CommandParameterProperty, value); }
 74         }
 75         
 76         public ICommand Command
 77         {
 78             get { return (ICommand)GetValue(CommandProperty); }
 79             set { SetValue(CommandProperty, value); }
 80         }
 81         
 82         public async Task BuildTiles(IEnumerable tiles)
 83         {
 84             // Wipe out the previous row definitions if they're there.
 85             if (RowDefinitions.Any())
 86             {
 87                 RowDefinitions.Clear();
 88             }
 89             Children.Clear();
 90             if (tiles == null)
 91             {
 92                 return;
 93             }
 94             var enumerable = tiles as IList ?? tiles.Cast<object>().ToArray();
 95             var numberOfRows = Math.Ceiling(enumerable.Count / (float)MaxColumns);
 96             HeightRequest = TileHeight * numberOfRows;
 97             InvalidateLayout();
 98             for (var i = 0; i < numberOfRows; i++)
 99             {
100                 RowDefinitions.Add(new RowDefinition { Height = TileHeight });
101             }
102             int ItemCount = enumerable.Count;
103             int size = (int)numberOfRows * MaxColumns;
104             
105             for (var index = 0; index < size; index++)
106             {
107                 var column = index % MaxColumns;
108                 var row = (int)Math.Floor(index / (float)MaxColumns);
109                 if (index  < ItemCount)
110                 {
111                     var tile = await BuildTile(enumerable[index]);
112                     Children.Add(tile, column, row);
113                 }
114                 else
115                 {
116                     var tile = await BuildEmptyTile();
117                     Children.Add(tile, column,row);
118                 }
119             }
120         }
121         
122         public async void UpdateTiles()
123         {
124             await BuildTiles(ItemsSource);
125         }
126 
127         private async Task<Xamarin.Forms.View> BuildEmptyTile()
128         {
129             return await Task.Run(() =>
130             {
131                 var content = ItemTemplate?.CreateContent();
132                 if (!(content is Xamarin.Forms.View) && !(content is ViewCell)) throw new Exception(content.GetType().ToString());
133                 var buildTile = (content is Xamarin.Forms.View) ? content as Xamarin.Forms.View : ((ViewCell)content).View;
134                 return buildTile;
135             });
136 
137         }
138 
139         private async Task<Xamarin.Forms.View> BuildTile(object item)
140         {
141             return await Task.Run(() =>
142             {
143                 var content = ItemTemplate?.CreateContent();
144                 if (!(content is Xamarin.Forms.View) && !(content is ViewCell)) throw new Exception(content.GetType().ToString());
145                 var buildTile = (content is Xamarin.Forms.View) ? content as Xamarin.Forms.View : ((ViewCell)content).View;
146                 buildTile.BindingContext = item;
147                 var tapGestureRecognizer = new TapGestureRecognizer
148                 {
149                     Command = Command,
150                     CommandParameter = item
151                 };
152                 buildTile.GestureRecognizers.Add(tapGestureRecognizer);
153                 return buildTile;
154             });
155 
156         }
157 
158     }
159 }
View Code

 

使用以下:orm

<controls:GridView
                        x:Name="mGridView"
                        HorizontalOptions="FillAndExpand"
                        VerticalOptions="FillAndExpand"
                        TileHeight="48"
                        MaxColumns ="5"
                        ItemsSource="{Binding UserInfoList,Mode=TwoWay}">
                        <controls:GridView.ItemTemplate>
                            <DataTemplate>
                                <Grid Padding="8" HeightRequest="48" WidthRequest="48" HorizontalOptions="Center">
                                    <Image Source="{Binding HeadImage}" HeightRequest="32" WidthRequest="32" Aspect="AspectFill"/>
                                </Grid>
                            </DataTemplate>
                        </controls:GridView.ItemTemplate>
                    </controls:GridView>
View Code

 

这段代码有一个问题,ItemsSource绑定ListSource后,当ListSource发生增长减小不会触发GridView的刷新,对象

以后将ListSource从新new一个对象后,才会触发propertyChanged从而刷新GridView。blog