< NULL 許容型 (Nullable) | Word の表を削除する >

February 2, 2009

続:添付プロパティにしちゃえばいいんじゃない ?

添付プロパティにしちゃえばいいんじゃない ?」の続きです。

要するに、SortDescription を設定することで ListView をソートする場合、ヘッダクリックした際にどのプロパティをキーにしてソートすればよいかを保持しておく必要があります。GridViewColumn や GridViewColumnHeader にそれを保持しようとすると「A Sortable GridView (I mean ListView) Control in WPF(翻訳中)」などのように GridViewColumn を継承したクラスを定義したりオオゴトになってしまうのですが、WPF には添付プロパティってものがあるやんって話です。

なので、こんなクラスで。

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace SampleApp
{
    public class GridViewSortHelper
    {
        protected ListView  listView;
        protected GridViewColumnHeader lastSortedHeader;
        protected ListSortDirection lastSortDirection = ListSortDirection.Ascending;

        /// <summary>
        /// <c>GridViewSortHelper.SortKey</c> 添付プロパティ
        /// </summary>
        /// <value>ソートに使用するキー文字列</value>
        public static readonly DependencyProperty SortKeyProperty =
            DependencyProperty.RegisterAttached ("SortKey", typeof (string), typeof (GridViewSortHelper), new FrameworkPropertyMetadata (""));

        /// <summary>
        /// 要素に <c>GridViewSortHelper.SortKey</c> 添付プロパティを設定します。
        /// </summary>
        /// <param name="element">プロパティ値の書き込み対象の要素。</param>
        /// <param name="value">要素の <c>GridViewSortHelper.SortKey</c> 属性値</param>
        public static void SetSortKey (GridViewColumn element, string value)
        {
            element.SetValue (SortKeyProperty, value);
        }

        /// <summary>
        /// 要素の <c>GridViewSortHelper.SortKey</c> 添付プロパティを取得します
        /// </summary>
        /// <param name="element"> プロパティ値の読み取り元の要素。</param>
        /// <returns>要素の <c>GridViewSortHelper.SortKey</c> 属性値</returns>
        public static string GetSortKey (GridViewColumn element)
        {
            return (string) element.GetValue (SortKeyProperty);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="listView">ソート対象の ListView</param>
        public GridViewSortHelper (ListView listView)
        {
            this.listView = listView;
        }

        /// <summary>
        /// ヘッダクリック時処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnHeaderClicked (object sender, RoutedEventArgs e)
        {
            var headerClicked = e.OriginalSource as GridViewColumnHeader;
            if (headerClicked != null)
            {
                var direction = ListSortDirection.Ascending;
                if (headerClicked == lastSortedHeader)
                {
                    if (lastSortDirection == ListSortDirection.Ascending)
                    {
                        direction = ListSortDirection.Descending;
                    }
                }
                var key = GetSortKey (headerClicked.Column);

                var dataView = CollectionViewSource.GetDefaultView (this.listView.ItemsSource);
                dataView.SortDescriptions.Clear ();
                var sortDescription = new SortDescription (key, direction);
                dataView.SortDescriptions.Add (sortDescription);
                dataView.Refresh ();

                lastSortedHeader = headerClicked;
                lastSortDirection = direction;
            }
        }
    }
}

使い方ですが、例えば XAML ではこんな感じで、GridViewColumnHeader に GridViewSortHelper.SortKey を設定しておきます。

<Window
    x:Class="SampleApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SampleApp"
    Title="GridViewSortHelper のテスト">
    <Window.Resources>
        <XmlDataProvider XPath="/" x:Key="BeansData">
            <x:XData>
                <categories name="すべてのカテゴリ" xmlns="">
                    <category name="ささげ類">
                        <items name="すべてのささげ類">
                            <item name="あずき"/>
                            <item name="ささげ"/>
                            <item name="大納言"/>
                        </items>
                    </category>
                    <category name="いんげん類">
                        <items name="すべてのいんげん類">
                            <item name="大正金時豆"/>
                            <item name="うずら豆"/>
                            <item name="てぼ豆"/>
                            <item name="虎豆"/>
                            <item name="キドニービーン"/>
                            <item name="大福豆"/>
                            <item name="白花豆"/>
                        </items>
                    </category>
                    <category name="そらまめ類">
                        <items name="すべてのそらまめ類">
                            <item name="そらまめ"/>
                        </items>
                    </category>
                    <category name="えんどう類">
                        <items name="すべてのえんどう類">
                            <item name="えんどう"/>
                        </items>
                    </category>
                    <category name="だいず類">
                        <items name="すべてのだいず類">
                            <item name="大豆"/>
                            <item name="黒大豆"/>
                            <item name="青大豆"/>
                        </items>
                    </category>
                </categories>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    
    <ListView x:Name="BeansList"
              ItemsSource="{Binding Source={StaticResource BeansData}, XPath=/categories/category/items/item}"
              GridViewColumnHeader.Click="GridViewColumnHeader_Click">
        <ListView.View>
            <GridView>
                <GridViewColumn local:GridViewSortHelper.SortKey="@name" Header="種類" DisplayMemberBinding="{Binding XPath=@name}"/>
                <GridViewColumn local:GridViewSortHelper.SortKey="../../@name" Header="分類" DisplayMemberBinding="{Binding XPath=../../@name}"/>
            </GridView>
        </ListView.View>        
    </ListView>

</Window>

例は XmlDataProvider を ItemsSource に指定しているので XPath を指定していますが、要は SortDirection の引数に与える文字列を指定します。オブジェクトの場合はプロパティですが、複雑な PropertyPath 構文を解釈できるかどうかは View の種類によるようです。←指定方法についてはまた参考リンクを貼ります。今時間がなくて…。

で、コードビハインドでは、GridViewSortHelper のインスタンスを生成し、ヘッダクリックイベントをハンドリングしてメソッドを呼び出します。

namespace SampleApp
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        private GridViewSortHelper sortHelper;

        public Window1 ()
        {
            InitializeComponent ();
            sortHelper = new GridViewSortHelper (BeansList);
        }

        private void GridViewColumnHeader_Click (object sender, RoutedEventArgs e)
        {
            sortHelper.OnHeaderClicked (sender, e);
        }
    }
}

関連エントリ
メモ: ソート可能な ListView/メモ: Custom Sort for DataBound WPF ListView

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/2312

コメント

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?