trapemiyaの日記

hatenablogが新しくなったんで新規一転また2019年1月からちょこちょこ書いてます。C#中心のプログラミングに関するお話です。

CollectionViewSourceを使ったグルーピングあれこれ【その1】

CollectionViewSourceを使った応用や問題点、およびその解決策について何回かに渡って紹介します。
今回は【その1】として、簡単なグルーピングを行った例を掲載します。これを元にいろいろ発展させていく予定です。

<Window x:Class="WPFGroupingExamples.単純GroupingView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFGroupingExamples"
        Title="単純GroupingView" Height="393" Width="693">
    
    <Window.DataContext>
        <local:単純GroupingViewModel />
    </Window.DataContext>
    
    <Window.Resources>

        <DataTemplate x:Key="グループヘッダTemplate">
            <StackPanel Background="#FFCDF2CD">
                <TextBlock Text="{Binding Name}" FontWeight="Bold"/>
                <Rectangle Height="1" Fill="Blue"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    
    <Grid>
        
        <ListView ItemsSource="{Binding UICollectionViewSource.View, Mode=OneWay}" Margin="0,0,0,37">

             <ListView.GroupStyle>
                <GroupStyle HeaderTemplate="{StaticResource グループヘッダTemplate}"/>
            </ListView.GroupStyle>

            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="大会ID" DisplayMemberBinding="{Binding 大会ID}" Width="Auto" />
                        <GridViewColumn Header="大会名" DisplayMemberBinding="{Binding 大会名}" Width="Auto" />
                        <GridViewColumn Header="ランナーID" DisplayMemberBinding="{Binding ランナーID}" Width="Auto" />
                        <GridViewColumn Header="ランナー名" DisplayMemberBinding="{Binding ランナー名}" Width="Auto" />
                        <GridViewColumn Header="タイム" DisplayMemberBinding="{Binding タイム}" Width="Auto" />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
        
        <Button Content="ソート・グループ化適用" Command="{Binding ソート・グループ化適応Command, Mode=OneWay}"
               Height="23" HorizontalAlignment="Left" Margin="100,329,0,0" Name="button1" VerticalAlignment="Top" Width="157" />
    
    </Grid>
    
</Window>

以下に出てくるRelayCommandについては、以下を参考にして下さい。

Model-View-ViewModel デザイン パターンによる WPF アプリケーション
http://msdn.microsoft.com/ja-jp/magazine/dd419663.aspx
using System;
using System.Collections.ObjectModel;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Input;
using WPFGroupingExamples.Helpers;

namespace WPFGroupingExamples
{
    class 単純GroupingViewModel
    {
        #region コンストラクタ

        public 単純GroupingViewModel()
        {
            ソート・グループ化適応Command = new RelayCommand(ソート・グループ設定);

            ObservableCollection<ランナー> uiobjects = new ObservableCollection<ランナー>();

            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(11, 59, 35) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 22, 31) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 1, ランナー名 = "関西太郎", タイム = new TimeSpan(12, 01, 28) });
            uiobjects.Add(new ランナー { 大会ID = 1, 大会名 = "サロマ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 16, 22) });
            uiobjects.Add(new ランナー { 大会ID = 2, 大会名 = "隠岐ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 45, 42) });
            uiobjects.Add(new ランナー { 大会ID = 3, 大会名 = "四万十川ウルトラ100km", ランナーID = 2, ランナー名 = "関東洋子", タイム = new TimeSpan(11, 14, 17) });

            UICollectionViewSource = new CollectionViewSource();
            UICollectionViewSource.Source = uiobjects;
        }

        #endregion

        #region プロパティ

        /// <summary>
        /// UIに表示するCollectionViewSourceを取得する。
        /// </summary>
        CollectionViewSource _UICollectionViewSource;
        public CollectionViewSource UICollectionViewSource { get { return _UICollectionViewSource; } set { _UICollectionViewSource = value; } }

        #endregion

        #region コマンド

        /// <summary>
        /// ソートおよびグループ化を実行するコマンド
        /// </summary>
        public ICommand ソート・グループ化適応Command { get; private set; }

        #endregion

        #region コマンドハンドラ

        /// <summary>
        /// ソートおよびグループ化を実行する。
        /// </summary>
        private void ソート・グループ設定(object obj)
        {
            SortDescription sortDescription;
            PropertyGroupDescription groupDescription;

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

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

            sortDescription = new SortDescription
            {
                PropertyName = "ランナーID",
                Direction = ListSortDirection.Ascending
            };
            UICollectionViewSource.SortDescriptions.Add(sortDescription);

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

            groupDescription = new ランナーGroupDescription
            {
                PropertyName = "ランナーID"
            };
            UICollectionViewSource.GroupDescriptions.Add(groupDescription);

            UICollectionViewSource.View.Refresh();
        }

        #endregion

        #region GroupDescription

        class ランナーGroupDescription : PropertyGroupDescription
        {
            public override object GroupNameFromItem(object item, int level, System.Globalization.CultureInfo culture)
            {
                var uiobject = (ランナー)item;

                return uiobject.ランナー名;
            }
        }

        #endregion
    }

    #region ランナー クラス

    class ランナー
    {
        public int 大会ID { get; set; }
        public string 大会名 { get; set; }
        public int ランナーID { get; set; }
        public string ランナー名 { get; set; }
        public TimeSpan タイム { get; set; }
    }

    #endregion

}

上記を実行すると以下の画面が表示されます。

「ソート・グループ化適用」ボタンを押すと、次のように表示されます。