【UWP】列表项宽度自适应的实现

目的

在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方法里做点文章,怠惰一点的话,也可以直接使最大宽度设置得大一点。

 

明明快到考期了,可我还是“死猪不怕开水烫,越到考期我越浪”,强行水了我的第一篇博客。如果有什么错误与不周到的地方还望大佬们指正。我去补作业了……

posted @ 2016-12-10 20:36  一只菜鸡  阅读(2579)  评论(0编辑  收藏  举报