17

ObservableCollection をカプセル化する CollectionViewSource にバインドされた WPF DataGrid があります。この CollectionViewSource には、次の 2 つの主な目的があります。

1) T の特定のプロパティで各項目をグループ化するには、GroupDescription で ValueConverter を使用して、必要なグループ化動作を取得しています。

2) グリッドを a) 主にグループ名 (上記で定義) および b) 個々のグループ項目でソートします。CollectionViewSource の「CustomSort」プロパティにカスタム IComparer をアタッチすることで、これを達成しています。

これはほとんどの場合うまく機能しますが、列ヘッダーがクリックされるとすぐに、並べ替えロジックがオーバーライドされます。並べ替えを無効にしたくありませんが、特定の列にカスタムの並べ替え順序を割り当てることができるかどうか疑問に思っていましたか?

少しわかりやすくするために、ユーザーが「ColumnA」をクリックするとします。現時点では、CustomSorter によってカプセル化された並べ替えロジックがオーバーライドされ、DataGrid がそのプロパティによって並べ替えられます。選択したプロパティで並べ替えるのではなく、代わりに CustomSorter のロジックを逆にしたいと思います。

4

10 に答える 10

33

この問題を処理するいくつかの添付プロパティを作成しました。これが誰かの役に立てば幸いです!

1 つ目 - 指向性比較子のシンプルなインターフェイス。これは IComparer を拡張しますが、もう 1 つのプロパティ (SortDirection) を提供します。実装では、これを使用して要素の正しい順序を決定する必要があります (そうしないと失われる可能性があります)。

public interface ICustomSorter : IComparer
{
    ListSortDirection SortDirection { get; set; }
}

次は付加されたビヘイビアです。これは 2 つのことを行います。1) カスタム ソート ロジック (AllowCustomSort=true) を使用するようにグリッドに指示し、b) 列ごとのレベルでこのロジックを設定する機能を提供します。

public class CustomSortBehaviour
{
    public static readonly DependencyProperty CustomSorterProperty =
        DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehaviour));

    public static ICustomSorter GetCustomSorter(DataGridColumn gridColumn)
    {
        return (ICustomSorter)gridColumn.GetValue(CustomSorterProperty);
    }

    public static void SetCustomSorter(DataGridColumn gridColumn, ICustomSorter value)
    {
        gridColumn.SetValue(CustomSorterProperty, value);
    }

    public static readonly DependencyProperty AllowCustomSortProperty =
        DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool),
        typeof(CustomSortBehaviour), new UIPropertyMetadata(false, OnAllowCustomSortChanged));

    public static bool GetAllowCustomSort(DataGrid grid)
    {
        return (bool)grid.GetValue(AllowCustomSortProperty);
    }

    public static void SetAllowCustomSort(DataGrid grid, bool value)
    {
        grid.SetValue(AllowCustomSortProperty, value);
    }

    private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var existing = d as DataGrid;
        if (existing == null) return;

        var oldAllow = (bool)e.OldValue;
        var newAllow = (bool)e.NewValue;

        if (!oldAllow && newAllow)
        {
            existing.Sorting += HandleCustomSorting;
        }
        else
        {
            existing.Sorting -= HandleCustomSorting;
        }
    }

    private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null || !GetAllowCustomSort(dataGrid)) return;

        var listColView = dataGrid.ItemsSource as ListCollectionView;
        if (listColView == null)
            throw new Exception("The DataGrid's ItemsSource property must be of type, ListCollectionView");

        // Sanity check
        var sorter = GetCustomSorter(e.Column);
        if (sorter == null) return;

        // The guts.
        e.Handled = true;

        var direction = (e.Column.SortDirection != ListSortDirection.Ascending)
                            ? ListSortDirection.Ascending
                            : ListSortDirection.Descending;

        e.Column.SortDirection = sorter.SortDirection = direction;
        listColView.CustomSort = sorter;
    }
}

これを使用するには、ICustomComparer (パラメーターなしのコンストラクターを使用) を XAML で実装します。

<UserControl.Resources>
    <converters:MyComparer x:Key="MyComparer"/>
    <!-- add more if you need them -->
