TOC

This article has been localized into Vietnamese by the community.

ListView:

Làm sao để: sắp xếp cột với ListView

Trong chương trước, chúng ta đã thấy làm thế nào chúng ta có thể dễ dàng sắp xếp một ListView từ Code-behind trong khi điều này sẽ đủ cho một số trường hợp, nó không cho phép người dùng quyết định việc sắp xếp. Ngoài ra, không có dấu hiệu nào cho thấy ListView được sắp xếp theo cột nào. Trong Windows và trong nhiều giao diện người dùng nói chung, việc minh họa các hướng sắp xếp trong danh sách bằng cách vẽ một hình tam giác bên cạnh tên cột hiện đang được sử dụng để sắp xếp là điều phổ biến.

Trong bài viết hướng dẫn này, tôi sẽ cung cấp cho bạn một giải pháp thiết thực cung cấp cho chúng tôi tất cả những điều trên, nhưng xin lưu ý rằng một số code ở đây vượt xa những gì chúng ta đã học cho đến nay - đó là lý do tại sao nó có nhãn "cách thực hiện".

Bài viết này được xây dựng dựa trên phần trước, nhưng tôi vẫn sẽ giải thích từng phần. Đây là mục tiêu của chúng tôi - một ListView với sắp xếp cột, bao gồm chỉ thị trực quan của trường sắp xếp và hướng. Người dùng chỉ cần nhấp vào một cột để sắp xếp theo và nếu cùng một cột được nhấp lại, hướng sắp xếp được đảo ngược. Đây là vẻ ngoài của nó:

XAML

Điều đầu tiên chúng ta cần là một file XAML để xác định giao diện người dùng. Hiện tại nó trông như thế này:

<Window x:Class="WpfTutorialSamples.ListView_control.ListViewColumnSortingSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ListViewColumnSortingSample" Height="200" Width="350">
    <Grid Margin="10">
        <ListView Name="lvUsers">
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="120" DisplayMemberBinding="{Binding Name}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Name" Click="lvUsersColumnHeader_Click">Name</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn Width="80" DisplayMemberBinding="{Binding Age}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Age" Click="lvUsersColumnHeader_Click">Age</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                    <GridViewColumn Width="80" DisplayMemberBinding="{Binding Sex}">
                        <GridViewColumn.Header>
                            <GridViewColumnHeader Tag="Sex" Click="lvUsersColumnHeader_Click">Sex</GridViewColumnHeader>
                        </GridViewColumn.Header>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</Window>

Lưu ý cách tôi đã chỉ định các tiêu đề cho từng cột bằng cách sử dụng phần tử GridViewColumnHeader thực tế thay vì chỉ định một chuỗi. Điều này được thực hiện để tôi có thể đặt các thuộc tính bổ sung, trong trường hợp này là thuộc tính Tag cũng như sự kiện Click.

Thuộc tính Tag được sử dụng để giữ tên trường sẽ được sử dụng để sắp xếp, nếu cột này được nhấp. Điều này được thực hiện trong sự kiện lvUsersColumnHeader_Click mà mỗi cột đăng ký.

Đó là những khái niệm chính của XAML. Bên cạnh đó, chúng tôi liên kết với Name, Age và Sex của Code-behind, nơi mà chúng ta sẽ thảo luận ngay bây giờ.

Code-behind

Trong Code-behind, có khá nhiều điều xảy ra. Tôi sử dụng tổng cộng ba lớp mà bạn thường chia thành các tệp riêng lẻ, nhưng để thuận tiện, tôi đã giữ chúng trong cùng một tệp, cung cấp cho chúng tôi tổng cộng ~ 100 dòng. Đầu tiên code và sau đó tôi sẽ giải thích cách nó hoạt động:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;

namespace WpfTutorialSamples.ListView_control
{
	public partial class ListViewColumnSortingSample : Window
	{
		private GridViewColumnHeader listViewSortCol = null;
		private SortAdorner listViewSortAdorner = null;

		public ListViewColumnSortingSample()
		{
			InitializeComponent();
			List<User> items = new List<User>();
			items.Add(new User() { Name = "John Doe", Age = 42, Sex = SexType.Male });
			items.Add(new User() { Name = "Jane Doe", Age = 39, Sex = SexType.Female });
			items.Add(new User() { Name = "Sammy Doe", Age = 13, Sex = SexType.Male });
			items.Add(new User() { Name = "Donna Doe", Age = 13, Sex = SexType.Female });
			lvUsers.ItemsSource = items;
		}

		private void lvUsersColumnHeader_Click(object sender, RoutedEventArgs e)
		{
			GridViewColumnHeader column = (sender as GridViewColumnHeader);
			string sortBy = column.Tag.ToString();
			if(listViewSortCol != null)
			{
				AdornerLayer.GetAdornerLayer(listViewSortCol).Remove(listViewSortAdorner);
				lvUsers.Items.SortDescriptions.Clear();
			}

			ListSortDirection newDir = ListSortDirection.Ascending;
			if(listViewSortCol == column && listViewSortAdorner.Direction == newDir)
				newDir = ListSortDirection.Descending;

			listViewSortCol = column;
			listViewSortAdorner = new SortAdorner(listViewSortCol, newDir);
			AdornerLayer.GetAdornerLayer(listViewSortCol).Add(listViewSortAdorner);
			lvUsers.Items.SortDescriptions.Add(new SortDescription(sortBy, newDir));
		}
	}

