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 GridViewColumnHeader elementu, zamiast po prostu określać ciąg znaków. This is done aby móc ustawić dodatkowe właściwości, w tym przypadkuTag właściwośc jak również poprzez zdarzenie Click

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

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