`
wangleifire
  • 浏览: 500092 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

关于WPF ListView数据绑定 入门 及分类,排序

    博客分类:
  • c#
阅读更多

不多说,先直接上代码

  1. public   class  Student : INotifyPropertyChanged  
  2. {  
  3.     public   event  PropertyChangedEventHandler PropertyChanged;  // 这个接口仅包含一个事件而已   
  4.   
  5.     private   int  id;  
  6.     public   int  Id  
  7.     {  
  8.         get  {  return  id; }  
  9.         set   
  10.         {  
  11.             id = value;  
  12.             if  ( this .PropertyChanged !=  null )  
  13.             {  
  14.   
  15.                 this .PropertyChanged.Invoke( this new  PropertyChangedEventArgs( "Id" ));  // 通知Binding是“Id”这个属性的值改变了   
  16.             }  
  17.         }  
  18.     } 
  19. }

public class Students :  ObservableCollection<Student>

{

}

 

<Window.Resource>

   <local:Students x:Key="Students">

      <local:Student Id="111" />

      <local:Student Id="222" />

  </local:Students>

</Window.Resource>

<ListView ItemsSource="{StaticResource Students}">

   <ListView.ItemTemplate>

      <DataTemplate>

          <TextBox Text="{Binding Path=Id}" />

      </DataTemplate>

   </ListView.ItemTemplate>

</ListView>

 

 

 

 

再来看看别人文章,里面有如何分类,排序的方法,仔细看哟!

 

1 Binding to List Data

前面都是绑定到一个对象,下面我们学习绑定到对象列表的方法。

我们还是先组织要绑定的数据,对象所对应的类还是Person,但新增了一个新类People,该类用来组织Person的列表.代码如下:

using 
System;
            using 
System.Collections.Generic;
            using 
System.ComponentModel;//INotifyPropertyChanged
            
namespace 
SimpleDataBinding
            {
            class 
Person 
: INotifyPropertyChanged
            
{
            public event 
PropertyChangedEventHandler 
PropertyChanged;
            protected void 
Notify(string 
PropName)
            {
            if 
(this
.PropertyChanged != null
)
            {
            PropertyChanged(this
, new 
PropertyChangedEventArgs
(PropName));
            }
            }
            public 
Person()
            {
            _Age = 0;
            _name = "Null"
;
            this
.CurrentDate = DateTime
.Now;
            }
            private string 
_name;
            public string 
Name
            {
            get 
{ return 
_name; }
            set
            
{
            if 
(value 
== _name)
            { return
; }
            _name = value
;//注意:不能用this.Name来赋值,如果这样形成循环调用,栈溢出
            
Notify("Name"
);
            }
            }
            private int 
_Age;
            public int 
Age
            {
            get 
{ return 
_Age; }
            set
            
{
            if 
(value 
== _Age) return
;
            _Age = value
;
            Notify("Age"
);
            }
            }
            public 
DateTime 
CurrentDate { get
; set
; }
            }
             
//People类
            
class 
People 
: List
<Person
>
            {
            }


            }
            

注意在同一命名空间下的代码最后添加了Perople类。

我们在UI里显示的XAML如下:

<
Window 
x
:
Class
="ListDataBinding.BindListDataTest"
            
xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            
xmlns
:
x
="http://schemas.microsoft.com/winfx/2006/xaml"
              


xmlns
:
src



="clr-namespace:ListDataBinding"



            
Title
="BindListDataTest" 
Height
="113" 
Width
="300">
            <
Window.Resources
>
             <


src
:
People 
x
:
Key


="Family">


            <
src
:
Person 
Name
="Jack" 
Age
="18"/>
            <
src
:
Person 
Name
="Tom" 
Age
="30"/>
            <
src
:
Person 
Name
="Jone" 
Age
="14"/>
            <
src
:
Person 
Name
="Rose" 
Age
="17"/>
            <
src
:
Person 
Name
="Mike" 
Age
="13"/>
            </
src
:
People
>
            </
Window.Resources
>
            <
Grid 
DataContext
="{
StaticResource 
Family
}">
            <
Grid.RowDefinitions
>
            <
RowDefinition
/>
            <
RowDefinition
/>
            </
Grid.RowDefinitions
>
            <
Grid.ColumnDefinitions
>
            <
ColumnDefinition 
Width
="80"/>
            <
ColumnDefinition 
Width
="*"/>
            </
Grid.ColumnDefinitions
>
            <
TextBlock 
Grid.Row
="0" 
Grid.Column
="0" 
Text
="Name" 
TextAlignment
="Center" 
VerticalAlignment
="Center"/>
            <
TextBlock 
Grid.Row
="1" 
Grid.Column
="0" 
Text
="Age" 
TextAlignment
="Center" 
VerticalAlignment
="Center"/>
            <
TextBox 
Grid.Row
="0" 
Grid.Column
="1" 
Name
="txtName" 
Text
="{
Binding 
Path


=Name}"

 />
            <
TextBox 
Grid.Row
="1" 
Grid.Column
="1" 
Name
="txtAge" 
Text
="{
Binding 
Path


=Age}"/

>
            </
Grid
>
            </
Window
>
            

我们发现这样的UI只能显示第一个数据项目,也就是说列表的当前项为0,至于其他的就无法显示出来了。

如果要显示其他的只有可通过如下代码的方式来取(注意:书中代码似乎有问题):

private void 
btnNext_Click(object 
sender, RoutedEventArgs 
e)
            {
            People 
people = (People
)this
.FindResource("Family"
);
            txtName.Text = people[1].Name;
            txtAge.Text = people[1].Age.ToString();
            }

 

1.1当前项Current Item

取得当前项

可以通过上面的方法取得当前项,当然我们更专业的做法还是使用Collection View

还是代码说明比较简洁:

People 
people = (People
)this
.FindResource("Family"
);
            ICollectionView 
view = CollectionViewSource
.GetDefaultView(people);
            Person 
peron = (Person
)view.CurrentItem;

注意:ICollectionView在System.ComponentModel命名空间里。

导航当前项

还是代码来说明更合适点:

      private 
ICollectionView 
GetView()
            {
            People 
people = (People
)this
.FindResource("Family"
);
            ICollectionView 
view = CollectionViewSource
.GetDefaultView(people);
            return 
view;
            }
            private void 
btnNext_Click(object 
sender, RoutedEventArgs 
e)
            {
            ICollectionView 
view = GetView();
            view.MoveCurrentToNext();
            if 
(view.IsCurrentAfterLast)
            {
            view.MoveCurrentToLast();
            }
            }
            private void 
btnPrior_Click(object 
sender, RoutedEventArgs 
e)
            {
            ICollectionView 
view = GetView();
            view.MoveCurrentToPrevious();
            if 
(view.IsCurrentBeforeFirst)
            {
            view.MoveCurrentToFirst();
            }
            }
1.2 List Data Targets

我们将列表数据绑定到类似TextBox这样的控件难以很好地展现列表数据。我们考虑ListBox控件来列举多个数据信息。

<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True"/>

这时的效果如下:列表确实显示了所有对象的信息,因为我们没有设置Path属性,所以采用默认的Convertation来处理,显示对象 类型。同时一定要注意使用IsSynchronizatizedWithCurrentItem=True,这样才能列表信息与其他信息同步。但究竟如何 才能更好地表达我们需要的信息呢,请参看下一节:

clip_image002

1.3 Display Members, Value Members, and Look-Up Bindings

代码示例也许更易理解:

<
ListBox 
Grid.Row
="3" 
Grid.Column
="1" 
Name
="lstbox" 
ItemsSource
="{
Binding
}"
            
DisplayMemberPath
="Name" 
SelectedValuePath
="Age" 
IsSynchronizedWithCurrentItem
="True"/>
            <
Button 
Grid.Row
="4" 
Grid.Column
="0" 
Name
="btnShowValue" 
Content
="ShowValue" 
Click
="btnShowValue_Click" />
private void 
btnShowValue_Click(object 
sender, RoutedEventArgs 
e)
            {
            MessageBox
.Show(lstbox.SelectedValue.ToString());
            }
1.4数据模板Data Templates

这是利用ListBox控件有一个ItemTemplate属性下面,他可以接受一个DataTemplate类实例,

该模板可以重复绑定到ListBox的每一个项目元素,注意DataTemplate只能指定一个孩子节点,所以一般使用容器控件来组织下面的布局。

<
ListBox 
Grid.Row
="3" 
Grid.Column
="1" 
Name
="lstbox" 
ItemsSource
="{
Binding
}">
            <
ListBox.ItemTemplate
>
            <
DataTemplate
>
            <
TextBlock 
Text
="{
Binding 
Path
=Name}">
            
的年龄是
<
TextBlock 
Text
="{
Binding 
Path
=Age}"></
TextBlock
>
            </
TextBlock
>
            </
DataTemplate
>
            </
ListBox.ItemTemplate
>
            </
ListBox
>
我本人不赞同书中这样的做法,添加一个StackPanel更舒服点。

 

1.5 列表改变List Changes

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

image

只是因为我们需要绑定的列表需要实现INotifyCollectionChanged接口:

namespace 
System.Collections.Specialized
            {
            public interface 
INotifyCollectionChanged
            
{
            event 
NotifyCollectionChangedEventHandler 
CollectionChanged;
            }
            }

 

namespace 
System.Collections.ObjectModel
            {
            public class 
ObservableCollection
<T> : Collection
<T>, INotifyCollectionChanged, INotifyPropertyChanged
            
{
            ...
            }
            }

欢呼雀跃吧,我们改变上面例题的代码,一切如我们想象的美好。

所有的一切就如此简单,简单代码改动:

//People类
            
class 
People 
: ObservableCollection


<Person
>
            {
            }

clip_image002[4]

1.6 排序Sorting

简单的代码还是足以繁杂的文字,让我们看如下方法:

       private void 
btnSort_Click(object 
sender, RoutedEventArgs 
e)
            {
            ICollectionView 
view = GetView();
            if 
(view.SortDescriptions.Count == 0)
            {
            view.SortDescriptions.Add(new 
SortDescription
("Name"
, ListSortDirection
.Ascending));
            view.SortDescriptions.Add(new 
SortDescription
("Age"
, ListSortDirection
.Descending));
            }
            else
            
{
            view.SortDescriptions.Clear();
            }
            }

当然我们还可以自定义排序方式:

    class 
PersonSorter
:IComparer
            
{
            public int 
Compare(object 
x, object 
y)
            {
            Person 
lhs = (Person
)x;
            Person 
rhs = (Person
)y;
            // Sort Name ascending and Age descending
            
int 
nameCompare = lhs.Name.CompareTo(rhs.Name);
            if 
(nameCompare != 0) return 
nameCompare;
            return 
rhs.Age - lhs.Age;
            }
            }

注意:WPF不使用System.Collection.Generic命名空间的泛型IComparer接口,而是使用System.Collection的。呵呵。

使用代码如下:

       private void 
btnSort_Click(object 
sender, RoutedEventArgs 
e)
            {
            ListCollectionView 
view = (ListCollectionView
)GetView();
            if 
(view.CustomSort == null
)
            {
            view.CustomSort = new 
PersonSorter
();
            }
            else
            
{
            view.CustomSort = null
;
            }
            }

注意:ListCollectionView支持自定义和排序,其他的不支持。

1.7 集合缺省视图类型Default Collection View

image

1.8 过滤 Filter

依然是我熟悉的表达方式:代码:

       private void 
btnFilter_Click(object 
sender, RoutedEventArgs 
e)
            {
            ListCollectionView 
view = (ListCollectionView
)GetView();
            if 
(view.Filter == null
)
            {
            view.Filter = delegate
(object 
item)
            {
            return 
((Person
)item).Age > 17;
            };
            }
            else
            
{
            view.Filter = null
;
            }
            }
1.9 分组Grouping

分组的意思大家很明白就是按照某一个或几个关键属性进行分类。

进行分组很简单和sort类似,只需要以下几行代码:

          ICollectionView 
view = GetView();
            if 
(view.GroupDescriptions.Count == 0)
            {
            view.GroupDescriptions.Add(new 
PropertyGroupDescription
("Age"
));
            }
            else
            
{
            view.GroupDescriptions.Clear();
            }

但这在UI层面并没有任何影响,这需要我们对ItemsControl类的控件(例如ListBox)设置GroupStyle属性,GroupStyle类缺省地提供了一个静态的属性实现,我们可以如下设置:

      <
ListBox 
Grid.Row
="3" 
Grid.Column
="1" 
Name
="lstbox" 
ItemsSource
="{
Binding
}"  
IsSynchronizedWithCurrentItem
="True">
            <


ListBox.GroupStyle


>
            <
x
:
Static 
Member


="GroupStyle.Default"/>
            </
ListBox.GroupStyle


>


            <
ListBox.ItemTemplate
>
            <
DataTemplate
>
            <
TextBlock 
Text
="{
Binding 
Path
=Name}">
            
的年龄是
<
TextBlock 
Text
="{
Binding 
Path
=Age}"></
TextBlock
>
            </
TextBlock
>
            </
DataTemplate
>
            </
ListBox.ItemTemplate
>
            </
ListBox
>

但也许这并不是我们所喜欢的界面,简单得让人生厌,还好微软提供了这个对象的一个属性:HeaderTemplate用于定义分组的栏目的外观,微软总是为大家想得那么周到,养活那么多天才是需要钱的,希望大家不要老是讲微软的坏话。

           <
ListBox.GroupStyle
>
            <
GroupStyle
>
            <
GroupStyle.HeaderTemplate
>
            <
DataTemplate
>
            <
StackPanel 
Background
="Green"  
Orientation
="Horizontal">
            <
TextBlock 
Text
="{
Binding 
Name
}"/>
            <
TextBlock 
Text
="("/>
            <
TextBlock 
Text
="{
Binding 
ItemCount
}"/>
            <
TextBlock 
Text
=")"/>
            </
StackPanel
>
            </
DataTemplate
>
            </
GroupStyle.HeaderTemplate
>
            </
GroupStyle
>
            </
ListBox.GroupStyle
>

有这模板属性一切由你发挥,真是好也,然而即使这样解决了UI问题,但是如果我们还希望更进一步,能否实现范围内分组呢?呵呵,然也:

这时我们不需要去想着如何继承GroupStyle类,而是采用围魏救赵的方式,定义一个IValueConverter,

   public class 
AgeRangeConvert 
: IValueConverter
            
{
            public object 
Convert(object 
value, Type 
targetType, object 
parameter, CultureInfo 
culture)
            {
            int 
_value = (int
)value;
            if 
(_value <= 10)
            return 
"10岁以下"
;
            else if 
(_value <= 20)
            return 
"20岁以下"
;
            else
            return 
"20岁以上"
;
            }
            public object 
ConvertBack(object 
value, Type 
targetType, object 
parameter, CultureInfo 
culture)
            {
            throw new 
NotImplementedException
();
            }
            }

简单调整前面分组代码:

           ICollectionView 
view = GetView();
            if 
(view.GroupDescriptions.Count == 0)
            {
            view.GroupDescriptions.Add(new 
PropertyGroupDescription
("Age"
,new  
AgeRangeConvert
()));
            }
            else
            
{
            view.GroupDescriptions.Clear();
            }

一切搞定,享受成果吧:

image

既然GroupDescripions是个集合类型,我们不妨看下面代码究竟是什么效果:

            ICollectionView 
view = GetView();
            if 
(view.GroupDescriptions.Count == 0)
            {
            view.GroupDescriptions.Add(new 
PropertyGroupDescription
("Age"
,new  
AgeRangeConvert
()));
            view.GroupDescriptions.Add(new 
PropertyGroupDescription
("Age"
));
            }
            else
            
{
            view.GroupDescriptions.Clear();
            }

运行如下:

clip_image002[1]

呵呵,这不正是有时你需要的效果吗?至于界面如何优化,模板如何定义更好看我们以后话题再说。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics