Click here to Skip to main content
15,037,178 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:

I am building a custom data class which I want to bind to a WPF DataGrid, and I am experiencing some headaches. From the looks of it, it appears that the datagrid is asking for Item(0) from my custom class on every other request. So to present the data, it asks for Items 0,0,0,1,0,2,0,3,0,4,0,5 ... etc. Is this supposed to be this way?

The process It is giving me grief as my class is paging large amounts of data, and requesting item 1000000 and then 0 and back to 1000001 triggers a lot of paging. I suppose I can bypass this by always keeping Item 0 in memory, but it would nevertheless be nice to understand what is going on...

Any comments on how this works?

I'll include a simple example in VB to illustrate the problem. Add a breakpoint at the Item() method, and watch the incoming argument.

On a side note, where can I find a guide on how to make code representable in these posts?

XAML for the Window with the DataGrid:

<window x:class="Viewer" xmlns:x="#unknown">
 Title="Window1" Height="300" Width="300">
        <datagrid autogeneratecolumns="True" name="DataGrid1" />

Code: (Place it all in the MainWindow if you will)

Class MainWindow
      Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
      End Sub
      Public Sub DoStuff()
            'Create a viewable collection
            Dim coll As New ViewableCollection
            For i = 0 To 100
                  coll.Add(New PropClass("name" & i.ToString, i))

            'Bind to Viewer
            Dim view As New Viewer
            view.DataGrid1.ItemsSource = coll
      End Sub
End Class
Public Class PropClass
      Public Name As String
      Public Value As Double
      Public Sub New(ByVal instanceName As String, ByVal instanceValue As Double)
            Name = instanceName
            Value = instanceValue
      End Sub
End Class

Public Class ViewableCollection
    Implements IList

    Private dataContainer As New List(Of PropClass)

    Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) Implements System.Collections.ICollection.CopyTo

    End Sub

    Public ReadOnly Property Count As Integer Implements System.Collections.ICollection.Count
            Return dataContainer.Count
        End Get
    End Property

    Public ReadOnly Property IsSynchronized As Boolean Implements System.Collections.ICollection.IsSynchronized
            Return False
        End Get
    End Property

    Public ReadOnly Property SyncRoot As Object Implements System.Collections.ICollection.SyncRoot
            Return Nothing
        End Get
    End Property

    Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return New VCEnumerator(Me)
    End Function

    Public Function Add(ByVal value As Object) As Integer Implements System.Collections.IList.Add
        dataContainer.Add(CType(value, PropClass))
        Return dataContainer.Count - 1
    End Function

    Public Sub Clear() Implements System.Collections.IList.Clear
    End Sub

    Public Function Contains(ByVal value As Object) As Boolean Implements System.Collections.IList.Contains
        Return dataContainer.Contains(DirectCast(value, PropClass))
    End Function

    Public Function IndexOf(ByVal value As Object) As Integer Implements System.Collections.IList.IndexOf
        Dim test = dataContainer.IndexOf(DirectCast(value, PropClass))
        Return dataContainer.IndexOf(DirectCast(value, PropClass))
    End Function

    Public Sub Insert(ByVal index As Integer, ByVal value As Object) Implements System.Collections.IList.Insert
        dataContainer.Insert(index, DirectCast(value, PropClass))
    End Sub

    Public ReadOnly Property IsFixedSize As Boolean Implements System.Collections.IList.IsFixedSize
            Return False
        End Get
    End Property

    Public ReadOnly Property IsReadOnly As Boolean Implements System.Collections.IList.IsReadOnly
            Return False
        End Get
    End Property

    Default Public Property Item(ByVal index As Integer) As Object Implements System.Collections.IList.Item
            Return dataContainer.Item(index)
        End Get
        Set(ByVal value As Object)
            dataContainer.Item(index) = DirectCast(value, PropClass)
        End Set
    End Property

    Public Sub Remove(ByVal value As Object) Implements System.Collections.IList.Remove
        dataContainer.Remove(DirectCast(value, PropClass))
    End Sub

    Public Sub RemoveAt(ByVal index As Integer) Implements System.Collections.IList.RemoveAt
    End Sub

    Public Class VCEnumerator
        Implements IEnumerator
        Private _owner As ViewableCollection
        Private pointer As Integer = -1
        Private lastIdx As Integer

        Public ReadOnly Property Current As Object Implements System.Collections.IEnumerator.Current
                Return _owner.Item(pointer)
            End Get
        End Property

        Public Function MoveNext() As Boolean Implements System.Collections.IEnumerator.MoveNext
            pointer += 1
            Return pointer <= lastIdx
        End Function

        Public Sub Reset() Implements System.Collections.IEnumerator.Reset
            pointer = -1
        End Sub

        Public Sub New(ByVal owner As ViewableCollection)
            _owner = owner
            lastIdx = _owner.Count - 1
        End Sub
    End Class

End Class
Updated 22-Nov-10 17:53pm

1 solution

I don't know whether this problem is still important for you. However, I am writing a data virtualization solution for DataGrid in order to allow the user to manage millions of rows without paging. I developed a list class which does the heavy work and a CollectionView class used by the ItemsControl. I am also experiencing weird item requests from the DataGrid.

I aprehended some facts:

1. The CollectionView should impelement IList (non generic) otherwise the ItemsControl will treat it as an IEnumerable and query/enumerate all the rows. Which is obviously not an option, since the point would be to keep only those pages in memory which are actually studied by the user. So I implemented IList.

2. The row with 0 index is frequently asked by the DataGrid, due to the fact that it is used for visual item measurement (according to the call stack). So, I simply cache this one.

3. The caching mechanism inside the DataGrid uses a predictable pattern to query the rows it shows. First it asks for the visible rows from top to bottom (twice every row), then it queries a couple of rows (depending on the size of the visible area) before the visible area (including the first visible row) in a descending order so, from bottom to top. After that it requests for a same amount of rows after the visible rows (inclucing the last visible row) from top to bottom.

I was wondering, why it requests for non visible rows evey time since those rows will be requested again when become visible. Measurement reasons again I guess.

If my page size is well set, I can serve all these requests from the current and previously loaded page.

4. If CanSelectMultipleItems is enabled (True), when selecting multiple items using the SHIFT button, the DataGrid enumerates all the rows from the begining(!!!!) of the list to the end of the selection using the IEnumerable interface. I really do not understand why...

I had to turn it off since I could not avoid the usage of GetEnumerator method.

5. If the selected row is not visible and the current visible area is far from the selected row, sometimes it starts querying all the items from the selected row to the end of the visible area. Including all the rows between which are not even visible.

I could not figure out the exact pattern of this behavior. Maybe my implementation causes it.

I hope these can help someone.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900