Link to home
Start Free TrialLog in
Avatar of Billy_London
Billy_London

asked on

Datagridview on vb.net form not updating from other thread

I have a datagrid view on a vb.net form that lists new files on a directory.
When opening the form, it checks for existing files, and adds them to the datagridview without any problem.
I have a FileSystemWatcher class that monitors for new files. when its Created event is triggered, it calls the same sub to add the entry to the datagrid view. Unfortunately, this form does not reflect the changes - presumably because the thread is not the same as the main form's.

I know the event is triggered because it runs through all the code, it's just that the Datagrid control is ignoring the call to update it.
I've tried using the InvokeRequired property but it doesn't work with this control type it seems.
 Any idea how to get the control to update when called from the event procedure?
Dim WithEvents varWatchUpload As System.IO.FileSystemWatcher

    Delegate Sub UpdateEDIList_Callback(ByVal pmFileNumber As Integer, ByVal pmDescription As String, ByVal pmFilePath As String)

    Public Sub UpdateEDIList(ByVal pmFileNumber As Integer, _
                         ByVal pmDescription As String, _
                               ByVal pmFilePath As String)

        If Me.EDIList1.InvokeRequired Then
            EDIList1.BeginInvoke(New UpdateEDIList_Callback(AddressOf Me.UpdateEDIList), New Object() {pmFileNumber, pmDescription, pmFilePath})
        End If


        Dim NewRow(4) As Object
        NewRow(0) = Date.Now
        NewRow(1) = pmFileNumber
        NewRow(2) = pmDescription
        NewRow(3) = pmFilePath

        Me.EDIList1.Rows.Add(NewRow)

FinishOff:
        Exit Sub

    End Sub

Open in new window

Avatar of Erick37
Erick37
Flag of United States of America image

Assuming the grid is on the form, then you could use this:

If Me.InvokeRequired Then
            Me.Invoke(New UpdateEDIList_Callback(AddressOf Me.UpdateEDIList), New Object() {pmFileNumber, pmDescription, pmFilePath})
        End If

Avatar of Billy_London
Billy_London

ASKER

It will work if all the code is run from the main form's module.
however, i call code from other modules that in turn calls the form's UpdateEDIList method.
The files are large xml messages that need a lot of code so I don't want to clog up the main form's code.

Me.InvokedRequired always returns False when another module calls it; the code will run, but the Datagrid is not updated.
The code you posted does not reflect that scenario so it's difficult to visualize what the sequence of events is.

Can you post a simplified version of what you are trying to do?  How is the other code instantiated and run?
The main form has varWatchUpload event procedure that runs when new file is created on target directory:

    Private Sub varWatchUpload_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles varWatchUpload.Created
        CheckIndividualFile(e.FullPath)
    End Sub

Sub CheckIndividualFile is in a separate module. It takes only one parameter - the filepath. It opens the file, reads xml, extracts some data and puts it into our database. Then it calls the Datagrid update procedure on the main form as in: Form1.UpdateEDIList(varFileNumber, varDocType, varFileName) so user can see summary of what file has been processed.
Avatar of Nasir Razzaq
When you step through the code, is the code in the update sub not executing at all? How about you using the file system watcher and uploading the XML to the database and the main form uses the SQLDependency to detect the new records and refreshes the grid?

http://www.codeproject.com/KB/database/chatter.aspx
I would recommend modifying the function to return the results to the calling procedure.  You could use a datarow, a collection, or user type to return the results in.

Private Sub varWatchUpload_Created(ByVal sender As Object, ByVal e As System.IO.FileSystemEventArgs) Handles varWatchUpload.Created
        Dim results As List(Of String) ''or DataRow
        results = CheckIndividualFile(e.FullPath)
        If Me.InvokeRequired Then
            Dim d As New UpdateEDIList_Callback(AddressOf UpdateEDIList)
            Me.Invoke(d, New Object() {results})
        Else
            ' use async thread
            UpdateEDIList(results)
        End If
        ...

End Sub
CodeCruiser:
The project has different routines that check for files from different directories. Some are xml that need to be logged on the db. Some are different kinds that won't be logged in db but need to be redirected to another folder. In that case there's no logging on the db, so no event to latch onto.
SQLDependency is nice idea though.

Erick37: I will go with your method for the moment. I did consider that at some point, but it's not ideal. It means that any subrouting that wants to send log something on the datagrid has to be converted to a function that returns those variables.

If anyone else has an idea how to bypass this thread protection on the form let me know please otherwise i'll close question.

thanks
>but need to be redirected to another folder. In that case there's no logging on the db, so no event to latch onto.
Do you need to interact with the grid in that case?
It's best practice to encapsulate your data -- meaning do not expose the form's grid to direct update via external processes.  Function calls provide this (and perhaps solve the problem).
CodeCruiser:
some EDI files might not be recognised by us, in which case I wouldn't log them on the db, i would put them in a separate folder. but i'd still want to report to the "dashboard" that it's processed the file.
It may also have an error logging onto the db, say because of unique index key violation with duplicate xml file, in which case I want to report to the dashboard that it couldn't process it and why.

That's a business logic question anyhow. This threading protection will crop up again in other areas, so it would be nice to know the solution rather than adapt my code around it.

Erick: the external process doesn't access the grid directly, it uses the public subroutine on the main form. Are you suggesting just changing the sub to a function or can you give me an idea what you mean pls?
You are correct.  I see that your external code calls the Forms public sub.

Question:  is CheckIndividualFile a Shared function or is it in a class instantiated by the main form?
CheckIndividualFile is a public sub/function in a separate module
do you think it would make a difference if it was a class object?
ASKER CERTIFIED SOLUTION
Avatar of Erick37
Erick37
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
setting SynchronizingObject property was the key!
Now it will update the datagrid when called from separate module.
thanks to both of you.