WPF – Vlastní řazení v DataGridu


Když není možné použít klasické, osvědčené a přímočaré postupy (defaultní chování, SortMemberPath) k tomu, aby uživatel v DataGridu mohl pohodlně řadit kliknutím na hlavičku sloupce, je potřeba použít něco méně tradičního. A tento méně tradiční postup je popsán v tomto článku.

Problém nastává v případě, že máme ve sloupci nějakou složitější hodnotu a podle ní chceme řadit. Protože z tohoto obecného popisu to může být docela nejasné, předvedu to na jednoduchém scénáři:

Scénář

V DataGridu máme kolekci hráčů, hrajících za jeden fotbalový tým. Jeden hráč může nastoupit na různých pozicích a proto máme pro každou pozici počet minut, které na ní hráč odehrál – jednoduše realizováno slovníkem, kde klíčem je pozice a hodnotou počet minut. V DataGridu chceme zobrazovat počet minut ne celkově na hřišti, ale na aktuálně “analyzované” pozici – uživatel si tedy může vybrat, která pozice ho právě zajímá. Proto do řazení vstupuje také aktuálně vybraná pozice.

Poznámka: Zobrazením požadované hodnoty v příslušném sloupci se v tomto článku zabývat nebudu – vyřešeno pomocí konvertoru.

Implementace IComparer

Nejprve je nutné vytvořit si vlastní implementaci rozhraní IComparer, která se pro řazení využije. Takže pro popisovaný scénář vyřešeno takto:

public class PlayerStatisticsComparer : IComparer
{
    private readonly Position position;
    private readonly ListSortDirection direction;

    public PlayerStatisticsComparer(Position position, ListSortDirection direction)
    {
        this.position = position;
        this.direction = direction;
    }

    public int Compare(object x, object y)
    {
        if (x == null || y == null)
        {
            throw new ArgumentNullException();
        }

        if (!(x is Player) || !(y is Player))
        {
            throw new ArgumentException();
        }

        var xMinutes = 0;
        (x as Player).Statistics.TryGetValue(this.position, out xMinutes);

        var yMinutes = 0;
        (y as Player).Statistics.TryGetValue(this.position, out yMinutes);

        if (this.direction == ListSortDirection.Ascending)
        {
            return xMinutes - yMinutes;
        }
        else
        {
            return yMinutes - xMinutes;
        }
    }
}

Něco málo ke komparátoru – porovnává vždy dva hráče (v DataGridu řádky) a porovnává jejich statistiky na zadané pozici (zadané v konstruktoru). Také v konstruktoru přebírá směr řazení, který vyplývá z toho, jak se aktuálně řadí sloupec – od nejmenšího (ListSortDirection.Ascending) nebo od největšího (ListSortDirection.Descending). Ještě jedna vysvětlivka ke komparátoru – když hráč na této pozici neodehrál nic, použijeme 0.

Použití komparátoru při řazení podle sloupce

Kouzlo celého postupu je v přiřazení výše popsaného komparátoru k řazení v DataGridu. To se provede tak, že se u DataGridu přidá handler události Sorting:

this.PlayersDataGrid.Sorting += this.PlayersDataGrid_Sorting;

A teď to nejdůležitější, samotný kód, který zařídí použití komparátoru a tedy vlastního řazení:

private void PlayersDataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
    DataGridColumn column = e.Column;

    //Vlastní řazení chceme použít jen pro sloupec s hlavičkou "Statistiky"
    if (column.Header.ToString() == "Statistiky")
    {
        this.SortPlayersByStatistics(e, column);
    }
}

private void SortPlayersByStatistics(DataGridSortingEventArgs e, DataGridColumn column)
{
    //Otáčení směru po opětovném řazení
    column.SortDirection = (column.SortDirection != ListSortDirection.Ascending)
        ? ListSortDirection.Ascending : ListSortDirection.Descending;

    //Získáme si z datagridu vnitřní ListCollectionView a nastavíme mu vlastní komparátor
    var listCollectionView =
        (ListCollectionView)CollectionViewSource.GetDefaultView
            (
                this.PlayersDataGrid.ItemsSource
            );

    //Vytvoříme vlastní komparátor (s vybranou pozicí a směrem) a přiřadíme ho
    var comparer = new PlayerStatisticsComparer(this.SelectedPosition,
        column.SortDirection.Value);
    listCollectionView.CustomSort = comparer;

    //Označíme řazení za provedené
    e.Handled = true;
}

Hned na začátku se zjistí, o jaký sloupec se jedná a podle toho se bude řazení provádět – v tomto případě implementujeme vlastní řazení jen pro statistiky (ostatní se bude řadit defaultně), ale samozřejmě není problém implementovat tímto způsobem různé řazení pro různé sloupce.

Samotné řazení podle statistik probíhá tak, že se nejprve určí a nastaví sloupci směr řazení (při opakovaném kliknutí na hlavičku sloupce se směr řazení má otáčet), poté se získá z DataGridu ListCollectionView, kterému lze nastavovat vlastní komparátor pro řazení. Požadovaný komparátor se poté vytvoří (předává se mu určený směr a také vybraná pozice) a nastaví se získanému ListCollectionView, čímž dojde k seřazení. Poté už jen řekneme, že je hotovo, aby dále nedocházelo i k defaultnímu řazení, a řádky v DataGridu jsou seřazené požadovaným způsobem.

, , ,

Komentáře jsou uzavřeny.