【问题标题】:How can I be notified if a DataGrid column is sorted (and not sorting)如果 DataGrid 列已排序(而不是排序),如何通知我
【发布时间】:2012-01-15 01:28:29
【问题描述】:

我需要在 WPF 应用程序中为 DataGrid 提供某种 Sorted 事件,但找不到获取它的方法。

这是我尝试过的:

DataGrid 提供了一个事件Sorting,但我不能使用它,因为它在排序完成之前被触发。 EventArgs 给我排序的列,但不是排序的方式,如果我得到排序方向,它将设置为旧值。当然我可以猜到它会是什么,因为我知道它会从无到上升,最后到下降,但这不是解决方案,因为如果控件的行为发生变化,它会失败。

第二次尝试:

DataGrid 有一个默认视图,提供对SortDescriptionCollection 的访问。此集合包含所有排序属性,但我认为没有任何可能让我通知更改。

我不得不说,我正在寻找一个尽可能干净的解决方案,因为它将用于一个大型项目,我无法使用如果环境发生变化可能会失败的解决方案。

有没有人根据经验(或文档?)知道我该如何解决这个问题?

编辑:为了更清楚地说明我想要实现的目标:当用户对列进行排序时,我需要了解哪个 DataGrid 列按哪个方向排序。这些信息不必在排序之后出现,它必须是正确的;)

【问题讨论】:

  • SortDescriptionCollection 实现了 INotifyCollectionChanged 接口。因此,如果将集合转换为 INotifyCollectionChanged 并为 CollectionChanged 事件设置处理程序,则可以在集合更改时收到通知。但我不确定在这种情况下它会有所帮助。请在下面查看我的答案。

标签: .net wpf sorting datagrid


【解决方案1】:

我自己通过覆盖 DataGrid 为 DataGrid 事件实现了 Sorted,如下所示:

public class ValueEventArgs<T> : EventArgs
{
    public ValueEventArgs(T value)
    {
        Value = value;
    }

    public T Value { get; set; }

}

public class DataGridExt : DataGrid
{
    public event EventHandler<ValueEventArgs<DataGridColumn>> Sorted;

    protected override void OnSorting(DataGridSortingEventArgs eventArgs)
    {
        base.OnSorting(eventArgs);

        if (Sorted == null) return;
        var column = eventArgs.Column;
        Sorted(this, new ValueEventArgs<DataGridColumn>(column));
    }
}

要使用它,您需要做的就是:

    private void Initialize()
    {
            myGrid.Sorted += OnSorted;
    }
    private void OnSorted(object sender, ValueEventArgs<DataGridColumn> valueEventArgs)
    {
    // Persist Sort...
    }

