博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
博客园客户端UAP开发随笔-从9个细节说ListView的使用
阅读量:6260 次
发布时间:2019-06-22

本文共 11402 字,大约阅读时间需要 38 分钟。

前言

ListView应该算是在WP开发中最常用的一个显示控件了,在我们的项目中,也大量的使用了ListView。很多WP上的开发者肯定也是如此。但是ListView有很多你可能没用到的功能。这篇博客主要是结合项目中遇到的问题,从9个细节之处来介绍下ListView的全面使用。

基本

首先定义好我们准备使用的实体类,之后的代码中将一直用到这些。

下面是MVVM中常用到的简单基类,用于让UI响应model的变化(虽然这个例子没用到,但是如果有兴趣的话,可以自己动手看看效果)

public abstract class Base : INotifyPropertyChanged    {        public event PropertyChangedEventHandler PropertyChanged;        // 使用CallerMemberNameAttribute可以获得调用这个方法的成员名称,对于属性的set来说,就是属性名        public void NotifyChange([CallerMemberName]string property = null)        {            if (this.PropertyChanged != null)            {                PropertyChanged(this, new PropertyChangedEventArgs(property));            }        }    }

用来在ListView中显示的Item,我们假设Time属性在应用中是不会改变的。

public class Item : Base    {        private string _title = string.Empty;        public string Title        {            get { return _title; }            set            {                _title = value;                NotifyChange();            }        }        private string _content = string.Empty;        public string Content        {            get { return _content; }            set            {                _content = value;                NotifyChange();            }        }        public string Time        {            get;            set;        }    }

再来个Helper用来生成测试数据。

public static class DataHelper    {        public static ObservableCollection
CreateItems() { var collection = new ObservableCollection
(); for (var i = 0; i < 10; i++) { collection.Add(new Item { Title = "Title " + i.ToString(), Content = "Content " + i.ToString(), Time = DateTime.Now.ToString() }); } return collection; } }

这里为了简单,直接把数据赋值给了页面的上下文(DataContext),这样在XAML中直接使用{Binding}即可绑定到当前的上下文。

public sealed partial class MainPage : Page    {        public MainPage()        {            this.InitializeComponent();            this.NavigationCacheMode = NavigationCacheMode.Required;            this.DataContext = DataHelper.CreateItems();        }    }

这样一个简单ListView完成了,但是可以看到,默认的项目样式有点丑,这个可以根据需要美化下ItemTemplate即可。。

对齐

我们的ListView现在所有的项目内容都是默认左对齐的,那么如果想要像博客园UAP那样,把一部分内容放在右边怎么办呢?

 

在XAML中,控件有两种对齐方式,HorizontalAlignment(水平对齐)和VerticalAlignment(垂直对齐),显然我们现在需要的是水平对齐。然后利用Grid,将之分成2行,第2行用来显示时间,并且设置TextBlock的HorizontalAlignment为Right就可以了,这样我们的项目模板就变成了:

但是运行的效果和想象的不一样啊,时间也没有靠右侧显示啊(我给每个项目都加了边框,看起来明显些)。

这是因为ListView的项目(也就是ListViewItem)的宽度和内容是一样的,所以看不来效果。我们把项目的宽度设置成和ListView一样的就可以了。

在Page.Resources内部修改ListViewItem的样式:

这次就对了。

多选

ListView默认是单选的,需要修改SelectionMode属性来启用多选的支持,这样每个项目左侧都会出现一个复选框。

现在ListView就变成下面这个样子了:

ScrollViewer的获得及应用

如果你用过我们的应用的话,你在某个作者的文章列表页面会发现:随着你用手向上滑动,页面的标题会变成作者的头像和昵称,方便用户识别当前在看谁的博客列表,那么这个功能是怎么实现的呢?

滚动前:

滚动后:

其实这个功能是通过判断ListView的ScrollViewer的滚动方向和距离来实现的。

首先我们需要找到ListView上的ScrollViewer控件,这个控件不是显式的在XAML中定义的,我们需要在VirtualTree上来查找。

下面这是个通用的查找方法。

public static ScrollViewer GetScrollViewer(Windows.UI.Xaml.DependencyObject depObj)        {            if (depObj is ScrollViewer)            {                return depObj as ScrollViewer;            }            for (int i = 0; i < Windows.UI.Xaml.Media.VisualTreeHelper.GetChildrenCount(depObj); i++)            {                var child = Windows.UI.Xaml.Media.VisualTreeHelper.GetChild(depObj, i);                var result = GetScrollViewer(child);                if (result != null) return result;            }            return null;        }

然后我们在ListView加载完成之后(这里一定要是加载完成之后,否则你得到可能是个null),查找这个ScrollViewer,然后添加ViewChanged事件(当滑动时触发),并在事件内对滑动的距离和方向进行判断

private void ListView_Loaded(object sender, RoutedEventArgs e)        {           this.scrollViewer = GetScrollViewer(this.ListView);           this.scrollViewer.ViewChanged += scrollViewer_ViewChanged;        }        void scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)        {            //VerticalOffset大于0表示向上滑动            if (this.scrollViewer.VerticalOffset > 50)            {                if (!isAuthorShowOnTitle)                {                //执行动画                    this.sb_AuthorMoveUp.Begin();                }            }            else            {                if (isAuthorShowOnTitle)                {               // 执行动画                    this.sb_AuthorMoveDown.Begin();                }            }        }

显示多列

现在我们的ListView一直都是只显示一列,那么怎么样实现下图的效果呢?可以不用GridView么?

使用ListView实现这个功能,需要自定义首先定义ItemsPanel模板(ItemsPanelTemplate),通过来实现的,WrapGrid是按从左到右或从上到下的顺序对子元素进行定位,而这个布局的功能实际上就是GridView的,再修改MaximumRowsOrColumns来定义能够显示的最多列/行,这样我们的ListView就实现了最多两列的效果。

效果如下:

Header和Footer

很多使用ListView的人可能没有注意到,其实ListView还有Header和Footer,同时也都支持自定义模板。

在博客园UAP中,我们使用Header来作为页面的副标题,Footer则可以用来作为增量加载时的提示,比如”加载中。。“,”没有更多了。。“。

Header和Footer的使用很简单,和ItemTemplate一样,只要定义好对应的模板就可以了。

效果如下图。

分组

当我第一次使用Windows phone时,觉得应用列表页面向上滑动时的效果很cool(如下图,当没有更多以b开头的应用之后,手指再向上滑动,c会慢慢把b顶上去),要在Windows phone上实现这一效果,只需要使用ListView显示分组数据就可以了,动画效果是自带的。

先定义一个简单的分组类:

public class Group : Base    {        private string _name = string.Empty;        public string Name        {            get { return _name; }            set            {                _name = value;                this.NotifyChange();            }        }        public ObservableCollection
Items { get; private set; } public Group() { this.Items = new ObservableCollection
(); } }

然后再DataHelper中添加一个生成分组数据的方法(请无视循环中可能存在的性能问题-_-)。

public static ObservableCollection
CreateGroups() { var groups = new ObservableCollection
(); for (var i = 0; i < 13; i++) { var group = new Group { Name = "Group " + i.ToString() }; for (var j = 0; j < 10; j++) { var item = new Item { Time = DateTime.Now.ToString(), Title = "Title " + j.ToString(), Content = "Content" + j.ToString() }; group.Items.Add(item); } groups.Add(group); } return groups; }

在页面上,ListView的ItemsSource和之前的有点不一样了,我们需要告诉ListView该怎么显示数据,每个分组中项目列表是哪个属性。这时候我们需要定义一个数据集视图(CollectionView),具体请看下面代码里的注释。

这样一个简单的分组功能就实现了

开始的时候:

被推上去了:

但是遗憾的是,ListView分组显示之后,就不能通过ISupportIncrementalLoading来实现增量加载了(增量加载组?)。

SemanticZoom控件

前面提到了WP的应用列表界面的分组显示,那么这个页面的另一个更cool的效果就是点击任意分组的Header之后,会显示一个缩小的索引视图,这个就是SemanticZoom控件的效果。

这个控件实际上是通过控制内部的两个ListView/GridView来实现这种效果的,一个显示缩小的索引视图(ZoomOutView),另一个显示具体的分组列表(ZoomInView)。前面我们已经实现了分组列表,这样我们只需要再用GridView实现个缩小的视图,然后放在SemanticZoom空间内部就完成了。

实现一个只显示Group.Name的Grid很简单。这里需要注意的是,我们直接把分组集合绑定到了GridView.ItemsSource,这样对于每个GridViewItem而言,其上下文就变成了Group,而不是Item,所以我们在TextBlock中绑定的是Group.Name。

显示如下,效果差不多-_-..

 

现在两个视图都有了,是时候放在SemanticZoom控件里了。现在把ListView放在ZoomInView用来显示详细信息,把GridView放在ZoomOutView显示缩略信息。

这时候你如果运行程序,默认显示的ListView,当你点击分组的Header后,GridView会自动弹出来了,这样一个简单SemanticZoom就是实现了,切换工作都是系统帮忙实现的。

更新项目

在博客园UAP这个应用中,在博客列表页面上,如果点击文章标题的话,会运行自定义动画把博客的summary隐藏起来,并显示“朕无视”来表示忽略此文章。虽然我们可以使用绑定状态来隐藏/显示控件,但是这样却不能执行自定义动画,所以我们是在ListView每个项目的DataContextChanged事件和OnApplyTemplate事件中进行状态判断的,其中每个项目都是一个自定义控件,在控件中判断当前绑定数据的状态来执行对应的逻辑。

下面这个PostControl自定义控件在两个事件中通过GetTemplateChild得到子控件,然后对子控件进行对应的设置,如动画,是否显示等。

public sealed class PostControl : Control    {        public PostControl()        {            this.DefaultStyleKey = typeof(PostControl);            this.DataContextChanged += PostControl_DataContextChanged;        }        void PostControl_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)        {            this.UpdateUI(false);        }        protected override void OnApplyTemplate()        {            this.UpdateUI(false);        }private void UpdateUI(bool showAnimation = true)        {        //更新逻辑        var tbSummary = this.GetTemplateChild("tb_Summary") as TextBlock;      }  }

这里需要注意,一定在这两个事件中都要进行更新,因为有的时候,其中某个事件还得不到子控件,这个暂时还不知道原因,可能和调用的顺序有关吧。

 

 

分享代码,改变世界!

Windows Phone Store App link:

Windows Store App link:

GitHub open source link:

MSDN Sample Code:

转载地址:http://cnqsa.baihongyu.com/

你可能感兴趣的文章
雏形已具?2018年物联网智能市场研究报告
查看>>
陕西破获特大捕杀濒危野生动物案 设置“高压线”电杀猎物
查看>>
“办事不求人”破天荒写入黑龙江省政府工作报告
查看>>
Python文件操作的20个面试题,帮你打开公司大门,值得收藏
查看>>
2018年将是区块链商用化元年
查看>>
自然语言处理时,通常的文本清理流程是什么?
查看>>
最靠谱的《数据分析师》成长指南!真实数据库、2年销售数据、50h的训练学习……...
查看>>
可能是最好的正则表达式的教程笔记了吧...
查看>>
实战react技术栈+express前后端博客项目(5)-- 前后端实现登录功能
查看>>
MySQL 前缀索引——让索引减负狂奔
查看>>
程序开发者,为什么要和聪明人一起工作?
查看>>
chrome使用技巧(看了定不让你失望)
查看>>
LSAnimator - 易于读写的多链式动画框架
查看>>
有赞透明多级缓存解决方案(TMC)
查看>>
Kotlin:娶妻当娶贤,嫁夫则嫁能
查看>>
设计模式初探之建造者模式(Builder)
查看>>
菜鸟学网络之 —— 长连接和短连接
查看>>
DDFE 技术周刊(第十八期)2017.3.14
查看>>
安得广厦千万间,大赚天下寒士俱欢颜
查看>>
这是一份优美的信息图,吴恩达点赞的deeplearning.ai课程总结
查看>>