This article is currently in the process of being translated into Polish (~99% done).
ListView grouping
Jak już wcześniej mówiliśmy, kontrolka ListView zdefiniowana w WPF jest bardzo elastyczna. Grupowanie to kolejna funkcja, którą obsługuje w sposób standardowy. Ponadto jest ona zarówno łatwa w użyciu jak też ma duże możliwości konfigurowania. Przejdźmy od razu do pierwszego przykładu, następnie go wyjaśnię, a później będziemy mogli użyć standardowych sztuczek WPF, aby jeszcze bardziej dostosować jej wygląd.
Na potrzeby tego artykułu wziąłem kod przykładowy z poprzedniego artykułu, a następnie rozszerzyłem go w taki sposób aby obsługiwał grupowanie. To wygląda tak:
<Window x:Class="WpfTutorialSamples.ListView_control.ListViewGroupSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListViewGroupSample" Height="300" Width="300">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
namespace WpfTutorialSamples.ListView_control
{
public partial class ListViewGroupSample : Window
{
public ListViewGroupSample()
{
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 });
lvUsers.ItemsSource = items;
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvUsers.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Sex");
view.GroupDescriptions.Add(groupDescription);
}
}
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; }
}
}
W XAML do ListView dodałem GroupStyle, w którym definiuję szablon nagłówka każdej grupy. Szablon ten składa się z kontrolki TextBlock, w której użyłem nieco większego i pogrubionego tekstu, aby pokazać, że jest to grupa — jak zobaczymy później, można to oczywiście dostosować w znacznie większym stopniu. Właściwość Text kontrolki TextBlock jest powiązana z właściwością Name, należy jednak pamiętać, że nie jest to właściwość Name obiektu danych (w tym przypadku klasy User). Zamiast tego jest to nazwa grupy przypisana przez WPF na podstawie właściwości, której używamy do dzielenia obiektów na grupy.
W Code-behind robimy to samo, co poprzednio: tworzymy listę i dodajemy do niej kilka obiektów User, a następnie wiążemy listę z ListView - nic nowego, za wyjątkiem nowej, dodanej przeze mnie właściwości Sex (płeć), która informuje czy użytkownik jest mężczyzną, czy kobietą.
Po przypisaniu ItemsSource, używamy go do uzyskania CollectionView, który tworzy dla nas ListView. Ta wyspecjalizowana instancja View daje wiele możliwości, w tym możliwość grupowania elementów. Grupowania takiego używamy dodając tak zwany PropertyGroupDescription do GroupDescriptions widoku. Zasadniczo mówi to WPF, aby grupował według określonej właściwości obiektów danych, w tym przypadku właściwości Sex (płeć).
Dostosowanie nagłówka grupy
Powyższy przykład świetnie nadawał się do pokazania podstaw grupowania w ListView ale jego wygląd był odrobinę nudny. Wykorzystajmy więc fakt, że WPF pozwala nam definiować własne szablony i wszystko to urozmaicać. Często potrzebna jest możliwość zwijania i rozwijania grupy i chociaż WPF domyślnie nie zapewnia takiego zachowania, to dość łatwo jest samodzielnie je zaimplementować. Zrobimy to poprzez całkowite przeprojektowanie kontenera grupy.
Może to wyglądać na nieco zagmatwane ale zastosowane tu zasady są dość proste i spotkasz je też w innych sytuacjach, w trakcie dostosowywania kontrolek WPF. Oto kod:
<Window x:Class="WpfTutorialSamples.ListView_control.ListViewCollapseExpandGroupSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListViewCollapseExpandGroupSample" Height="300" Width="300">
<Grid Margin="10">
<ListView Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
</Window>
Kod Code-behind jest dokładnie taki sam jak w pierwszym przykładzie - możesz przewinąć w górę i go użyć.
Teraz nasze grupy wyglądają nieco bardziej ekscytująco i zawierają nawet przycisk rozwijania, który po kliknięciu przełącza widoczność elementów grupy (to właśnie dlatego pojedyncza kobieta nie jest widoczna na zrzucie ekranu - zwinąłem tę konkretną grupę). Używając właściwości ItemCount udostępnianej przez grupę, możemy nawet pokazać z ilu elementów aktualnie składa się każda grupa.
Jak widać wymaga to nieco większej ilości znaczników w porównaniu do tego, do czego jesteśmy przyzwyczajeni ale też przykład wykracza nieco ponad to, co zwykle robimy, więc wydaje się to uzasadnione. Czytając kod, szybko zorientujesz się, że wiele linii to po prostu wspólne elementy, takie jak styl i szablon.
Podsumowanie
Dodawanie grupowania do kontrolek ListView w WPF jest bardzo proste - wszystko czego potrzebujesz, to GroupStyle z HeaderTemplate aby powiedzieć ListView w jaki sposób renderować grupę oraz kilka wierszy kodu Code-behind aby poinformować WPF, według której właściwości należy grupować. Jak widać na ostatnim przykładzie, grupy są bardzo konfigurowalne, co pozwala na tworzenie naprawdę fajnych widoków bez większego nakładu pracy.