</UserControl.Resources>
<DataGrid behaviours:CustomSortBehaviour.AllowCustomSort="True" ItemsSource="{Binding MyListCollectionView}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Test" Binding="{Binding MyValue}" behaviours:CustomSortBehaviour.CustomSorter="{StaticResource MyComparer}" />
    </DataGrid.Columns>
</DataGrid>
于 2013-08-13T20:49:23.230 に答える
3

この回答は trilson86 のソリューションと非常に似ていますが、それに基づいていましたがSortMemberPath、比較子に渡される値が行ではなく列の実際の値になるように説明しています。これにより、ソーターでの再利用がはるかに容易になります。さらに、カスタムの並べ替えインターフェイスがまったく不要になります。

DataGridSortBehavior.cs

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

namespace YourNamespace
{
    public class DataGridSortBehavior 
    {
        public static IComparer GetSorter(DataGridColumn column)
        {
            return (IComparer)column.GetValue(SorterProperty);
        }

        public static void SetSorter(DataGridColumn column, IComparer value)
        {
            column.SetValue(SorterProperty, value);
        }

        public static bool GetAllowCustomSort(DataGrid grid)
        {
            return (bool)grid.GetValue(AllowCustomSortProperty);
        }

        public static void SetAllowCustomSort(DataGrid grid, bool value)
        {
            grid.SetValue(AllowCustomSortProperty, value);
        }

        public static readonly DependencyProperty SorterProperty = DependencyProperty.RegisterAttached("Sorter", typeof(IComparer), 
            typeof(DataGridSortBehavior));
        public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), 
            typeof(DataGridSortBehavior), new UIPropertyMetadata(false, OnAllowCustomSortChanged));

        private static void OnAllowCustomSortChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var grid = (DataGrid)obj;

            bool oldAllow = (bool)e.OldValue;
            bool newAllow = (bool)e.NewValue;

            if (!oldAllow && newAllow)
            {
                grid.Sorting += HandleCustomSorting;
            }
            else
            {
                grid.Sorting -= HandleCustomSorting;
            }
        }

        public static bool ApplySort(DataGrid grid, DataGridColumn column)
        {
            IComparer sorter = GetSorter(column);
            if (sorter == null)
            {
                return false;
            }

            var listCollectionView = CollectionViewSource.GetDefaultView(grid.ItemsSource) as ListCollectionView;
            if (listCollectionView == null)
            {
                throw new Exception("The ICollectionView associated with the DataGrid must be of type, ListCollectionView");
            }

            listCollectionView.CustomSort = new DataGridSortComparer(sorter, column.SortDirection ?? ListSortDirection.Ascending, column.SortMemberPath);
            return true;
        }

        private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
        {
            IComparer sorter = GetSorter(e.Column);
            if (sorter == null)
            {
                return;
            }

            var grid = (DataGrid)sender;
            e.Column.SortDirection = e.Column.SortDirection == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
            if (ApplySort(grid, e.Column))
            {
                e.Handled = true;
            }
        }

        private class DataGridSortComparer : IComparer
        {
            private IComparer comparer;
            private ListSortDirection sortDirection;
            private string propertyName;
            private PropertyInfo property;

            public DataGridSortComparer(IComparer comparer, ListSortDirection sortDirection, string propertyName)
            {
                this.comparer = comparer;
                this.sortDirection = sortDirection;
                this.propertyName = propertyName;
            }

            public int Compare(object x, object y)
            {
                PropertyInfo property = this.property ?? (this.property = x.GetType().GetProperty(propertyName));
                object value1 = property.GetValue(x);
                object value2 = property.GetValue(y);

                int result = comparer.Compare(value1, value2);
                if (sortDirection == ListSortDirection.Descending)
                {
                    result = -result;
                }
                return result;
            }
        }
    }
}

あなたのXaml

これも trilson86 のソリューションに似ているはずです。

<UserControl.Resources>
    <converters:MyComparer x:Key="MyComparer"/>
</UserControl.Resources>
<DataGrid behaviours:DataGridSortBehavior.AllowCustomSort="True" ItemsSource="{Binding MyListCollectionView}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Test" Binding="{Binding MyValue}" behaviours:DataGridSortBehavior.Sorter="{StaticResource MyComparer}" />
    </DataGrid.Columns>
</DataGrid>
于 2016-04-15T00:07:43.000 に答える
2

@ trilson86の回答を変更して、DataGrid全体でカスタムソータークラスが1つだけ必要になるようにしました。

