WPF DataGrid 默认排序不起作用

WPF DataGrid default sort not working

我有一个 DataGrid,列 XAML 是这样的:

<DataGridTextColumn Header="Time" Binding="{Binding Date, StringFormat='yyyy-MM-dd  HH:mm:ss'}" SortMemberPath="Date" SortDirection="Descending" Width="130" CanUserResize="True" />
<DataGridTextColumn Header="Level" Binding="{Binding Level}" Width="60" CanUserResize="True" />
<DataGridTextColumn Header="Source" Binding="{Binding Logger}" Width="150" CanUserResize="True" />
<DataGridTextColumn Header="Message" Binding="{Binding Message}" Width="*" CanUserResize="True" />

我将其绑定到 ObservableCollection<EalsLogEvent>,其中 EalsLogEvent.Date 键入 DateTime:

public ObservableCollection<EalsLogEvent> LogEvents 
{
    get
    {
        return _logEvents;
    }
}

网格视图模型使用计时器来刷新自身,网格看起来一切正常,除了在应用程序启动时首次加载时。然后,Time 列似乎是降序排列的,但实际上是升序排列的。

为了正确排序,我必须单击 header 列两次;第一次将顺序更改为升序,现在与列的内容匹配。第二次单击列 header 将其排序顺序改回降序,这次它正确地对列内容进行排序,即降序。

如果我在 _logEvents 刷新时使用 LINQ 对 collection 进行排序,我将丢失用户通过单击其 header 为该列设置的任何顺序。如果我必须让视图告诉模型 LINQ 排序应该使用哪个顺序,有些东西闻起来很糟糕。

您可以在 XAML 中使用 CollectionViewSource 来定义默认排序。

假设我们有一个视图模型:

public class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Item> Items { get; private set; }
}

我们可以为 Items collection 创建自定义 CollectionView:

<Window xmlns:l="clr-namespace:YourNamespace"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase">
    <Window.DataContext>
        <l:ViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <CollectionViewSource Source="{Binding Items}" x:Key="GridItems">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Date" Direction="Descending"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>
    </Window.Resources>
    <DataGrid ItemsSource="{Binding Source={StaticResource GridItems}}" AutoGenerateColumns="False">
        <DataGrid.Columns>                    
            <DataGridTextColumn Header="Time" Binding="{Binding Date, StringFormat='yyyy-MM-dd  HH:mm:ss'}" Width="130" CanUserResize="True" />
            <DataGridTextColumn Header="Level" Binding="{Binding Level}" Width="60" CanUserResize="True" />
            <DataGridTextColumn Header="Source" Binding="{Binding Logger}" Width="150" CanUserResize="True" />
            <DataGridTextColumn Header="Message" Binding="{Binding Message}" Width="*" CanUserResize="True" />
        </DataGrid.Columns>
    </DataGrid>
</Window>

使用这种方法,您的基础源 collection(本例中的 Items)不会受到影响,排序仅发生在视图中。

如您在 MSDN 中所读:

You can think of a collection view as the layer on top of the binding source collection that allows you to navigate and display the collection based on sort, filter, and group queries, all without having to manipulate the underlying source collection itself. If the source collection implements the INotifyCollectionChanged interface, the changes raised by the CollectionChanged event are propagated to the views.

您还应该注意以下几点:

All collections have a default CollectionView. WPF always binds to a view rather than a collection. If you bind directly to a collection, WPF actually binds to the default view for that collection.

因此,使用 CollectionViewSource,您只是为 collection.

定义自定义视图