TOC

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

Il controllo ListView:

How-to: ListView con ordinamento per colonna

Nell'ultimo capitolo abbiamo visto come potremmo facilmente ordinare un ListView da Code-behind e, sebbene ciò sia sufficiente per alcuni casi, non consente all'utente finale di decidere l'ordinamento. Oltre a ciò, non vi era alcuna indicazione su quale colonna il ListView fosse ordinato. In Windows e in molte interfacce utente in generale, è comune illustrare le direzioni di ordinamento in un elenco disegnando un triangolo accanto al nome della colonna attualmente utilizzata per ordinare.

In questo articolo, ti darò una soluzione pratica che ci fornisce tutto quanto specificato sopra, ma tieni presente che parte del codice va un po 'oltre ciò che abbiamo imparato finora - ecco perché il titolo ha l'etichetta "How-to".

Questo articolo si basa su quello precedente, ogni parte verrà spiegata man mano. Ecco il nostro obiettivo: una ListView con ordinamento sulle colonne, inclusa l'indicazione visiva del campo di ordinamento e della direzione. L'utente fa semplicemente click su una colonna per ordinare e, se fa nuovo click sulla stessa colonna, la direzione di ordinamento viene invertita. Ecco come appare:

The XAML

The first thing we need is some XAML to define our user interface. It currently looks like this:

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

Nota come ho specificato le intestazioni per ciascuna delle colonne usando un vero elemento GridViewColumnHeader invece di specificare semplicemente una stringa. Questo è fatto in modo che io possa impostare proprietà aggiuntive, in questo caso la proprietà Tag e l'evento Click .

La proprietà Tag viene utilizzata per contenere il nome del campo che verrà utilizzato per l'ordinamento, se si fa click su questa colonna specifica. Questo viene fatto nell'evento lvUsersColumnHeader_Click su cui ciascuna delle colonne è in ascolto.

Questi erano i concetti chiave di XAML. Inoltre, ci leghiamo alle nostre proprietà Code-behind name, Age e Sex, di cui discuteremo ora.

The Code-behind

In Code-behind, ci sono molti concetti. Uso un totale di tre classi, che normalmente divideresti in singoli file, ma per per comodità, li ho conservati nello stesso file, dandoci un totale di ~ 100 righe. Prima vediamo il codice e poi ti spiego come funziona:

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

Consentitemi di iniziare dal basso e poi risalire spiegando cosa succede. L'ultima classe nel file è una classe Adorner chiamata SortAdorner . Tutto ciò che fa questa piccola classe è disegnare un triangolo, rivolto verso l'alto o verso il basso, a seconda della direzione di ordinamento. WPF utilizza il concetto di ornatori per permetterti di dipingere oggetti su altri controlli, e questo è esattamente ciò che vogliamo qui: la possibilità di disegnare un triangolo di ordinamento in cima della nostra intestazione di colonna ListView.

Il SortAdorner funziona definendo due oggetti Geometry , che vengono sostanzialmente utilizzati per descrivere le forme 2D; in questo caso un triangolo con la punta rivolta verso l'alto e uno con la punta rivolta verso il basso. Il metodo Geometry.Parse () utilizza l'elenco di punti per disegnare i triangoli, che lo costituiscono. E' spiegato più approfonditamente in un articolo successivo.

SortAdorner conosce la direzione di ordinamento, perché deve disegnare il triangolo corretto, ma non conosce il campo che ordiniamo. Esso viene gestito nel livello dell'interfaccia utente.

The User class is just a basic information class, used to contain information about a user. Some of this information is used in the UI layer, where we bind to the Name, Age and Sex properties.

Nella classe Window, abbiamo due metodi: il costruttore in cui costruiamo un elenco di utenti e lo assegniamo alla ItemsSource della nostra ListView, e il gestore di eventi click. Esso verrà invocato quando l'utente fa click su una colonna. Nella parte superiore della classe, abbiamo definito due variabili private: listViewSortCol e listViewSortAdorner . Queste ci aiuteranno a tenere traccia circa la che colonna stiamo attualmente ordinando e l'ornatore.

Nel gestore di eventi lvUsersColumnHeader_Click, come prima cosa otteniamo un riferimento alla colonna su cui l'utente ha fatto click. Con questa indicazione, semplicemente guardando la proprietà Tag che abbiamo definito in XAML, possiamo decidere quale è la proprietà della classe User da ordinare. Quindi controlliamo se stiamo già ordinando per colonna; in tal caso, rimuoviamo l'ornatore e cancelliamo le descrizioni di ordinamento correnti.

Dopodiché, siamo pronti a decidere la direzione. L'impostazione predefinita è crescente. Se è già stato fatto un precedente ordinemento su quella colonna, cambieremo la direzione in decrescente.

Alla fine, creiamo un nuovo SortAdorner, passando la colonna su cui deve essere eseguito il rendering, nonché la direzione. Aggiungiamo questo ad AdornerLayer dell'intestazione di colonna e, alla fine, aggiungiamo un SortDescription a ListView, per fargli sapere da quale proprietà ordinare e in quale direzione.

Summary

Congratulazioni, ora hai un ListView completamente ordinabile con indicazione visiva della colonna e della direzione di ordinamento. Nel caso in cui desideri saperne di più su alcuni dei concetti utilizzati in questo articolo, come l'associazione dei dati, la geometria o ListView in generale, consulta alcuni degli altri articoli, in cui ciascuno degli argomenti è trattato in modo approfondito.

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!