Tin tức WPF – Data Binding và Collection: Sorting, Filtering, Grouping

WPF – Data Binding và Collection: Sorting, Filtering, Grouping

Ngày đăng: - chuyên mục Bài viết đơn

Một collection cũng có thể được coi là một đối tượng, tuy nhiên việc binding loại dữ liệu này yêu cầu những chức năng như sắp xếp, lọc, gom nhóm,… Trong bài này, tôi sẽ giới thiệu về Collection View và sử dụng để thực hiện các chức năng này trên dữ liệu được binding vào ListBox.

 

Collection View là gì?

Đây là một lớp dùng để hiển thị dữ liệu và cung cấp các chức năng duyệt, sắp xếp, lọc,… trên một collection. Việc sử dụng các chức năng này không ảnh hưởng gì đến dữ liệu nguồn. Với những thao tác làm thay đổi dữ liệu nguồn như thêm, xóa,… collection cần được hiện thực interface INotifyCollectionChanged. Tuy nhiên WPF cũng cung cấp một lớp generic làObservableCollection<T> đã được hiện thực sẵn INotifyCollectionChanged để bạn sử dụng mà không cần tạo thêm một kiểu collection mới.

Thay vì tạo một đối tượng mới, bạn cũng có thể lấy Collection View mặc định của một collection bằng phương thức static CollectionViewSource.GetDefaultView() với kiểu trả về làSystem.ComponentModel.ICollectionView.

Tạo Collection View

Tôi có một lớp Person sau:

1
2
3
4
5
6
7
8
9
10
public class Person
{
    public int Age{get;set;}
    public string Name {get;set;}
 
    public override string ToString()
    {
        return string.Format("{0} - age: {1}",Name,Age);
    }
}

và một lớp Window1 với property Persons:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public partial class Window1 : Window
{
    ObservableCollection<Person> _persons =new ObservableCollection<Person>(){
        new Person(){Age=10,Name="John"},
        new Person(){Age=11,Name="Alice"},
        new Person(){Age=12,Name="Danby"},
    };
 
  ...
 
    public ObservableCollection<Person> Persons{
        get { return _persons; }
    }
}

Để tạo một Collection View trong XAML, bạn cần sử dụng lớp CollectionViewSource, đây là lớp đại diện cho CollectionView. Sử dụng property Source để xác định dữ liệu nguồn:

1
2
3
4
5
6
7
8
9
10
<Window x:Name="mainWindow" x:Class="BindingExample.Window1"
...
    >
    <Window.Resources>
        <CollectionViewSource
          Source="{Binding ElementName=mainWindow, Path=Persons}"
          x:Key="myCollectionView" />
    </Window.Resources>
...
</Window>

Sau đó bạn có thể dùng CollectionViewSource này làm binding source cho một control hiển thị collection như ListBox:

<ListBox x:Name="listBox1"  ItemsSource="{Binding Source={StaticResource myCollectionView}}"/>

Thay vì dùng XAML, bạn có thể viết trong code-behind:

1
2
3
4
5
CollectionView collectionView=new CollectionView(this.Persons);
listBox1.ItemsSource = collectionView;
// or
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Persons);
listBox1.ItemsSource=collectionView;

Sorting

Collection View chứa một collection SortDescriptions các đối tượng SortDescription. Mỗi SortDescription tương ứng với một cột (property của phần tử trong collection) cần xếp thông qua hai property là PropertyName và Direction. Ví dụ sau thực hiện sắp xếp giảm dần theo property Name của collection:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Window x:Name="mainWindow" x:Class="BindingExample.Window1"
         ...
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding ElementName=mainWindow, Path=Persons}"
            x:Key="myCollectionView" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Name" Direction="Descending" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
...
</Window>

Trong code-behind:

1
2
3
4
5
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Persons);
collectionView.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Descending));
listBox1.ItemsSource=collectionView;
// or
listBox1.Items.SortDescriptions.Add(new SortDescription("Name",ListSortDirection.Descending));

Filtering

Collection View cung cấp một property Filter có kiểu Predicate<Object>. Kiểu này là một delegate chấp nhận một tham số kiểu object và trả về một giá trị boolean dựa trên các điều kiện bạn thực hiện trên tham số đó. Ví dụ nếu muốn lọc ra các Person có Age > 10. Ta có thể viết:

listBox1.Items.Filter=(obj)=> ((Person)obj).Age>10;

Grouping

