反向排序ObservableCollection?

时间:2014-02-22 01:51:48

标签: wpf vb.net wpfdatagrid observablecollection

专家,

简短版本:

ObservableCollection排序的方式是最新的,我需要它完全相反,以便在WPF DataGrid中显示。现在,一切都很好,但最后添加了新条目,因此用户无法看到添加的新行。

简短版本:

我在Entry类上有一个DateTime字段,如果需要按/上排序,但说实话,如果我可以将它们添加到顶部,因为我得到它们,我甚至不需要 em>排序!我只需要:

*每个项目都添加到要添加到 top 的集合中,而不是默认的底部。*

更长的版本:

我根本无法找到将新元素引入集合“顶部”的方法......“为什么?”好吧,我在WPF表单中显示数据行,我希望最新的数据在顶部,按对象中的日期字段排序。

如果它与IList相同,那么为什么这么难?

太复杂了?让我简化:

真的很长版本:

一开始,有一个类将构成WPF DataGrid中的一行。该类称为“Entry”,下面仅有的两个属性是:

Class Entry
[...]
Public Property TsCreated As Nullable(Of DateTime)
Public Property EntryRaw As String
    Set(value As String)
        If value <> _entryRaw Then
            _entryRaw = value
            OnPropertychanged("EntryRaw")
        End If
    End Set
    Get
        Return _entryRaw
    End Get
End Property
Private _entryRaw As String
[...]
End Class

接下来是这些Entrys的ObservableCollection ......

Public Class SysEvents
    Inherits ObservableCollection(**Of Entry**)

    Private Shared _list As New SysEvents

    Public Sub New()
    End Sub
End Class

ObservableCollection 有一个不可覆盖的 Sub Add(),因此我无法编写拥有的 Sub Add()并排序正确添加后......

所以在使用中,这个WPF Window类是这样的(再次,使它非常简单):

Class MainWindow

    Private RawEvents As New toag.syslog.SysEvents

    Sub New()
        grdEntryRaw.ItemsSource = RawEvents ' grid that holds rows
    End Sub

    Sub AddRow()
        Me.RawEvents.Add(sysEvent)
    End Sub

End Class

好像很重要,XAML(我可以在XAML中排序吗?):

<DataGrid x:Name="grdEntryRaw" [...]>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding EntryRaw}" Header="Entry Raw" />
    </DataGrid.Columns>
</DataGrid>

确定。没人做这个版本:

由于我无法拦截Binding正在进行的.Add(),似乎我无法使用某种排序算法进入...

当我想到这一点时,我认为战斗结束了......但是现在看来我在1码线上已经取得了成功!哦,Visual Studio ..你是一个残酷的情妇...

TIA !!!

2 个答案:

答案 0 :(得分:1)

ObservableCollection除了实现INotifyCollectionChangedINotifyPropertyChanged之外,没有什么特别之处。

我建议您使用所需的行为创建自己的ObservableCollection

Public Class ObservableStack(Of T)
    Implements IEnumerable, ICollection, IList
    Implements IEnumerable(Of T), ICollection(Of T), IList(Of T)
    Implements INotifyCollectionChanged, INotifyPropertyChanged

    Public Sub New()
        Me.list = New List(Of T)
    End Sub

   '...

    Public Sub Add(item As T) Implements ICollection(Of T).Add
        'TODO: Validate.
        Me.list.Insert(0, item) 'Insert at top of the list.
        Me.RaisePropertyChanged("Count")
        Me.RaisePropertyChanged("Item")
        Me.RaiseCollectionChanged(NotifyCollectionChangedAction.Add, item, 0)
    End Sub

    Private Function _Add(obj As Object) As Integer Implements IList.Add
        Me.Add(TryCast(obj, T))
        Return 0
    End Function

   '...

    Private ReadOnly list As List(Of T)

End Class

示例

