WPF数据绑定(2 绑定列表数据Binding to List Data)
(读完此系列WPF和Silverlight的数据绑定问题你就轻松搞定 ) 1 Binding to List Data 前面都是绑定到一个对象,下面我们学习绑定到对象列表的方法。 我们还是先组织要绑定的数据,对象所对应的类还是Person,但新增了一个新类People,该类用来组织Person的列
(读完此系列WPF和Silverlight的数据绑定问题你就轻松搞定)
1 Binding to List Data
前面都是绑定到一个对象,下面我们学习绑定到对象列表的方法。
我们还是先组织要绑定的数据,对象所对应的类还是Person,但新增了一个新类People,该类用来组织Person的列表.代码如下:
<span>using </span>System; <span>using </span>System.Collections.Generic; <span>using </span>System.ComponentModel;<span>//INotifyPropertyChanged </span><span>namespace </span>SimpleDataBinding { <span>class </span><span>Person </span>: <span>INotifyPropertyChanged </span>{ <span>public event </span><span>PropertyChangedEventHandler </span>PropertyChanged; <span>protected void </span>Notify(<span>string </span>PropName) { <span>if </span>(<span>this</span>.PropertyChanged != <span>null</span>) { PropertyChanged(<span>this</span>, <span>new </span><span>PropertyChangedEventArgs</span>(PropName)); } } <span>public </span>Person() { _Age = 0; _name = <span>"Null"</span>; <span>this</span>.CurrentDate = <span>DateTime</span>.Now; } <span>private string </span>_name; <span>public string </span>Name { <span>get </span>{ <span>return </span>_name; } <span>set </span>{ <span>if </span>(<span>value </span>== _name) { <span>return</span>; } _name = <span>value</span>;<span>//注意:不能用this.Name来赋值,如果这样形成循环调用,栈溢出 </span>Notify(<span>"Name"</span>); } } <span>private int </span>_Age; <span>public int </span>Age { <span>get </span>{ <span>return </span>_Age; } <span>set </span>{ <span>if </span>(<span>value </span>== _Age) <span>return</span>; _Age = <span>value</span>; Notify(<span>"Age"</span>); } } <span>public </span><span>DateTime </span>CurrentDate { <span>get</span>; <span>set</span>; } } <strong><span><span>//People类 </span><span>class </span><span>People </span>: <span>List</span>Person</span>> { }</strong> } 登录后复制 |
注意在同一命名空间下的代码最后添加了Perople类。
我们在UI里显示的XAML如下:
<span><span>Window </span><span>x</span><span>:</span><span>Class</span><span>="ListDataBinding.BindListDataTest" </span><span>xmlns</span><span>="http://schemas.microsoft.com/winfx/2006/xaml/presentation" </span><span>xmlns</span><span>:</span><span>x</span><span>="http://schemas.microsoft.com/winfx/2006/xaml" <u> </u></span><strong><u><span><span>xmlns</span><span>:</span><span>src</span></span></u></strong><span><strong><u><span>="clr-namespace:ListDataBinding"</span></u></strong> </span><span>Title</span><span>="BindListDataTest" </span><span>Height</span><span>="113" </span><span>Width</span><span>="300"> <span>Window.Resources</span><span>> <span> <strong></strong></span></span><span><strong><span>src</span><span>:</span><span>People </span><span>x</span><span>:</span><span>Key</span></strong></span><span><span><strong>="Family"></strong></span> <span>src</span><span>:</span><span>Person </span><span>Name</span><span>="Jack" </span><span>Age</span><span>="18"/> <span>src</span><span>:</span><span>Person </span><span>Name</span><span>="Tom" </span><span>Age</span><span>="30"/> <span>src</span><span>:</span><span>Person </span><span>Name</span><span>="Jone" </span><span>Age</span><span>="14"/> <span>src</span><span>:</span><span>Person </span><span>Name</span><span>="Rose" </span><span>Age</span><span>="17"/> <span>src</span><span>:</span><span>Person </span><span>Name</span><span>="Mike" </span><span>Age</span><span>="13"/> </span><span>src</span><span>:</span><span>People</span><span>> </span><span>Window.Resources</span><span>> <span>Grid </span><span>DataContext</span><span>="{</span><span>StaticResource </span><span>Family</span><span>}"> <span>Grid.RowDefinitions</span><span>> <span>RowDefinition</span><span>/> <span>RowDefinition</span><span>/> </span><span>Grid.RowDefinitions</span><span>> <span>Grid.ColumnDefinitions</span><span>> <span>ColumnDefinition </span><span>Width</span><span>="80"/> <span>ColumnDefinition </span><span>Width</span><span>="*"/> </span><span>Grid.ColumnDefinitions</span><span>> <span>TextBlock </span><span>Grid.Row</span><span>="0" </span><span>Grid.Column</span><span>="0" </span><span>Text</span><span>="Name" </span><span>TextAlignment</span><span>="Center" </span><span>VerticalAlignment</span><span>="Center"/> <span>TextBlock </span><span>Grid.Row</span><span>="1" </span><span>Grid.Column</span><span>="0" </span><span>Text</span><span>="Age" </span><span>TextAlignment</span><span>="Center" </span><span>VerticalAlignment</span><span>="Center"/> <span>TextBox </span><span>Grid.Row</span><span>="0" </span><span>Grid.Column</span><span>="1" </span><span>Name</span><span>="txtName" </span><strong><span><span>Text</span><span>="{</span><span>Binding </span><span>Path</span></span></strong><span><strong><span>=Name}"</span></strong> /> <span>TextBox </span><span>Grid.Row</span><span>="1" </span><span>Grid.Column</span><span>="1" </span><span>Name</span><span>="txtAge" </span><span><strong><span>Text</span><span>="{</span><span>Binding </span><span>Path</span></strong></span><span><span><strong>=Age}"/</strong></span>> </span><span>Grid</span><span>> </span><span>Window</span><span>> </span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span> 登录后复制 |
我们发现这样的UI只能显示第一个数据项目,也就是说列表的当前项为0,至于其他的就无法显示出来了。
如果要显示其他的只有可通过如下代码的方式来取(注意:书中代码似乎有问题):
<span>private void </span>btnNext_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>People </span>people = (<span>People</span>)<span>this</span>.FindResource(<span>"Family"</span>); txtName.Text = people[1].Name; txtAge.Text = people[1].Age.ToString(); } 登录后复制 |
1.1当前项Current Item
取得当前项
可以通过上面的方法取得当前项,当然我们更专业的做法还是使用Collection View
还是代码说明比较简洁:
<span>People </span>people = (<span>People</span>)<span>this</span>.FindResource(<span>"Family"</span>); <span>ICollectionView </span>view = <span>CollectionViewSource</span>.GetDefaultView(people); <span>Person </span>peron = (<span>Person</span>)view.CurrentItem; 登录后复制 |
注意:ICollectionView在System.ComponentModel命名空间里。
导航当前项
还是代码来说明更合适点:
<span> private </span><span>ICollectionView </span>GetView() { <span>People </span>people = (<span>People</span>)<span>this</span>.FindResource(<span>"Family"</span>); <span>ICollectionView </span>view = <span>CollectionViewSource</span>.GetDefaultView(people); <span>return </span>view; } <span>private void </span>btnNext_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>ICollectionView </span>view = GetView(); view.MoveCurrentToNext(); <span>if </span>(view.IsCurrentAfterLast) { view.MoveCurrentToLast(); } } <span>private void </span>btnPrior_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>ICollectionView </span>view = GetView(); view.MoveCurrentToPrevious(); <span>if </span>(view.IsCurrentBeforeFirst) { view.MoveCurrentToFirst(); } } 登录后复制 |
1.2 List Data Targets
我们将列表数据绑定到类似TextBox这样的控件难以很好地展现列表数据。我们考虑ListBox控件来列举多个数据信息。
这时的效果如下:列表确实显示了所有对象的信息,因为我们没有设置Path属性,所以采用默认的Convertation来处理,显示对象类型。同时一定要注意使用IsSynchronizatizedWithCurrentItem=True,这样才能列表信息与其他信息同步。但究竟如何才能更好地表达我们需要的信息呢,请参看下一节:
1.3 Display Members, Value Members, and Look-Up Bindings
代码示例也许更易理解:
<span><span>ListBox </span><span>Grid.Row</span><span>="3" </span><span>Grid.Column</span><span>="1" </span><span>Name</span><span>="lstbox" </span><span>ItemsSource</span><span>="{</span><span>Binding</span><span>}" </span><span>DisplayMemberPath</span><span>="Name" </span><span>SelectedValuePath</span><span>="Age" </span><span>IsSynchronizedWithCurrentItem</span><span>="True"/> <span>Button </span><span>Grid.Row</span><span>="4" </span><span>Grid.Column</span><span>="0" </span><span>Name</span><span>="btnShowValue" </span><span>Content</span><span>="ShowValue" </span><span>Click</span><span>="btnShowValue_Click" /></span></span></span> 登录后复制 |
<span>private void </span>btnShowValue_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>MessageBox</span>.Show(lstbox.SelectedValue.ToString()); } 登录后复制 |
1.4数据模板Data Templates
这是利用ListBox控件有一个ItemTemplate属性下面,他可以接受一个DataTemplate类实例,
该模板可以重复绑定到ListBox的每一个项目元素,注意DataTemplate只能指定一个孩子节点,所以一般使用容器控件来组织下面的布局。
<span><span>ListBox </span><span>Grid.Row</span><span>="3" </span><span>Grid.Column</span><span>="1" </span><span>Name</span><span>="lstbox" </span><span>ItemsSource</span><span>="{</span><span>Binding</span><span>}"> <span>ListBox.ItemTemplate</span><span>> <span>DataTemplate</span><span>> <span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>Path</span><span>=Name}"> </span><span>的年龄是</span><span><span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>Path</span><span>=Age}"></span><span>TextBlock</span><span>> </span><span>TextBlock</span><span>> </span><span>DataTemplate</span><span>> </span><span>ListBox.ItemTemplate</span><span>> </span><span>ListBox</span><span>></span></span></span></span></span></span> 登录后复制 <span>我本人不赞同书中这样的做法,添加一个StackPanel更舒服点。</span> 登录后复制 |
1.5 列表改变List Changes
当我们改变列表的数据的时候,却出现如下现象:
只是因为我们需要绑定的列表需要实现INotifyCollectionChanged接口:
<span>namespace </span>System.Collections.Specialized { <span>public interface </span><span>INotifyCollectionChanged </span>{ <span>event </span><span>NotifyCollectionChangedEventHandler </span>CollectionChanged; } } 登录后复制 |
<span>namespace </span>System.Collections.ObjectModel { <span>public class </span><span>ObservableCollection</span><t> : <span>Collection</span><t>, INotifyCollectionChanged, <span>INotifyPropertyChanged </span>{ ... } }</t></t> 登录后复制 |
欢呼雀跃吧,我们改变上面例题的代码,一切如我们想象的美好。
所有的一切就如此简单,简单代码改动:
<span>//People类 </span><span>class </span><span>People </span>: <span><strong><span>ObservableCollection</span></strong></span>Person> { } 登录后复制 |
1.6 排序Sorting
简单的代码还是足以繁杂的文字,让我们看如下方法:
<span> private void </span>btnSort_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>ICollectionView </span>view = GetView(); <span>if </span>(view.SortDescriptions.Count == 0) { view.SortDescriptions.Add(<span>new </span><span>SortDescription</span>(<span>"Name"</span>, <span>ListSortDirection</span>.Ascending)); view.SortDescriptions.Add(<span>new </span><span>SortDescription</span>(<span>"Age"</span>, <span>ListSortDirection</span>.Descending)); } <span>else </span>{ view.SortDescriptions.Clear(); } } 登录后复制 |
当然我们还可以自定义排序方式:
<span> class </span><span>PersonSorter</span>:<span>IComparer </span>{ <span>public int </span>Compare(<span>object </span>x, <span>object </span>y) { <span>Person </span>lhs = (<span>Person</span>)x; <span>Person </span>rhs = (<span>Person</span>)y; <span>// Sort Name ascending and Age descending </span><span>int </span>nameCompare = lhs.Name.CompareTo(rhs.Name); <span>if </span>(nameCompare != 0) <span>return </span>nameCompare; <span>return </span>rhs.Age - lhs.Age; } } 登录后复制 |
注意:WPF不使用System.Collection.Generic命名空间的泛型IComparer接口,而是使用System.Collection的。呵呵。
使用代码如下:
<span> private void </span>btnSort_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>ListCollectionView </span>view = (<span>ListCollectionView</span>)GetView(); <span>if </span>(view.CustomSort == <span>null</span>) { view.CustomSort = <span>new </span><span>PersonSorter</span>(); } <span>else </span>{ view.CustomSort = <span>null</span>; } } 登录后复制 |
注意:ListCollectionView支持自定义和排序,其他的不支持。
1.7 集合缺省视图类型Default Collection View
1.8 过滤 Filter
依然是我熟悉的表达方式:代码:
<span> private void </span>btnFilter_Click(<span>object </span>sender, <span>RoutedEventArgs </span>e) { <span>ListCollectionView </span>view = (<span>ListCollectionView</span>)GetView(); <span>if </span>(view.Filter == <span>null</span>) { view.Filter = <span>delegate</span>(<span>object </span>item) { <span>return </span>((<span>Person</span>)item).Age > 17; }; } <span>else </span>{ view.Filter = <span>null</span>; } } 登录后复制 |
1.9 分组Grouping
分组的意思大家很明白就是按照某一个或几个关键属性进行分类。
进行分组很简单和sort类似,只需要以下几行代码:
<span> ICollectionView </span>view = GetView(); <span>if </span>(view.GroupDescriptions.Count == 0) { view.GroupDescriptions.Add(<span>new </span><span>PropertyGroupDescription</span>(<span>"Age"</span>)); } <span>else </span>{ view.GroupDescriptions.Clear(); } 登录后复制 |
但这在UI层面并没有任何影响,这需要我们对ItemsControl类的控件(例如ListBox)设置GroupStyle属性,GroupStyle类缺省地提供了一个静态的属性实现,我们可以如下设置:
<span> <span>ListBox </span><span>Grid.Row</span><span>="3" </span><span>Grid.Column</span><span>="1" </span><span>Name</span><span>="lstbox" </span><span>ItemsSource</span><span>="{</span><span>Binding</span><span>}" </span><span>IsSynchronizedWithCurrentItem</span><span>="True"> <strong><span></span></strong></span><span><strong><span>ListBox.GroupStyle</span></strong></span><strong><span><span>> <span>x</span><span>:</span><span>Static </span><span>Member</span></span><span><span>="GroupStyle.Default"/> </span><span>ListBox.GroupStyle</span></span></span></strong><span><strong><span>></span></strong> <span>ListBox.ItemTemplate</span><span>> <span>DataTemplate</span><span>> <span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>Path</span><span>=Name}"> </span><span>的年龄是</span><span><span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>Path</span><span>=Age}"></span><span>TextBlock</span><span>> </span><span>TextBlock</span><span>> </span><span>DataTemplate</span><span>> </span><span>ListBox.ItemTemplate</span><span>> </span><span>ListBox</span><span>></span></span></span></span></span></span> 登录后复制 |
但也许这并不是我们所喜欢的界面,简单得让人生厌,还好微软提供了这个对象的一个属性:HeaderTemplate用于定义分组的栏目的外观,微软总是为大家想得那么周到,养活那么多天才是需要钱的,希望大家不要老是讲微软的坏话。
<span> <span>ListBox.GroupStyle</span><span>> <span>GroupStyle</span><span>> <span>GroupStyle.HeaderTemplate</span><span>> <span>DataTemplate</span><span>> <span>StackPanel </span><span>Background</span><span>="Green" </span><span>Orientation</span><span>="Horizontal"> <span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>Name</span><span>}"/> <span>TextBlock </span><span>Text</span><span>="("/> <span>TextBlock </span><span>Text</span><span>="{</span><span>Binding </span><span>ItemCount</span><span>}"/> <span>TextBlock </span><span>Text</span><span>=")"/> </span><span>StackPanel</span><span>> </span><span>DataTemplate</span><span>> </span><span>GroupStyle.HeaderTemplate</span><span>> </span><span>GroupStyle</span><span>> </span><span>ListBox.GroupStyle</span><span>></span></span></span></span></span></span></span></span></span></span> 登录后复制 |
有这模板属性一切由你发挥,真是好也,然而即使这样解决了UI问题,但是如果我们还希望更进一步,能否实现范围内分组呢?呵呵,然也:
这时我们不需要去想着如何继承GroupStyle类,而是采用围魏救赵的方式,定义一个IValueConverter,
<span> public class </span><span>AgeRangeConvert </span>: <span>IValueConverter </span>{ <span>public object </span>Convert(<span>object </span>value, <span>Type </span>targetType, <span>object </span>parameter, <span>CultureInfo </span>culture) { <span>int </span>_value = (<span>int</span>)value; <span>if </span>(_value return <span>"10岁以下"</span>; <span>else if </span>(_value return <span>"20岁以下"</span>; <span>else return </span><span>"20岁以上"</span>; } <span>public object </span>ConvertBack(<span>object </span>value, <span>Type </span>targetType, <span>object </span>parameter, <span>CultureInfo </span>culture) { <span>throw new </span><span>NotImplementedException</span>(); } } 登录后复制 |
简单调整前面分组代码:
<span> ICollectionView </span>view = GetView(); <span>if </span>(view.GroupDescriptions.Count == 0) { view.GroupDescriptions.Add(<span>new </span><span>PropertyGroupDescription</span>(<span>"Age"</span>,<span>new </span><span>AgeRangeConvert</span>())); } <span>else </span>{ view.GroupDescriptions.Clear(); } 登录后复制 |
一切搞定,享受成果吧:
既然GroupDescripions是个集合类型,我们不妨看下面代码究竟是什么效果:
<span> ICollectionView </span>view = GetView(); <span>if </span>(view.GroupDescriptions.Count == 0) { view.GroupDescriptions.Add(<span>new </span><span>PropertyGroupDescription</span>(<span>"Age"</span>,<span>new </span><span>AgeRangeConvert</span>())); view.GroupDescriptions.Add(<span>new </span><span>PropertyGroupDescription</span>(<span>"Age"</span>)); } <span>else </span>{ view.GroupDescriptions.Clear(); } 登录后复制 |
运行如下:

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

DDREASE是一种用于从文件或块设备(如硬盘、SSD、RAM磁盘、CD、DVD和USB存储设备)恢复数据的工具。它将数据从一个块设备复制到另一个块设备,留下损坏的数据块,只移动好的数据块。ddreasue是一种强大的恢复工具,完全自动化,因为它在恢复操作期间不需要任何干扰。此外,由于有了ddasue地图文件,它可以随时停止和恢复。DDREASE的其他主要功能如下:它不会覆盖恢复的数据,但会在迭代恢复的情况下填补空白。但是,如果指示工具显式执行此操作,则可以将其截断。将数据从多个文件或块恢复到单

0.这篇文章干了啥?提出了DepthFM:一个多功能且快速的最先进的生成式单目深度估计模型。除了传统的深度估计任务外,DepthFM还展示了在深度修复等下游任务中的最先进能力。DepthFM效率高,可以在少数推理步骤内合成深度图。下面一起来阅读一下这项工作~1.论文信息标题:DepthFM:FastMonocularDepthEstimationwithFlowMatching作者:MingGui,JohannesS.Fischer,UlrichPrestel,PingchuanMa,Dmytr

谷歌力推的JAX在最近的基准测试中性能已经超过Pytorch和TensorFlow,7项指标排名第一。而且测试并不是在JAX性能表现最好的TPU上完成的。虽然现在在开发者中,Pytorch依然比Tensorflow更受欢迎。但未来,也许有更多的大模型会基于JAX平台进行训练和运行。模型最近,Keras团队为三个后端(TensorFlow、JAX、PyTorch)与原生PyTorch实现以及搭配TensorFlow的Keras2进行了基准测试。首先,他们为生成式和非生成式人工智能任务选择了一组主流

在iPhone上面临滞后,缓慢的移动数据连接?通常,手机上蜂窝互联网的强度取决于几个因素,例如区域、蜂窝网络类型、漫游类型等。您可以采取一些措施来获得更快、更可靠的蜂窝互联网连接。修复1–强制重启iPhone有时,强制重启设备只会重置许多内容,包括蜂窝网络连接。步骤1–只需按一次音量调高键并松开即可。接下来,按降低音量键并再次释放它。步骤2–该过程的下一部分是按住右侧的按钮。让iPhone完成重启。启用蜂窝数据并检查网络速度。再次检查修复2–更改数据模式虽然5G提供了更好的网络速度,但在信号较弱

MetaMask(中文也叫小狐狸钱包)是一款免费的、广受好评的加密钱包软件。目前,BTCC已支持绑定MetaMask钱包,绑定后可使用MetaMask钱包进行快速登入,储值、买币等,且首次绑定还可获得20USDT体验金。在BTCCMetaMask钱包教学中,我们将详细介绍如何注册和使用MetaMask,以及如何在BTCC绑定并使用小狐狸钱包。MetaMask钱包是什么?MetaMask小狐狸钱包拥有超过3,000万用户,是当今最受欢迎的加密货币钱包之一。它可免费使用,可作为扩充功能安装在网络

哭死啊,全球狂炼大模型,一互联网的数据不够用,根本不够用。训练模型搞得跟《饥饿游戏》似的,全球AI研究者,都在苦恼怎么才能喂饱这群数据大胃王。尤其在多模态任务中,这一问题尤为突出。一筹莫展之际,来自人大系的初创团队,用自家的新模型,率先在国内把“模型生成数据自己喂自己”变成了现实。而且还是理解侧和生成侧双管齐下,两侧都能生成高质量、多模态的新数据,对模型本身进行数据反哺。模型是啥?中关村论坛上刚刚露面的多模态大模型Awaker1.0。团队是谁?智子引擎。由人大高瓴人工智能学院博士生高一钊创立,高

特斯拉机器人Optimus最新视频出炉,已经可以在厂子里打工了。正常速度下,它分拣电池(特斯拉的4680电池)是这样的:官方还放出了20倍速下的样子——在小小的“工位”上,拣啊拣啊拣:这次放出的视频亮点之一在于Optimus在厂子里完成这项工作,是完全自主的,全程没有人为的干预。并且在Optimus的视角之下,它还可以把放歪了的电池重新捡起来放置,主打一个自动纠错:对于Optimus的手,英伟达科学家JimFan给出了高度的评价:Optimus的手是全球五指机器人里最灵巧的之一。它的手不仅有触觉

这周,由OpenAI、微软、贝佐斯和英伟达投资的机器人公司FigureAI宣布获得接近7亿美元的融资,计划在未来一年内研发出可独立行走的人形机器人。而特斯拉的擎天柱也屡屡传出好消息。没人怀疑,今年会是人形机器人爆发的一年。一家位于加拿大的机器人公司SanctuaryAI最近发布了一款全新的人形机器人Phoenix。官方号称它能以和人类一样的速率自主完成很多工作。世界上第一台能以人类速度自主完成任务的机器人Pheonix可以轻轻地抓取、移动并优雅地将每个对象放置在它的左右两侧。它能够自主识别物体的