Việc gom nhóm dữ liệu trong collection để hiển thị trên ListBox cần phải thiết lập giá trị property ItemsControl.GroupStyle. Thay vì tạo một custom GroupStyle, bạn có thể dùng giá trị mặc định GroupStyle.Default để hiển thị:

1
2
3
4
5
<ListBox x:Name="listBox1" ItemsSource="{Binding}">
    <ListBox.GroupStyle>
        <x:Static Member="GroupStyle.Default" />
    </ListBox.GroupStyle>
</ListBox>

Để gom nhóm theo một property, bạn cần tạo một đối tượng PropertyGroupDescription và gán giá trị cho property PropertyName. Ví dụ tôi muốn gom nhóm theo property Age của Person:

1
2
3
PropertyGroupDescription groupDescription = new PropertyGroupDescription();
groupDescription.PropertyName = "Age";
listBox1.Items.GroupDescriptions.Add(groupDescription);

Với dữ liệu cần hiển thị là:

1
2
3
4
5
6
ObservableCollection<Person> _persons =new ObservableCollection<Person>(){
    new Person(){Age=10,Name="John"},
    new Person(){Age=11,Name="Alice"},
    new Person(){Age=12,Name="Danby"},
    new Person(){Age=12,Name="Tobin"},
};

Sử dụng custom GroupStyle, bạn có thể tạo một DataTemplate để thiết lập kiểu hiển thị của phần tiêu đề mỗi nhóm. Như ví dụ sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<Window x:Name="mainWindow" x:Class="BindingExample.Window1"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="BindingExample" Height="300" Width="300"
>
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding ElementName=mainWindow, Path=Persons}"
            x:Key="myCollectionView" />
        <DataTemplate x:Key="groupTemplate" DataType="Group">
            <Border Margin="4" BorderBrush="Blue" BorderThickness="1" >
                <StackPanel Orientation="Horizontal" Background="DarkGray">
                    <TextBlock Text="Group: " Foreground="LightCyan"/>
                    <TextBlock Text="{Binding Name}"  Foreground="White"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </Window.Resources>
    <StackPanel DataContext="{StaticResource myCollectionView}">
        <ListBox x:Name="listBox1" ItemsSource="{Binding}">
            <ListBox.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource groupTemplate}"/>
            </ListBox.GroupStyle>
        </ListBox>
    </StackPanel>
</Window>

Kết quả:

Binding đến phần tử hiện tại trong Collection View

XAML sử dụng kí tự “/” dùng cho việc binding đến phần tử hiện tại trong Collection View. Mỗi dấu “/” tương ứng với một cấp của collection. Bạn có thể coi đây giống như dấu ngăn cách tên các thư mục trong Windows Explorer. Ví dụ này tôi  gán dữ liệu dạng collection cho DataContext của một phần tử cha của các Label, sau đó binding đến collection và phần tử hiện tại trong Collection View vào property Label.Content:

1
2
3
4
<Label Content="{Binding}" />                 <!-- collection -->
<Label Content="{Binding Path=/}" />        <!-- current item -->
<Label Content="{Binding Path=/Name}" />    <!-- Name property of current item -->
<Label Content="{Binding Path=/Name/}" />    <!-- First charater of Name property (a string is a collection of character) -->

Screenshot:

Mã nguồn hoàn chỉnh của tài liệu Window1.xaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<Window x:Name="mainWindow" x:Class="BindingExample.Window1"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        Title="BindingExample" Height="300" Width="300"
>
    <Window.Resources>
        <CollectionViewSource
            Source="{Binding ElementName=mainWindow, Path=Persons}"
            x:Key="myCollectionView" >
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Name" Direction="Descending" />
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <StackPanel DataContext="{StaticResource myCollectionView}">
        <ListBox x:Name="listBox1" ItemsSource="{Binding}"/>
 
        <Label Content="{Binding}" />                 <!-- collection -->
        <Label Content="{Binding Path=/}" />        <!-- current item -->
        <Label Content="{Binding Path=/Name}" />    <!-- Name property of current item -->
        <Label Content="{Binding Path=/Name/}" />    <!-- First charater of Name property (a string is a collection of character) -->
    </StackPanel>
</Window>
  • Từ khóa có liên quan:

Liên hệ

Để được hỗ trợ và tư vấn trực tiếp hãy liên hệ với chúng tôi ngay bây giờ.
CÔNG TY TNHH PHẦN MỀM L.I.B.S
183/2, Khu phố Thạnh Lợi,
Phường An Thạnh, Thị xã Thuận An,
Tỉnh Bình Dương
Hotline: 0906442230
© 2015 LIBS Software