Public Class ObservableStack(Of T)
    Implements IEnumerable, ICollection, IList
    Implements IEnumerable(Of T), ICollection(Of T), IList(Of T)
    Implements INotifyCollectionChanged, INotifyPropertyChanged

    Public Sub New()
        Me.list = New List(Of T)
    End Sub

    Public Event CollectionChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged
    Protected Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public ReadOnly Property Count() As Integer Implements ICollection.Count, ICollection(Of T).Count
        Get
            Return Me.list.Count
        End Get
    End Property

    Default Public Property Item(index As Integer) As T Implements IList(Of T).Item
        Get
            Return Me.list.Item(index)
        End Get
        Set(value As T)
            Me.Replace(index, value)
        End Set
    End Property

    Private ReadOnly Property _IsFixedSize() As Boolean Implements IList.IsFixedSize
        Get
            Return CType(Me.list, IList).IsFixedSize
        End Get
    End Property

    Private ReadOnly Property _IsReadOnly() As Boolean Implements IList.IsReadOnly, ICollection(Of T).IsReadOnly
        Get
            Return CType(Me.list, IList).IsReadOnly
        End Get
    End Property

    Private ReadOnly Property _IsSynchronized() As Boolean Implements ICollection.IsSynchronized
        Get
            Return CType(Me.list, ICollection).IsSynchronized
        End Get
    End Property

    Private Property _Item(index As Integer) As Object Implements IList.Item
        Get
            Return Me.Item(index)
        End Get
        Set(value As Object)
            Me.Item(index) = DirectCast(value, T)
        End Set
    End Property

    Private ReadOnly Property _SyncRoot() As Object Implements ICollection.SyncRoot
        Get
            Return CType(Me.list, ICollection).SyncRoot
        End Get
    End Property

    Public Sub Add(item As T) Implements ICollection(Of T).Add
        Me.Insert(0, item)
    End Sub

    Public Sub Clear() Implements IList.Clear, ICollection(Of T).Clear
        If (Me.Count > 0) Then
            Me.list.Clear()
            Me.RaisePropertyChanged("Count")
            Me.RaisePropertyChanged("Item")
            Me.RaiseCollectionReset()
        End If
    End Sub

    Public Function Contains(item As T) As Boolean Implements ICollection(Of T).Contains
        Return Me.list.Contains(item)
    End Function

    Public Sub CopyTo(array() As T, index As Integer) Implements ICollection(Of T).CopyTo
        Me.list.CopyTo(array, index)
    End Sub

    Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
        Return Me.list.GetEnumerator()
    End Function

    Public Function IndexOf(item As T) As Integer Implements IList(Of T).IndexOf
        Return Me.list.IndexOf(item)
    End Function

    Public Sub Insert(index As Integer, item As T) Implements IList(Of T).Insert
        'TODO: Validate item.
        Me.list.Insert(index, item)
        Me.RaisePropertyChanged("Count")
        Me.RaisePropertyChanged("Item")
        Me.RaiseCollectionChanged(NotifyCollectionChangedAction.Add, item, index)
    End Sub

    Public Sub Move(ByVal oldIndex As Integer, ByVal newIndex As Integer)
        Me.MoveItem(oldIndex, newIndex)
    End Sub

    Protected Overridable Sub MoveItem(ByVal oldIndex As Integer, ByVal newIndex As Integer)
        Dim item As T = Me.Item(oldIndex)
        Me.list.RemoveAt(oldIndex)
        Me.list.Insert(newIndex, item)
        Me.RaisePropertyChanged("Item")
        Me.RaiseCollectionChanged(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex)
    End Sub

    Protected Overridable Sub OnCollectionChanged(e As NotifyCollectionChangedEventArgs)
        RaiseEvent CollectionChanged(Me, e)
    End Sub

    Protected Overridable Sub OnPropertyChanged(e As PropertyChangedEventArgs)
        RaiseEvent PropertyChanged(Me, e)
    End Sub

    Private Sub RaiseCollectionChanged(action As NotifyCollectionChangedAction, item As T, index As Integer)
        Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(action, item, index))
    End Sub

    Private Sub RaiseCollectionChanged(ByVal action As NotifyCollectionChangedAction, ByVal item As Object, ByVal index As Integer, ByVal oldIndex As Integer)
        Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(action, item, index, oldIndex))
    End Sub

    Private Sub RaiseCollectionChanged(action As NotifyCollectionChangedAction, oldItem As T, newItem As T, index As Integer)
        Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(action, newItem, oldItem, index))
    End Sub

    Private Sub RaiseCollectionReset()
        Me.OnCollectionChanged(New NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))
    End Sub

    Private Sub RaisePropertyChanged(propertyName As String)
        Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
    End Sub

    Public Function Remove(item As T) As Boolean Implements ICollection(Of T).Remove
        Dim index As Integer = Me.IndexOf(item)
        If (index <> -1) Then
            Me.RemoveAt(index)
            Return True
        End If
        Return False
    End Function

    Public Sub RemoveAt(index As Integer) Implements IList.RemoveAt, IList(Of T).RemoveAt
        Dim item As T = Me.Item(index)
        Me.list.RemoveAt(index)
        Me.RaisePropertyChanged("Count")
        Me.RaisePropertyChanged("Item")
        Me.RaiseCollectionChanged(NotifyCollectionChangedAction.Remove, item, index)
    End Sub

    Public Sub Replace(index As Integer, newItem As T)
        'TODO: Validate item.
        Dim oldItem As T = Me.Item(index)
        Me.list.Item(index) = newItem
        Me.RaisePropertyChanged("Item")
        Me.RaiseCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem, newItem, index)
    End Sub

    Private Function _Add(obj As Object) As Integer Implements IList.Add
        Me.Add(DirectCast(obj, T))
        Return 0
    End Function

    Private Function _Contains(obj As Object) As Boolean Implements IList.Contains
        Return Me.Contains(DirectCast(obj, T))
    End Function

    Private Sub _CopyTo(array As Array, index As Integer) Implements ICollection.CopyTo
        CType(Me.list, ICollection).CopyTo(array, index)
    End Sub

    Private Function _GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return Me.GetEnumerator()
    End Function

    Private Function _IndexOf(obj As Object) As Integer Implements IList.IndexOf
        Return Me.IndexOf(DirectCast(obj, T))
    End Function

    Private Sub _Insert(index As Integer, obj As Object) Implements IList.Insert
        Me.Insert(index, DirectCast(obj, T))
    End Sub

    Private Sub _Remove(obj As Object) Implements IList.Remove
        Me.Remove(DirectCast(obj, T))
    End Sub

    Private ReadOnly list As List(Of T)

