3

您如何实现(最好纯粹在 XAML 中)WPF DataGrid 的列标题中的排序标记与 CollectionViewSource 中的当前排序顺序同步?

例如,我有以下示例代码,它显示了 C:\ 中所有文件的属性,按长度排序:

<Window x:Class="DataGridSortTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <CollectionViewSource x:Key="items" Source="{Binding Files}">
            <CollectionViewSource.SortDescriptions>
                <cm:SortDescription PropertyName="Length"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>
        <DataGrid ItemsSource="{Binding Source={StaticResource items}}"/>
    </Grid>
</Window>

MainWindow.xaml.cs 中的代码隐藏是:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Files = Directory.GetFiles(@"C:\").Select(f => new FileInfo(f)).ToList();

        DataContext = this;
    }

    public IEnumerable<FileInfo> Files { get; private set; } 
}

当我启动这个程序时,它看起来像这样:

截屏

即文件排序正确,但标题中的标记丢失。使用“标记”,我的意思是:

排序标记

注意:我正在寻找通用解决方案。只是设置DataGridColumn.SortDirection不是解决方案。我正在寻找一种方法来指示 DataGrid 从集合视图中自动检索排序顺序。

4

3 回答 3

1

这似乎是一个老问题,但今天我遇到了同样的问题并找到了一个通用的解决方案。所以,这个想法是 SortDescriptions 集合隐式地实现了 INotifyCollectionChanged,因此我们有能力观察它并适当地设置 DataGrid“排序标记”。我在单独的行为中实现了这一点:

public class DataGridSortDescriptionsSyncBehavior : Behavior<DataGrid>
{
    protected override void OnAttached()
    {
        base.OnAttached();

        var view = CollectionViewSource.GetDefaultView(AssociatedObject.ItemsSource);
        var notifyCollection = view.SortDescriptions as INotifyCollectionChanged;
        if (notifyCollection != null)
            notifyCollection.CollectionChanged += SortDescriptions_CollectionChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();

        var view = CollectionViewSource.GetDefaultView(AssociatedObject.ItemsSource);
        var notifyCollection = view.SortDescriptions as INotifyCollectionChanged;
        if (notifyCollection != null)
            notifyCollection.CollectionChanged -= SortDescriptions_CollectionChanged;
    }

    private void SortDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Reset)
        {
            // clear all columns sort directions
            foreach (var column in AssociatedObject.Columns)
                column.SortDirection = null;
        }

        if (e.NewItems != null)
        {
            // set columns sort directions
            foreach (SortDescription descr in e.NewItems)
                SetSortDirection(descr.PropertyName, descr.Direction);
        }

        if (e.OldItems != null)
        {
            // reset columns sort directions
            foreach (SortDescription descr in e.OldItems)
                SetSortDirection(descr.PropertyName, null);
        }
    }

    private void SetSortDirection(string sortMemberPath, ListSortDirection? direction)
    {
        var column = AssociatedObject.Columns.FirstOrDefault(c => c.SortMemberPath == sortMemberPath);
        if (column != null)
        {
            column.SortDirection = direction;
        }
    }
}

对于那些不了解行为的人 - 您需要添加对 System.Windows.Interactivity 程序集的引用并将行为应用于 DataGrid:

<DataGrid ItemsSource="{Binding Data}">
    <i:Interaction.Behaviors>
        <local:DataGridSortDescriptionsSyncBehavior />
    </i:Interaction.Behaviors>
</DataGrid>

现在,当您修改收藏时,“排序标记”会自动调整。另外,请注意,此行为仅适用于默认集合视图。如果您正在使用显式创建的集合视图 - 只需将其发送到行为(例如,通过行为的属性)并使用它而不是默认集合视图。

于 2015-02-09T22:46:25.323 回答
0

对于您的通用解决方案,我有两个建议。

第一的:

您可以创建新的 DataGrid 类并自动检查 SortDescriptions。

 class ExDataGrid : DataGrid
    {
        bool isFirstRow = true;

        protected override void OnLoadingRow(DataGridRowEventArgs e)
        {
            base.OnLoadingRow(e);
            if (isFirstRow)
                SortDirectionHelper(this);
        }

        private void SortDirectionHelper(DataGrid dg)
        {
            var tmp = dg.Items.SortDescriptions;

            foreach (SortDescription sd in tmp)
            {
                var col = dg.Columns.Where(x => (((Binding)x.ClipboardContentBinding).Path.Path == sd.PropertyName)).FirstOrDefault();
                if (col != null)
                {
                    col.SortDirection = sd.Direction;
                }
            }

            isFirstRow = false;
        }
    }

Xaml 文件:

<Window x:Class="DataGridSortTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
        Title="MainWindow" Height="350" Width="525"
        xmlns:dg="clr-namespace:DataGridSortTest"
        >
    <Window.Resources>
        <CollectionViewSource x:Key="items" Source="{Binding Files}" >
            <CollectionViewSource.SortDescriptions>
                <cm:SortDescription PropertyName="Length"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <Grid>      
        <dg:ExDataGrid x:Name="grid" ItemsSource="{Binding Source={StaticResource items}}" />
    </Grid>
</Window>

第二:

您可以在每个网格中为 Loaded 事件创建 EventHandler

 private void grid_Loaded(object sender, RoutedEventArgs e)
        {
            SortDirectionHelper(sender as DataGrid);
        }

        public void SortDirectionHelper(DataGrid dg)
        {
            var tmp = dg.Items.SortDescriptions;

            foreach (SortDescription sd in tmp)
            {
                var col = dg.Columns.Where(x => (((Binding)x.ClipboardContentBinding).Path.Path == sd.PropertyName)).FirstOrDefault();
                if (col != null)
                {
                    col.SortDirection = sd.Direction;
                }
            }
        }
于 2012-07-16T19:01:39.907 回答
0

来自msdn论坛

您需要在 DataGrid Columns 上显式指定 SortDirection 属性以使标记显示在列标题中,因为标记功能是 DataGrid 的而不是绑定源。

所以也许你最好使用@kmatyaszek 解决方案

于 2012-07-17T07:53:36.830 回答