WPF DataGrid无法对Protobuf Timestamp列排序的解决咨询
解决WPF DataGrid中Protobuf Timestamp列无法排序的问题
方案一:通过Sorting事件实现自定义排序
这是无需修改Protobuf类的快速方案,直接在UI层接管Timestamp列的排序逻辑:
- 给DataGrid绑定Sorting事件:
<DataGrid x:Name="YourDataGrid" Sorting="YourDataGrid_Sorting" AutoGenerateColumns="True"/> - 后台实现事件处理逻辑:
注意替换代码中的private void YourDataGrid_Sorting(object sender, DataGridSortingEventArgs e) { if (e.Column.SortMemberPath != "Timestamp") return; // 阻止默认排序,避免触发报错 e.Handled = true; var view = CollectionViewSource.GetDefaultView(YourDataGrid.ItemsSource) as ListCollectionView; if (view == null) return; // 设置自定义比较器,根据当前排序方向调整 var sortDir = e.Column.SortDirection ?? ListSortDirection.Ascending; view.CustomSort = new TimestampComparer(sortDir); // 更新列的排序箭头显示 e.Column.SortDirection = sortDir == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending; } // 自定义Timestamp比较器,利用Protobuf Timestamp自身的CompareTo方法 private class TimestampComparer : IComparer { private readonly ListSortDirection _sortDir; public TimestampComparer(ListSortDirection sortDir) => _sortDir = sortDir; public int Compare(object x, object y) { var itemX = x as YourProtobufClass; var itemY = y as YourProtobufClass; if (itemX?.Timestamp == null && itemY?.Timestamp == null) return 0; if (itemX?.Timestamp == null) return _sortDir == ListSortDirection.Ascending ? -1 : 1; if (itemY?.Timestamp == null) return _sortDir == ListSortDirection.Ascending ? 1 : -1; var compareResult = itemX.Timestamp.CompareTo(itemY.Timestamp); return _sortDir == ListSortDirection.Descending ? -compareResult : compareResult; } }YourProtobufClass为你实际的自动生成类名。
方案二:转换Timestamp为.NET原生可排序类型(更简洁推荐)
Protobuf的Timestamp可以通过ToDateTimeOffset()方法转换为.NET原生的DateTimeOffset,该类型默认支持排序。你可以通过两种方式实现:
方式1:创建包装类
由于自动生成的Protobuf类无法修改,可创建包装类转发属性并转换Timestamp:
public class ProtobufItemWrapper { public YourProtobufClass Original { get; set; } // 转发字符串属性示例 public string StringProp1 => Original.StringProp1; public string StringProp2 => Original.StringProp2; // 转换Timestamp为可排序的DateTimeOffset public DateTimeOffset Timestamp => Original.Timestamp.ToDateTimeOffset(); }
将DataGrid的ItemsSource绑定到List<ProtobufItemWrapper>集合后,自动生成的Timestamp列会基于DateTimeOffset默认支持排序,无需额外处理。
方式2:利用AutoGeneratingColumn修改绑定
若不想创建包装类,可在列自动生成时转换绑定值:
<DataGrid x:Name="YourDataGrid" AutoGeneratingColumn="YourDataGrid_AutoGeneratingColumn" AutoGenerateColumns="True"/>
后台代码:
private void YourDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e) { if (e.PropertyName == "Timestamp" && e.PropertyType == typeof(Google.Protobuf.WellKnownTypes.Timestamp)) { var textColumn = e.Column as DataGridTextColumn; if (textColumn == null) return; // 替换绑定,加入转换器将Timestamp转为DateTimeOffset textColumn.Binding = new Binding("Timestamp") { Converter = new TimestampToDateTimeOffsetConverter() }; } } // 转换器实现 public class TimestampToDateTimeOffsetConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is Google.Protobuf.WellKnownTypes.Timestamp ts ? ts.ToDateTimeOffset() : DateTimeOffset.MinValue; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
注意:这种方式下默认排序可能仍会调用原始Timestamp类型的比较逻辑,建议结合方案一的Sorting事件,或直接使用包装类方案更省心。
错误原因
Protobuf自动生成的Timestamp虽实现了IComparable<Timestamp>泛型接口,但WPF DataGrid的默认排序机制在反射获取比较器时,无法正确识别泛型接口的比较逻辑,导致抛出比较失败的异常。
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

