Search code examples
.netvb.netlistsortingbindingsource

VB BindingSource SupportsSorting is false. Why?


I've been cruising around the interwebs trying to figure this out but I cant.

I have a BindingSource to which I .add a bunch of FileInfos from .GetFiles.

When I step through, the BindingSource's SupportsSorting property is False. I don't understand why.

I tried setting the BindingSource.DataSource to a BindingList in stead of adding each FileInfo one by one, but then the underlying list is still of type IList.

Here's my code:

 If Directory.Exists(configPath) Then
            For Each foundFile As FileInfo In New DirectoryInfo(configPath).GetFiles("*.csv", SearchOption.TopDirectoryOnly)
                If foundFile.Length >= My.Settings.scanFileSizeThreshold Then

                    scansBindingSource.Add(foundFile)
                End If
            Next
        Else

All I want to do is something like this:

scansBindingList.Sort = "LastWriteTime Desc"

And have the data in a DataGridView sorted by last write time. Is this possible? Thanks!


Solution

  • The BindingList does not support sorting. As well on MSDN has this for BindingSource:

    If the data source is not an IBindingList, the SupportsSorting property always returns false.

    You can have NET deliver the files to you in the order of LastWriteTime so that you dont have to sort them (and also get rid of the loop):

    Dim theFiles = New DirectoryInfo(fpath).EnumerateFiles(fmask, SearchOption.TopDirectoryOnly).
                Where(Function(f) f.Length > szMin).
                OrderByDescending(Function(s) s.LastWriteTime)
    
    dgv2.DataSource = New BindingSource(theFiles, Nothing)
    

    NET will put the files in order and exclude the files which do not match rather than deliver them to you just to be tested and excluded. The result is an IEnumerable which the binding source can use directly.

    If you want to be able to add to a list of these and sort them, use a List(Of FileInfo) for storage and sorting and then a BindingSource for adding/deleting.

    Private filesBS As BindingSource
    Private FilesList As List(Of FileInfo)
    ...
    FilesList = New DirectoryInfo(...).EnumerateFiles("*.csv", SearchOption.AllDirectories).
                   Where(Function(f) f.Length > MinSize).
                   OrderByDescending(Function(q) q.LastWriteTime).
                   ToList()
    
    filesBS = New BindingSource(FilesList, Nothing)
    dgv2.DataSource = filesBS
    

    The files query is the same as before, I just added ToList(); then use it for the BindingSource. If you add and remove items via the BindingSource they will show in the collection and DGV.

    filesBS.RemoveAt(0)
    filesBS.Add(newItem)
    

    The dreary part is sorting because it has to be done 'manually' (which is to say the DGV wont /cant do it) and because there are so many properties to FileInfo. One way to sort is to use extension methods as was done in the query:

    ' sort by file size
    If fileSortOrder = SortOrder.Ascending Then
        FilesList = FilesList.OrderBy(Function(f) f.Length).ToList()
    Else
        FilesList = FilesList.OrderByDescending(Function(f) f.Length).ToList()
    End If
    filesBS = New BindingSource(FilesList, Nothing)
    ' ToDo: toggle fileSortOrder like below
    
    dgv2.DataSource = filesBS
    

    Note that since the FilesList collection is recreated, you need to rebuild the BindingSource as well. You can also use the Sort method:

    FilesList.Sort(AddressOf FileInfoSizeCompare)
    dgv2.Refresh()
    ...
    Private fileSortOrder As SortOrder = SortOrder.Ascending
    Private Function FileInfoSizeCompare(x As FileInfo, y As FileInfo) As Int32
        Dim sortReverser As Int32 = If(fileSortOrder = SortOrder.Ascending, 1, -1)
        Dim ret As Int32 = 0
        If x.Length < y.Length Then ret = -1
        If x.Length > y.Length Then ret = 1
    
        Return (ret * sortReverser)
    End Function
    

    In this case, since the list is not recreated, you just need to refresh the control to redraw the contents. You will need a different method for each column/property you wish to sort by. With either approach, if you invoke the methods from the ColumnHeaderMouseClick event, it will work like it does when the DGV can sort the DataSource.

    If I were to use the second version, I might put the Sort methods in a class so that a) I could reuse them and b) to get all that code out of the forms.