	public enum SexType { Male, Female };

	public class User
	{
		public string Name { get; set; }

		public int Age { get; set; }

		public string Mail { get; set; }

		public SexType Sex { get; set; }
	}

	public class SortAdorner : Adorner
	{
		private static Geometry ascGeometry =
			Geometry.Parse("M 0 4 L 3.5 0 L 7 4 Z");

		private static Geometry descGeometry =
			Geometry.Parse("M 0 0 L 3.5 4 L 7 0 Z");

		public ListSortDirection Direction { get; private set; }

		public SortAdorner(UIElement element, ListSortDirection dir)
			: base(element)
		{
			this.Direction = dir;
		}

		protected override void OnRender(DrawingContext drawingContext)
		{
			base.OnRender(drawingContext);

			if(AdornedElement.RenderSize.Width < 20)
				return;

			TranslateTransform transform = new TranslateTransform
				(
					AdornedElement.RenderSize.Width - 15,
					(AdornedElement.RenderSize.Height - 5) / 2
				);
			drawingContext.PushTransform(transform);

			Geometry geometry = ascGeometry;
			if(this.Direction == ListSortDirection.Descending)
				geometry = descGeometry;
			drawingContext.DrawGeometry(Brushes.Black, null, geometry);

			drawingContext.Pop();
		}
	}
}

Cho phép tôi bắt đầu từ phía dưới và sau đó làm việc theo cách của mình trong khi giải thích những gì xảy ra. Lớp cuối cùng trong tệp là một lớp Adorner có tên là SortAdorner. Tất cả các lớp nhỏ này làm là vẽ một hình tam giác, chỉ lên hoặc xuống, tùy thuộc vào hướng sắp xếp. WPF sử dụng khái niệm tô điểm để cho phép bạn vẽ các thứ trên các điều khiển khác và đây chính xác là những gì chúng ta muốn ở đây: Khả năng vẽ một tam giác sắp xếp trên đầu tiêu đề cột ListView của chúng tôi.

SortAdorner hoạt động bằng cách xác định hai đối tượng Hình học, về cơ bản được sử dụng để mô tả các hình dạng 2D - trong trường hợp này là một hình tam giác với đầu nhọn hướng lên và một đối tượng với đầu nhọn hướng xuống. Phương thức Geometry.Parse() sử dụng danh sách các điểm để vẽ các hình tam giác, sẽ được giải thích kỹ hơn trong một bài viết sau.

SortAdorner nhận thức được hướng sắp xếp, bởi vì nó cần vẽ tam giác thích hợp, nhưng không nhận thức được trường mà chúng ta đặt hàng theo - điều này được xử lý trong lớp UI.

User chỉ là một lớp thông tin cơ bản, được sử dụng để chứa thông tin về người dùng. Một số thông tin này được sử dụng trong lớp UI, nơi chúng tôi liên kết với các thuộc tính Tên, Tuổi và Giới tính.

Trong Window class, chúng tôi có hai phương thức: Trình xây dựng nơi chúng tôi xây dựng danh sách người dùng và gán nó cho ItemSource của ListView, và sau đó trình xử lý sự kiện nhấp chuột thú vị hơn sẽ được nhấn khi người dùng nhấp vào một cột. Trong phần đầu, chúng tôi đã xác định hai biến riêng: listViewSortCollistViewSortAdorner. Những điều này sẽ giúp chúng tôi theo dõi cột nào chúng tôi hiện đang sắp xếp và nơi mà chúng tôi đặt để chỉ ra cột đó.

Trong trình xử lý sự kiện lvUsersColumnHeader_Click, chúng tôi bắt đầu bằng cách lấy tham chiếu đến cột mà người dùng đã nhấp. Với điều này, chúng ta có thể quyết định thuộc tính nào trên lớp User để sắp xếp, chỉ bằng cách xem thuộc tính Thẻ mà chúng ta đã xác định trong XAML. Sau đó, chúng tôi sẽ kiểm tra xem chúng tôi đã sắp xếp theo một cột chưa - nếu đó là trường hợp, chúng tôi sẽ loại bỏ các góc và xóa các mô tả sắp xếp hiện tại.

Sau đó, chúng tôi đã sẵn sàng để quyết định hướng đi. Mặc định là tăng dần, nhưng chúng tôi kiểm tra xem liệu chúng tôi đã sắp xếp theo cột mà người dùng đã nhấp chưa - nếu đó là trường hợp, chúng tôi thay đổi hướng thành giảm dần.

Cuối cùng, chúng tôi tạo một SortAdorner mới, chuyển qua cột mà nó sẽ được hiển thị, cũng như hướng. Chúng tôi thêm phần này vào AdornerLayer của tiêu đề cột và cuối cùng, chúng tôi thêm một Mô tả sắp xếp vào ListView, để cho nó biết thuộc tính nào được sắp xếp theo và theo hướng nào.

Tổng kết

Xin chúc mừng, bây giờ bạn có một ListView hoàn toàn có thể sắp xếp với chỉ dẫn trực quan về cột và hướng sắp xếp. Trong trường hợp bạn muốn biết thêm về một số khái niệm được sử dụng trong bài viết này, như liên kết dữ liệu, hình học hoặc ListView nói chung, thì vui lòng kiểm tra một số bài viết khác, trong đó mỗi chủ đề được đề cập sâu.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!