This article has been localized into Czech by the community.
Jak na řazení sloupců v ListView
V poslední kapitole jsme viděli, jak bychom mohli snadno třídit ListView z Code-behind, což může stačit jen pro některé případy, ale neumožňuje to o třídění rozhodnout koncovému uživateli. Kromě toho nevidíme žádný údaj o tom, podle kterého sloupce ListView byla data seřazena. V systému Windows a v mnoha uživatelských rozhraních obecně je běžné ilustrovat v seznamu stav řazení vykreslením trojúhelníku vedle názvu sloupce, podle kterého se aktuálně řazení používá.
V tomto článku vám ukáži praktické řešení, které zahrnuje všechny výše uvedené postupy, ale upozorňuji, že některý kód jde trochu nad rámec toho, co jsme se až doposud naučili - a to je důvod, proč má tento článek název "jak-na ...".
Tento článek navazuje na předchozí, ale ještě jednou vysvětlím každou část znova, co znamená. Zde je náš výsledek - ListView s řazením sloupců, včetně vizuální indikace třídícího pole a směru třídění. Uživatel jednoduše klikne na sloupec, podle kterého se řadí, a pokud na stejný sloupec klepne opět, směr řazení se obrátí. Zde je návod, jak to udělat:
XAML
První věc, kterou potřebujeme, je nějaký XAML kód, kde bychom definovali naše uživatelské rozhraní. V současné době to vypadá takto:
<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>
Všimněte si, jak jsem zadal záhlaví pro každý sloupec pomocí elementu GridViewColumnHeader namísto pouhého zadání řetězce. To proto, abych mohl nastavit další vlastnosti, v tomto případě vlastnost Tag, a stejně tak událost Click.
Vlastnost Tag slouží k uložení názvu pole, podle kterého budeme třídit, pokud klepneme na konkrétní sloupec. Toto zajistí událost lvUsersColumnHeader_Click v případě, že se k ní každý ze sloupců přihlásí.
Toto je klíčový koncept XAML. Kromě toho v Code behind navážeme vlastnosti Jméno, věk a sex, což si ukážeme nyní.
Code-behind
V Code-behind se děje poměrně málo věcí. Používám celkem tři třídy, které byste normálně rozdělit do jednotlivých souborů, ale pro zjednodušení, jsem je nechal ve stejném souboru, o délce cca ~ 100 řádků. Nejprve se podíváme na kód a pak vysvětlím, jak to funguje:
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();
}
}
}
Dovolte mi začít s vysvětlováním od spodu a pak se pomalu pracovat nahoru s popisem, co se kde děje. Poslední třída v souboru je třída Adorner s názvem SortAdorner. Vše, co tato malá třída dělá, je nakreslit trojúhelník, buď směřující nahoru, nebo dolů, v závislosti na směru řazení. WPF používá koncept ozdoby, který umožní malovat věci přes jiné ovládací prvky, a to je přesně to, co zde chceme: Schopnost vykreslit trojúhelník řazení na vrcholu našeho záhlaví sloupce ListView.
SortAdorner funguje tak, že definuje dva geometrické objekty, které se v podstatě používají k popisu 2D obrazců - v tomto případě trojúhelník s hrotem směřujícím nahoru a jeden s hrotem směřujícím dolů. Metoda Geometry.Parse() používá seznam bodů k nakreslení trojúhelníků, a to důkladněji vysvětlím v článku později.
SortAdorner si je vědom směru řazení, protože potřebuje nakreslit správný trojúhelník, ale neví, podle kterého pole třídíme - to zpracovává vrstva řízení.
Třída User je pouze základní třída obsahující informace o uživateli. Některé z těchto informací se používají ve vrstvě uživatelského rozhraní, kde se vážeme na vlastnosti Jméno, Věk a Pohlaví.
Ve třídě Window máme dvě metody: Konstruktor, kde vytvoříme seznam uživatelů a přiřadíme jej ItemsSource našeho ListView a pak zajímavější obslužnou rutinu události kliknutí, která bude reagovat na uživatelovo kliknutí na sloupec. V horní části třídy jsme definovali dvě soukromé proměnné: listViewSortCol a listViewSortAdorner. Ty nám pomohou sledovat, který sloupec je použit pro třídění a kde jsme umístili patřičnou ozdobu (trojúhelník).
V obslužné rutině události lvUsersColumnHeader_Click začneme tím, že získáme odkaz na sloupec, na který uživatel klikl. S tímto způsobem zjistíme, podle které vlastnosti třídy User se budou položky třídit, a to jednoduše tak, že se podíváme na vlastnost Tag, kterou jsme definovali v XAML. Pak zkontrolujeme, zda již podle daného sloupce třídíme, a pokud tomu tak je, odstraníme ozdobu (trojúhelník) a vyčistíme aktuální značky řazení.
Následně jsme připraveni určit směr třídění. Výchozí hodnota je „vzestupně“, ale i tak provedeme kontrolu, zda již seřadíme podle sloupce, na který uživatel klikl - pokud tomu tak je, změníme směr na „sestupně“.
Nakonec vytvoříme nový SortAdorner, předáme jej sloupci, na kterém má být vykreslen směr třídění. Přidáme jej do záhlaví sloupce AdornerLayer a na samém konci přidáme SortDescription do ListView, aby bylo jasné, podle které vlastnosti se má třídit a ve kterém směru.
Shrnutí
Gratulujeme, nyní máte plně tříditelné ListView s vizuální indikací třídícího sloupce a směru třídění. V případě, že se chcete dozvědět více o některých konceptech použitých v tomto článku, jako jsou datové vazby, geometrie nebo ListView, pak se prosím podívejte na některé z dalších článků, kde probíráme každou z těchto záležitostí více do hloubky.