[WPF/xaml] ListViewをグループ表示/ソート表示する(C#コードで書く)


もくじ

やりたいこと

Grid表示のListViewの中身を、ある列の内容でグループ表示したい。下記のようなイメージ。

やり方

<CollectionViewSource>を使う。
前回の記事で、xamlにCollectionViewSourceを書いてみたので、今回はコードに書くやり方を試してみる。
(今回はついでにソートも実施)

MainWindow.xaml
<Window x:Class="WpfApp48.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp48"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="200">
    <Window.Resources>
        <!-- グループのヘッダのスタイルを作成 -->
        <DataTemplate x:Key="GroupHeaderTemplate">
            <Border Background="#FFCDF2CD">
                <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
            </Border>
        </DataTemplate>
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="10*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>

        <!-- ListViewのItemsSourceをCollectionViewSourceのViewにするのがポイント -->
        <ListView Name="MainList" ItemsSource="{Binding UICollectionViewSource.View}">

            <!-- グループのヘッダのスタイルを指定(これを指定しないと、グループのヘッダはナシになる) -->
            <ListView.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource GroupHeaderTemplate}" />
            </ListView.GroupStyle>
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="機体" DisplayMemberBinding="{Binding MachineName}" Width="80" />
                    <GridViewColumn Header="パイロット" DisplayMemberBinding="{Binding PilotName}" Width="80" />
                </GridView>
            </ListView.View>
        </ListView>

        <Button Grid.Row="1" Content="ボタン" Click="Button_Click"/>

    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;

namespace WpfApp48
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        #endregion

        // ガンダム情報を格納
        public ObservableCollection<MyData> DataList { get; set; } = new ObservableCollection<MyData>();

        // ソート/グルーピングに使うCollectionViewSource 
        CollectionViewSource _UICollectionViewSource;
        public CollectionViewSource UICollectionViewSource { get { return _UICollectionViewSource; } set { _UICollectionViewSource = value; } }

        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            // CollectionViewSourceのソースにDataListを設定
            UICollectionViewSource = new CollectionViewSource();
            UICollectionViewSource.Source = DataList;
        }

        // ボタンをおしたらデータの中身を追加
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            DataList.Add(new MyData() { MachineName = "ガンダム", PilotName = "アムロ" });
            DataList.Add(new MyData() { MachineName = "シャアザク", PilotName = "シャア" });
            DataList.Add(new MyData() { MachineName = "グフ", PilotName = "誰やったっけ?" });
            DataList.Add(new MyData() { MachineName = "Zガンダム", PilotName = "カミーユ" });
            DataList.Add(new MyData() { MachineName = "ガンダムmk-Ⅱ", PilotName = "アムロ" });
            DataList.Add(new MyData() { MachineName = "ジ・オ", PilotName = "シロッコ" });
            DataList.Add(new MyData() { MachineName = "百式", PilotName = "シャア" });
            DataList.Add(new MyData() { MachineName = "ZZガンダム", PilotName = "ジュドー" });
            DataList.Add(new MyData() { MachineName = "νガンダム", PilotName = "アムロ" });

            //ソートの指定
            SortDescription sortDescription;
            UICollectionViewSource.SortDescriptions.Clear();

            sortDescription = new SortDescription
            {
                PropertyName = "MachineName",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            sortDescription = new SortDescription
            {
                PropertyName = "PilotName",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

            //グループの指定
            PropertyGroupDescription groupDescription;
            UICollectionViewSource.GroupDescriptions.Clear();

            groupDescription = new MyDataGroupDescription
            {
                // なにでグループを作るか決める
                PropertyName = "PilotName"
            };
            UICollectionViewSource.GroupDescriptions.Add(groupDescription);

            UICollectionViewSource.View.Refresh();
        }
    }

    // ガンダム情報クラス
    public class MyData
    {
        public string MachineName { get; set; }
        public string PilotName { get; set; }
    }

    class MyDataGroupDescription : PropertyGroupDescription
    {
        // データの中身からグループのヘッダに出すものを決める
        public override object GroupNameFromItem(object item, int level, System.Globalization.CultureInfo culture)
        {
            var uiobject = (MyData)item;

            return uiobject.PilotName;
        }
    }
}

参考

こちらのやり方をかなり参考にさせて頂いています。ありがとうございます。