【讨论】:

    【解决方案2】:

    我无法让 Stephen Lautier 的解决方案在 VB.Net 中工作,但我找到了另一个可以工作的解决方案。

    每当发生排序操作时,都会按列出的顺序发生以下事件:

    1. 排序
    2. UnloadingRow(适用于 DataGrid 中的所有行)
    3. Lo​​adingRow(适用于 DataGrid 中的所有行)
    4. 布局已更新

    这可以通过以下方式使用:

    变量

    Private _updateSorted As Boolean
    Private _tempSender As Object
    Private _rowsLoaded As List(Of DataGridRowEventArgs)
    _rowsLoaded = New List(Of DataGridRowEventArgs)
    

    排序

    Private Sub myDataGrid_Sorting(sender As Object, e As DataGridSortingEventArgs) Handles myDataGrid.Sorting
        _updateSorted = True
        _rowsLoaded.Clear()
        _tempSender = Nothing
    End Sub
    

    UnloadingRow/Loading Row 事件

    'Save pre-sorting state here, if desired'
    'Perform operation on pre-sorting rows here, if desired'
    Private Sub myDataGrid_UnloadingRow(sender As Object, e As DataGridRowEventArgs) Handles myDataGrid.UnloadingRow
    
    End Sub
    
    'Save post-sorting state here.'
    'Perform operation on post-sorting rows here'
    'In this example, the operation is dependent on the DataGrid updating its layout first so I included items relevant to handling that'
    Private Sub myDataGrid_LoadingRow(sender As Object, e As DataGridRowEventArgs) Handles myDataGrid.LoadingRow
        Dim myDataGridCell As DataGridCell = GetCellByRowColumnIndex(myDataGrid, e.Row.GetIndex, colIndex)  
        'Or whatever layout-dependent object you are using, perhaps utilizing e As DataGridRowEventArgs'
    
            If Not IsNothing(myDataGridCell) Then
               '~~ Perform operations here ~~'
            Else
                If _updateSorted Then
                    'Update has occurred but the updated DataGrid is not yet available'
                    'Save variables to use once the DataGrid is updated'
                    _rowsLoaded.Add(e)
                    _tempSender = sender
                End If
            End If
    End Sub
    

    布局更新

    Private Sub myDataGrid_LayoutUpdated(sender As Object, e As EventArgs) Handles myDataGrid.LayoutUpdated
        If _updateSorted Then
            Dim rowsLoaded As New List(Of DataGridRowEventArgs)
            For Each eRow As DataGridRowEventArgs In _rowsLoaded
                rowsLoaded.Add(eRow)
            Next
    
            For Each eRow As DataGridRowEventArgs In rowsLoaded
                'Now perform the action to the sorted DataGridRows in the order they were added'
                myDataGrid_LoadingRow(_tempSender, eRow)
            Next
            _updateSorted = False
        End If
    End Sub
    

    【讨论】:

    • 您在哪里找到有关事件顺序的信息?我正在尝试使用 LayoutUpdated 事件(在 Sorting 事件之前)作为将数据网格滚动到顶部的触发器。虽然所有相关调用似乎都按预期执行,但最终行为并没有发生 - 我的数据网格在排序后没有滚动到顶部。相反,它会保留选定的行。
    • 我希望我知道参考来源,但我不知道!相反,我做了一些实验。基本上,在这些场景中,我尝试创建各种我认为我想要的或相关的事件。然后我让事件侦听器向控制台发送一条信息性消息。这样,报告不会干扰使用 GUI,并且消息让我知道触发了特定事件。通过反复试验,我可以了解发射顺序、频率、模式,或者它与不同设置和动作的一致性。
    • 您的情况听起来取决于您在数据网格或相关代码中设置的某些选项,例如自动滚动到选择。因此,如果这不是您想要的,我会研究在启动排序后如何维护或重置您的选择。这可以为您提供关于在何处/如何更改行为的线索。
    【解决方案3】:

    经过数小时的尝试和阅读,我自己解决了这个问题。我对解决方案并不完全满意,但它对我有用。

    当我让代码生成DataGrid 列时,我可以使用每列的DependencyProperty SortDirection 并添加一个在属性更改时将调用的方法。这样做是这样的:

    DataGridBoundColumn column = GetTheColumnIWantToObserve();
    if (column != null)
    {
        // add value changed notification to be informed about changes
        var desc = DependencyPropertyDescriptor.FromProperty(DataGridColumn.SortDirectionProperty, typeof(DataGridColumn));
        desc.AddValueChanged(column, ColumnSortDirectionChanged);
    }
    

    ColumnSortDirectionChanged 方法中,我现在可以处理更改。

    private void ColumnSortDirectionChanged(object sender, EventArgs eventArgs)
    {
        var column = sender as DataGridColumn;
        if (column != null)
        {
            var newSortDirection = column.SortDirection;
            // Yay, I got it!!
        }
    }
    

    如果我自己不生成列,我将不得不使用DataGrid 事件(例如LoadedAutoGeneratedColumns)并将通知添加到所有现有列。

    【讨论】:

    • 这确实是一个神解决方案。当您单击排序时,SortDirection 会发生变化。实际排序可能需要更长的时间,您无法保证您的解决方案会在排序完成后准确启动。
    • 那么您还有其他想法吗?对我来说这很好,因为我使用这些信息将其存储在配置文件中以供下一次应用程序启动。时机没有那么重要。尽管如此,正如我所写,我对此并不完全满意,所以我很乐意提出建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    相关资源
    最近更新 更多