Search code examples
vb.netdatatabledatagridviewdatasource

Updating the DataTable row from DataGridView after Filter has changed


I have a DataGridView control that I use a filter on. That all works. When I execute code through a background worker, I use data from that grid for the worker to act upon. The worker then updates the grid (e.g. as in the number of records found in the process of him (her?) doing the work).

I got that working just fine, if I pass the index of the grid and depend on the grid remaining static.

Where things go sideways is if I change the filter. Thus, the index of the grid changes, even the row that I was working with might not even exist in the filtered view.

So I start to search around for a solution, even found a few articles on here (How Do I Get the Selected DataRow in a DataGridView?) that I thought might point me in the right direction.

When the worker reports his (her?) progress, the DataRowView is there for it to read, but it cannot update the ItemArray.

Here are some code snipets:

Private Sub bgw_PostProcess_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bgw_PostProcess.ProgressChanged

    Select Case e.ProgressPercentage
        Case 0
            '~~ This is the code that is failing.  The value of e.UserState(5) does not 
            '   get set to the Row.ItemArray.  (In debugging, it has a value of 9).
            '
            '   e.UserState(6) is the DataRowView that gets passed to the BackgroundWorker
            '   and in debugging is correctly passed and present.
            '
            e.UserState(6).Row.ItemArray(1) = e.UserState(5)

            ts_Status.Text = "Reading thread (page " & e.UserState(0) & " of " & e.UserState(2) & ")"

    End Select
End Sub

Even if this is not the right way of updating the DataViewGrid, I need a way to update the underlying DataTable that is bound to the DataGridView control.

Any ideas?


Added comments (per request).

If you are unfamiliar with background workers, allow me to explain. The background worker is essentially a separate thread executed independently from the main executable. It can call two sub linking it to the main executable: ProgressChanged and RunWorkerCompleted.

When ProgressChanged is triggered, there are event arguments that can get passed from the worker to the main executable. This is the "e" variable. It contains an object passed back. That code works well. That is not the problem. For relevant purposes here. e.UserState array is:

[5] the number I want to show back on the DataGridControl
[6] the DataViewGrid.CurrentRow.DataBoundItem

The DataViewGridControl.CurrentRow.DataBoundItem gets set via a Queue object:

    qItemCount.Enqueue({grid_Names.Rows(idx).Cells("Name").Value,
               "https://www.mywebsite.com/" & grid_Names.Rows(idx).Cells("Link").Value,
               grid_Names.CurrentRow.DataBoundItem,
               0})
    bgw_PostProcess.RunWorkerAsync()

This code works and, as requested, I am posting it.

And the background worker makes the call to report the progress in the DoWork sub:

    worker.ReportProgress(iProcessType, {iPageCount, sName, iMaxPages, sThreadTitle, sThreadID, iThreadImageCount, drvGridRow})

drvGridRow is dequeued previously from qItemCount. That all works just as it should.

However, placing a breakpoint of the

e.UserState(6).Row.ItemArray(1) = e.UserState(5)

and checking the variables in the Command Window, I get what I would expect.

>? e.UserState(6).Row.ItemArray
{Length=3}
    (0): "Person Name"
    (1): 0
    (2): "show.php?9524231"
>? e.UserState(5)
13

Notice ItemArray(1) is zero. This is expected.

Now, when I step to the next line and recheck the variables, I get the same result even though an assigned value of 13 is made to ItemArray(1). I even tried to hard code a value in there and it won't update. There is no runtime error generated, VB just acts as if the assign worked.

According to Microsoft, this assignment should work. See DataRow.ItemArray Property


Solution

  • I missed it the first time but the issue here is a misunderstanding of how the ItemArray property works. It is not "live" data. You use that property to get a full set of field values out of a DataRow or into a DataRow. Changing the array itself has no effect on the DataRow because it is independent of the DataRow. It's just an array, like any other, so setting an element just affects that array, not any object that the contents of the array may have come from originally. To do what you want using the ItemArray property, you would have to do this:

    Dim fieldValues As Object() = e.UserState(6).Row.ItemArray
    
    fieldValues(1) = e.UserState(5)
    e.UserState(6).Row.ItemArray = fieldValues
    

    You get an array of field values from the ItemArray property, you set the desired element(s) of that array, then you pass the array back into the property. Internally, the DataRow will populate its fields from the elements of that array.

    The thing is though, there's no point doing that if you're only changing one field. If you want to change one field, just change that field:

    e.UserState(6).Row(1) = e.UserState(5)
    

    Now you're updating the actual field of the DataRow directly.