最初のインターフェイス:

public interface ICustomSorter : IComparer
{
    ListSortDirection SortDirection { get; set; }

    string SortMemberPath { get; set; }
}

次に、DateGridRow ではなく DataGrid で直接使用できるように CustomSorterProperty を定義する Bevaviour クラスです。HandleCustomSorting() では、CustomSorter のプロパティ SortMemberPath にクリックされた列の実際の値が入力されます。この値を CustomSorter で使用して、目的の列に対して並べ替えることができます。

public class CustomSortBehaviour
{
    #region Fields and Constants

    public static readonly DependencyProperty CustomSorterProperty =
        DependencyProperty.RegisterAttached("CustomSorter", typeof (ICustomSorter), typeof (CustomSortBehaviour));

    public static readonly DependencyProperty AllowCustomSortProperty =
        DependencyProperty.RegisterAttached("AllowCustomSort",
            typeof (bool),
            typeof (CustomSortBehaviour),
            new UIPropertyMetadata(false, OnAllowCustomSortChanged));



    #endregion

    #region public Methods

    public static bool GetAllowCustomSort(DataGrid grid)
    {
        return (bool) grid.GetValue(AllowCustomSortProperty);
    }


    public static ICustomSorter GetCustomSorter(DataGrid grid)
    {
        return (ICustomSorter)grid.GetValue(CustomSorterProperty);
    }

    public static void SetAllowCustomSort(DataGrid grid, bool value)
    {
        grid.SetValue(AllowCustomSortProperty, value);
    }


    public static void SetCustomSorter(DataGrid grid, ICustomSorter value)
    {
        grid.SetValue(CustomSorterProperty, value);
    }

    #endregion

    #region nonpublic Methods

    private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e)
    {
        var dataGrid = sender as DataGrid;
        if (dataGrid == null || !GetAllowCustomSort(dataGrid))
        {
            return;
        }

        var listColView = dataGrid.ItemsSource as ListCollectionView;
        if (listColView == null)
        {
            throw new Exception("The DataGrid's ItemsSource property must be of type, ListCollectionView");
        }

        // Sanity check
        var sorter = GetCustomSorter(dataGrid);
        if (sorter == null)
        {
            return;
        }

        // The guts.
        e.Handled = true;

        var direction = (e.Column.SortDirection != ListSortDirection.Ascending)
            ? ListSortDirection.Ascending
            : ListSortDirection.Descending;

        e.Column.SortDirection = sorter.SortDirection = direction;
        sorter.SortMemberPath = e.Column.SortMemberPath;

        listColView.CustomSort = sorter;
    }

    private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var existing = d as DataGrid;
        if (existing == null)
        {
            return;
        }

        var oldAllow = (bool) e.OldValue;
        var newAllow = (bool) e.NewValue;

        if (!oldAllow && newAllow)
        {
            existing.Sorting += HandleCustomSorting;
        }
        else
        {
            existing.Sorting -= HandleCustomSorting;
        }
    }

    #endregion
}

次のように XAML で使用できます。

<Window x:Class="..."
        xmlns:sorter="clr-namespace:...Sorting"
        ...
        >

    <Window.Resources>
        <sorter:CustomSorter x:Key="MySorter"/>
    </Window.Resources>

    <Grid>

        <DataGrid ItemsSource="{Binding ...}"
                  sorter:CustomSortBehaviour.AllowCustomSort="True"
                  sorter:CustomSortBehaviour.CustomSorter="{StaticResource MySorter}" >


            <DataGrid.Columns>
                <DataGridTextColumn Header="Column 1" Binding="{Binding Column1}"/>
                <DataGridTextColumn Header="Column 2" Binding="{Binding Column2}"/>
                <DataGridTextColumn Header="Column 3" Binding="{Binding Column3}"/>
            </DataGrid.Columns>

        </DataGrid>

    </Grid>
</Window>
于 2015-12-22T13:56:47.433 に答える
2

これは、OnSorting イベントをオーバーライドして自分で実装することで実現しました。

http://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid.onsorting.aspx

これは基本的に、ListCollectionView を再ソートすることを意味していました。

申し訳ありませんが、あまりにも詳細な答えではありません。

于 2013-08-08T10:22:30.650 に答える