目的
在UWP开发中,我们常常用到两个显示列表的控件:ListView和GridView。而这两个列表控件在PC等大屏幕上如果能多列“智能”调整自己的大小(通常是根据当前窗口大小调整宽度),那么用户就会在同一屏幕内接收到更多信息,同时空间的利用率得以提高,也会提高应用的视觉体验。这是我的第一篇博客,向大家分享一下列表项宽度自适应的实现。
实现思路及方法
我们最初的想法可能是,定义列表项模板,给模板里的Panel起个名字,在窗口的SizeChanged事件里加入调整其宽度的代码。这种想法最朴素,然而在操作中遇到了一些问题:我们不能在后台代码里直接访问DataTemplete里的元素。而且,当同一页面中存在多个列表时,给大家都分配一个名字也比较傻,而且后台代码会堆积,不利于维护。
这个时候,数据绑定就可以大显身手了。
我们可以在Xaml里定义一个随便什么元素(当然,它只起到一个“桥”的作用,不能影响当前布局。(废话))这里,我们定义一个Border即可:
1 <Border x:Name="width"/>
然后,创建一个我们想要实现自适应的列表,注意其中的绑定
1 <GridView x:Name="testlist"> 2 <GridView.ItemTemplate> 3 <DataTemplate> 4 <Grid Width="{Binding ElementName=width,Path=Width}"> 5 ...... 6 </Grid> 7 </DataTemplate> 8 </GridView.ItemTemplate> 9 </GridView>
接下来我们所要做的只有一步:在后台的SizeChanged事件中加入对width宽度的调整即可。这里为了方便,我们把它写成一个方法,可以放在自己App的Helper类里供应用内所有列表调用。这个方法长成这个样子:
1 class WidthFit 2 { 3 /// <summary> 4 /// 获取自适应列表项宽度 5 /// </summary> 6 /// <param name="width">当前窗口宽度</param> 7 /// <param name="max">列表项最大宽度</param> 8 /// <param name="min">列表项最小宽度</param> 9 /// <param name="offset">偏移量</param> 10 /// <returns></returns> 11 public static double GetWidth(double width, int max, int min, int offset = 8) 12 { 13 if (offset < 0 || offset > 12) 14 { 15 offset = 8; 16 } 17 double w = 1; 18 int column = 1; 19 int maxcolumn = (int)width / min; 20 double i2 = width / min; 21 for (int i = 1; i <= maxcolumn; i++) 22 { 23 if (Math.Abs(i - i2) < 1) 24 { 25 column = (int)Math.Truncate(i2) == 0 ? 1 : (int)Math.Truncate(i2); 26 } 27 } 28 w = width / column; 29 w -= offset * column; 30 return w; 31 } 32 }
代码很易读,在这里不做过多说明。只说明一下,offset这个参数用来设定宽度的偏移量,因为我们的列表项之间、列表与父面板间通常会有间距,这个间距也要被考虑到,否则实际显示的列数可能会减少,很不美观。
这样,我们可以方便地调用此方法:
1 private void Page_SizeChanged(object sender, SizeChangedEventArgs e) 2 { 3 width.Width = WidthFit.GetWidth(ActualWidth, 600, 300); 4 }
实现的效果就不贴出来了,大家可以动手试一试。
还有什么没提到……
ListView能实现这种效果吗?能。具体做法也很简单,更改一下ListView默认的ItemPanel即可,余下的工作与GridView完全一样。
1 <ListView.Style> 2 <Style TargetType="ListView"> 3 <Setter Property="ItemsPanel"> 4 <Setter.Value> 5 <ItemsPanelTemplate> 6 <ItemsWrapGrid Orientation="Horizontal"/> 7 </ItemsPanelTemplate> 8 </Setter.Value> 9 </Setter> 10 </Style> 11 </ListView.Style>
到了要说“但是”的时候了。上面提到的GetWidth方法有一个小缺陷:可能会造成看上去像是“显示的列数受到损失”的情况(当然啦,这个概率很小)。我们发现,这个Bug出现在列数发生变动的临界值附近。而原因其实也很简单,此时我们定义的最大宽度小于当前窗口宽度与计算出的列数的比,因而无法铺满窗口的宽度,看上去就像是少了一列一样。这个小缺陷当然也易于修正,大家可以在GetWidth方法里做点文章,怠惰一点的话,也可以直接使最大宽度设置得大一点。
明明快到考期了,可我还是“死猪不怕开水烫,越到考期我越浪”,强行水了我的第一篇博客。如果有什么错误与不周到的地方还望大佬们指正。我去补作业了……