End Class

答案 1 :(得分:0)

@Bjørn-RogerKringsjå当然指出了我正确的方向。

我将事情剥离到最低限度。

我没有创建自己的集合类,而是在WPF Window类中创建了集合:

WPF MainWindow类顶部的

声明:

Private RawEvents As ObservableCollection(Of Entry)

然后,我必须在类实例化的机制中实例化它,并为DataGrid设置ItemsSource:

RawEvents = New ObservableCollection(Of Entry)
grdEntryRaw.ItemsSource = RawEvents ' source for main window (events)

唯一剩下的就是将新事件放入集合中(我从消息队列中获取新事件,但不重要:

Public Sub PeekQ(ByVal sender As System.Object, ByVal e As System.Messaging.PeekCompletedEventArgs) Handles Q.PeekCompleted
    [..]    
    ' send to main display (newest entries on top)
    Me.Dispatcher.Invoke(CType(Sub() **Me.RawEvents.Insert(0, someEvent)**, Action))
    '
    Me.CountryLookupQ.BeginPeek()

End Sub

......就是这样!我甚至不需要额外的类来保存事件......我只是使用了在WPF窗口中创建的ObservableCollection。 XAML很简单,最好的部分是没有排序算法:

[...]
<DockPanel x:Name="EntryRawDockPanel"  HorizontalAlignment="Left" LastChildFill="False" Width="517" Margin="0,26,0,41">
    <DataGrid x:Name="grdEntryRaw" Grid.Column="1" Margin="0,0,10,43" AutoGenerateColumns="False" HorizontalContentAlignment="Stretch" CanUserAddRows="False" CanUserDeleteRows="True" AlternatingRowBackground="#FFDEFFE4">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding EntryRaw}" Header="Entry Raw" IsReadOnly="False"/>
        </DataGrid.Columns>
    </DataGrid>
</DockPanel>
[...]

老实说,这就是整个解决方案。 Entry()类在任何方面都不是特殊的。

希望这可以帮助其他人,是的,我已经看过不止一种方法来做这件事,比如在XAML中进行排序,甚至在XAML中实例化一个类,但这对我的写作方式来说是最简单的。