This article has been localized into German by the community.
ListView mit Spaltensortierung
Im letzten Kapitel haben wir gesehen, wie man einen ListView leicht vom Code-behind aus sortieren kann. Obwohl dies für viele Fälle ausreicht, erlaubt es dem Endbenutzer nicht, selbst über die Sortierung zu entscheiden. Außerdem gab es keinen Hinweis darauf, nach welcher Spalte die ListView sortiert war. In Windows und in vielen anderen Benutzeroberflächen ist es üblich, Sortierrichtungen einer Liste zu veranschaulichen, indem ein Dreieck neben dem Spaltennamen angezeigt wird, nach dem derzeit sortiert wird.
In diesem How-To-Artikel gebe ich Ihnen eine praktische Lösung, die uns alle oben genannten Punkte liefert, aber bitte beachten Sie, dass ein Teil des Codes hier ein bisschen über das hinausgeht, was wir bisher gelernt haben - deshalb hat es den "Gewusst Wie"- Zusatz.
Dieser Artikel baut auf dem vorherigen auf, aber ich werde immer noch jeden Teil erklären, während wir weitermachen. Hier ist unser Ziel - ein ListView mit Spaltensortierung, einschließlich visueller Anzeige von Sortierspalte und Richtung zu erstellen. Der Benutzer klickt einfach auf eine Spalte, nach der sortiert werden soll. Wenn dieselbe Spalte erneut angeklickt wird, wird die Sortierrichtung umgekehrt. Das sieht dann so aus:
Der XAML-Code
Das erste, was wir dafür benötigen, um unsere Oberfläche zu gestalten, ist etwas XAML-Code. Im Moment sieht er wie folgt aus:
<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>
Beachten Sie, wie ich die Header für jede Spalte angegeben habe, indem ich ein GridViewColumnHeader-Element verwende, anstatt nur eine Zeichenfolge anzugeben. Das dient dazu, dass ich weitere Eigenschaften festlegen kann - in diesem Fall die Eigenschaft Tag sowie das Click -Ereignis.
Mit der Eigenschaft Tag wird der Feldname gespeichert, nach dem sortiert wird, wenn auf diese Spalte geklickt wird. Dies geschieht im Ereignis lvUsersColumnHeader_Click, welches jede Spalte abonniert.
Das ist eines der Schlüsselkonzepte von XAML. Außerdem binden wir uns an unsere Code-Behind-Eigenschaften Name, Alter und Geschlecht, die wir nun besprechen werden.
Der Code-Behind
In Code-behind gibt es eine ganze Menge Dinge, die passieren. Ich verwende insgesamt drei Klassen, die man normalerweise in einzelne Dateien aufteilen würde, aber aus Gründen der Übersichtlichkeit habe ich sie in die selbe Datei gelegt, so dass wir insgesamt ~100 Zeilen haben. Zuerst den Code und dann erkläre ich, wie es funktioniert:
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();
}
}
}
Lassen Sie mich von unten nach oben arbeiten und Ihnen dabei die Vorgehensweise zu erklären. Die letzte Klasse in der Datei ist die Adorner-Klasse namens SortAdorner. Alles was diese kleine Klasse macht, ist ein kleines Dreieck zu zeichnen, das entweder nach oben oder nach unten zeigt, abhängig von der Sortierrichtung. WPF verwendet das Konzept von Adornern, das es Ihnen erlaubt, Objekte über andere Steuerelemente zu zeichnen und genau das wollen wir hier: Die Möglichkeit, ein Sortierdreieck über unsere Spaltenüberschrift zu zeichnen.
Der SortAdorner definiert zwei Geometry -Objekte, die im Wesentlichen zur Beschreibung von 2D-Formen verwendet werden - in diesem Fall für ein Dreieck mit der Spitze nach oben und einem mit der Spitze nach unten. Die Geometry.Parse () -Methode verwendet eine Liste von Punkten, um die Dreiecke zu zeichnen, was ich in einem späteren Artikel noch ausführlicher erläutern werde.
Dem SortAdorner ist die Sortierrichtung bekannt, da er ja das richtige Dreieck zeichnen muss, aber es kennt nicht das Feld nach dem wir sortieren - dies geschieht in der UI-Ebene.
Die User-Klasse ist nur eine grundlegende Informationsklasse, die verwendet wird, um Informationen über einen Benutzer zu enthalten. Einige dieser Informationen werden in der Oberflächenschicht verwendet, wo wir an die Eigenschaften Name, Alter und Geschlecht binden.
In der Window-Klasse haben wir zwei Methoden: Den Konstruktor, in dem wir eine Liste von Benutzern erstellen und sie der ItemsSource unserer ListView zuweisen, und dann der interessantere Click-Event-Handler, der beim Anklicken einer Spalte durch den Benutzer gefeuert wird. Am Anfang der Klasse haben wir zwei private Variablen definiert: listViewSortCol und listViewSortAdorner. Diese helfen uns nachzuvollziehen, nach welcher Spalte wir gerade sortieren und welchen Adorner wir gerade verwenden, um die Sortierung anzuzeigen.
Im Ereignishandler lvUsersColumnHeader_Click erhalten wir zunächst einen Verweis auf die Spalte, auf die der Benutzer geklickt hat. Damit können wir entscheiden, nach welcher Eigenschaft der User-Klasse wir sortieren müssen, einfach indem wir die Tag-Eigenschaft betrachten, die wir in XAML definiert haben. Wir prüfen dann, ob wir bereits nach einer Spalte sortieren - wenn dies der Fall ist, entfernen wir den Adorner und löschen die aktuellen Sortierbeschreibungen.
Danach sind wir bereit, die Richtung zu bestimmen. Der Standardwert ist aufsteigend, aber wir prüfen, ob wir bereits nach der Spalte sortieren, auf die der Benutzer geklickt hat. Wenn das der Fall ist, ändern wir die Richtung auf absteigend.
Am Ende erstellen wir einen neuen SortAdorner, indem wir die zu rendernde Spalte sowie die Richtung übergeben. Wir fügen dies dem AdornerLayer der Spaltenüberschrift hinzu. Ganz am Ende fügen wir der ListView eine SortDescription hinzu, um sie darüber zu informieren, nach welcher Eigenschaft sortiert werden soll und in welche Richtung.
Zusammenfassung
Herzlichen Glückwunsch, Sie haben jetzt eine vollständig sortierbare ListView mit visueller Anzeige der Sortierspalte und -Richtung. Wenn Sie mehr die in diesem Artikel verwendeten Konzepte wie Datenbindung, Geometrie oder ListViews im Allgemeinen erfahren möchten, dann lesen Sie bitte die betreffenden Artikel, in denen das entsprechende Thema vertiefend behandelt wird.