Heim >Datenbank >MySQL-Tutorial >WPF数据绑定(2 绑定列表数据Binding to List Data)

WPF数据绑定(2 绑定列表数据Binding to List Data)

WBOY
WBOYOriginal
2016-06-07 15:48:521410Durchsuche

(读完此系列WPF和Silverlight的数据绑定问题你就轻松搞定 ) 1 Binding to List Data 前面都是绑定到一个对象,下面我们学习绑定到对象列表的方法。 我们还是先组织要绑定的数据,对象所对应的类还是Person,但新增了一个新类People,该类用来组织Person的列

(读完此系列WPF和Silverlight的数据绑定问题你就轻松搞定WPF数据绑定(2 绑定列表数据Binding to List Data)

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,这样才能列表信息与其他信息同步。但究竟如何才能更好地表达我们需要的信息呢,请参看下一节:

WPF数据绑定(2 绑定列表数据Binding to List Data)

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

当我们改变列表的数据的时候,却出现如下现象:

WPF数据绑定(2 绑定列表数据Binding to List Data)

只是因为我们需要绑定的列表需要实现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>
            {
            }

WPF数据绑定(2 绑定列表数据Binding to List Data)

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

WPF数据绑定(2 绑定列表数据Binding to List Data)

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();
            }

一切搞定,享受成果吧:

WPF数据绑定(2 绑定列表数据Binding to List Data)

既然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();
            }

运行如下:

WPF数据绑定(2 绑定列表数据Binding to List Data)

呵呵,这不正是有时你需要的效果吗?至于界面如何优化,模板如何定义更好看我们以后话题再
Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn