TOC

This article is currently in the process of being translated into Polish (~99% done).

Kontrolka ListView:

How-to: ListView with column sorting

W ostatnim rozdziale widzieliśmy, w jaki sposób możemy łatwo sortować ListView z poziomu Code-behind, i choć w niektórych przypadkach jest to wystarczające, nie pozwala to użytkownikowi końcowemu na decydowanie o sortowaniu. Poza tym, nie było wskazania, według której kolumny posortowany został ListView. W systemie Windows i ogólnie w wielu interfejsach użytkownika ogólnie rzecz biorąc, często ilustruje się kierunki sortowania na liście, rysując trójkąt obok nazwy kolumny aktualnie używanej do sortowania.

W tym artykule przedstawię praktyczne rozwiązanie, które daje nam wszystkie powyższe, ale należy pamiętać, że część kodu tutaj wykracza nieco poza to, czego nauczyliśmy się do tej pory - dlatego etykieta "jak to zrobić". wykracza poza to, czego nauczyliśmy się do tej pory - dlatego ma etykietę "jak to zrobić".

Ten artykuł opiera się na poprzednim, ale nadal będę wyjaśniał każdą część w miarę postępów. Oto nasz cel - widok listy z sortowaniem kolumn, w tym wizualne wskazanie pola i kierunku sortowania. Użytkownik po prostu klika kolumnę do sortowania, a jeśli ta sama kolumna zostanie kliknięta ponownie, kierunek sortowania zostanie odwrócony. Oto jak to wygląda:

Oto XAML

Pierwszą rzeczą, której potrzebujemy, jest trochę XAML, aby zdefiniować nasz interfejs użytkownika. Obecnie wygląda on następująco:

<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>

Zwróć uwagę, jak określiłem nagłówki dla każdej z kolumn, używając elementu GridViewColumnHeader, zamiast po prostu określać ciąg znaków. Dzięki temu mogę ustawić dodatkowe właściwości - w tym przypadku właściwość Tag jak i zdarzenie Click.

Właściwość Tag jest używana do przechowywania nazwy pola, które będzie używane do sortowania, jeśli ta konkretna kolumna zostanie kliknięta. Odbywa się to w przypadku wydarzenia lvUsersColumnHeader_Click, którego subskrybuje każda z kolumn.

To były kluczowe koncepcje XAML. Poza tym wiążemy się z naszymi właściwościami Code-behind Nazwa, Wiek i Płeć, które teraz omówimy.

Kod źródłowy

W Kodzie źródłowym dzieje się całkiem sporo rzeczy. Używam w sumie trzech klas, które normalnie można by podzielić na osobne pliki, ale dla wygody trzymam je w tym samym pliku, co daje nam w sumie ~100 linii. Najpierw kod, a następnie wyjaśnię, jak to działa:

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

Pozwólcie, że zacznę od dołu, a następnie przejdę do góry, wyjaśniając, co się dzieje. Ostatnią klasą w pliku jest klasa Adorner o nazwie SortAdorner. Wszystko, co robi ta mała klasa, to rysowanie trójkąta skierowanego w górę lub w dół, w zależności od kierunku sortowania. WPF używa aby umożliwić malowanie elementów na innych kontrolkach i właśnie tego tutaj chcemy: Możliwość narysowania trójkąta sortowania na górze naszego nagłówka kolumny ListView.

SortAdorner działa poprzez zdefiniowanie dwóch obiektów Geometry, które są zasadniczo używane do opisywania kształtów 2D - w tym przypadku trójkąt z końcówką skierowaną w górę i jeden z końcówką skierowaną w dół. Metoda Geometry.Parse() wykorzystuje listę punktów do narysowania trójkątów, co zostanie zostanie dokładniej wyjaśnione w późniejszym artykule.

SortAdorner jest świadomy kierunku sortowania, ponieważ musi narysować odpowiedni trójkąt, ale nie jest świadomy pola, które zamawiamy według - jest to obsługiwane w warstwie interfejsu użytkownika.

Klasa User jest tylko podstawową klasą informacyjną, używaną do przechowywania informacji o użytkowniku. Niektóre z tych informacji są wykorzystywane w warstwie UI gdzie wiążemy się z właściwościami Nazwa, Wiek i Płeć.

W klasie Window , mamy dwie methody: Konstruktor, w którym tworzymy listę użytkowników i przypisaliśmy do niego ItemsSource naszej ListView a później bardziej interesująca obsługa zdarzenia kliknięcia, która zostanie uruchomiona, gdy użytkownik kliknie kolumnę. W górnej części klasy zdefiniowaliśmy dwie zmienne prywatne: listViewSortCol i listViewSortAdorner.Pomogą nam one śledzić, według której kolumny aktualnie sortujemy, oraz ozdobnik, który umieściliśmy, aby to wskazać.

W obsłudze zdarzenia lvUsersColumnHeader_Click zaczynamy od uzyskania odniesienia do kolumny, którą kliknął użytkownik. Dzięki temu możemy zdecydować, którą w klasie User, po prostu patrząc na właściwość Tag, którą zdefiniowaliśmy w XAML. Następnie sprawdzamy, czy już sortujemy według kolumny - jeśli tak, to usuwamy ją. Jeśli tak, usuwamy adorner i czyścimy bieżące opisy sortowania.

Następnie możemy zdecydować o kierunku sortowania. Domyślnie jest on rosnący, ale sprawdzamy, czy już sortujemy według kolumny, którą kliknął użytkownik - jeśli tak, zmieniamy kierunek na malejący. kliknął użytkownik - jeśli tak jest, zmieniamy kierunek na malejący.

Na koniec tworzymy nowy SortAdorner, przekazując kolumnę, na której ma być renderowany, a także kierunek. Dodajemy to do warstwy AdornerLayer nagłówka kolumny nagłówka kolumny, a na samym końcu dodajemy SortDescription do ListView, aby poinformować go, według której właściwości ma sortować i w jakim kierunku.

Podsumowanie

Gratulacje, masz teraz w pełni sortowalny ListView z wizualnym wskazaniem kolumny i kierunku sortowania. Jeśli chcesz dowiedzieć się więcej o niektórych pojęć użytych w tym artykule, takich jak wiązanie danych, geometria lub ogólnie ListView, zapoznaj się z innymi artykułami, w których każdy z tych tematów został dogłębnie omówiony. tematy są omówione dogłębnie.


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!
Table of Contents