I'm trying to write a simple program that detects when files are changed in a folder (for another question here). It has a standard Windows with a TextBox and a couple buttons. Upon program start it calls .NET function FileSystemWatcher then sets up four event handlers for it. What I want is for the event handlers to write something to the TextBox, but VB.NET complains that shared functions can't access instance objects. I know a work-around in c++ by setting a global pointer to the instance which can be accessed by shared functions but can't think of a work around in VB. Anyone know how to do this?

The error is on line 30 of the code posted below.

Private Sub TurnOn()
        watcher = New FileSystemWatcher()
        ' Create a new FileSystemWatcher and set its properties. 
        watcher.Path = txtFolder.Text
        ' Watch for changes in LastAccess and LastWrite times, and 
        ' the renaming of files or directories. 
        watcher.NotifyFilter = (NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName)
        ' watch all files.
        watcher.Filter = "*.*"

        ' Add event handlers. 
        AddHandler watcher.Changed, AddressOf OnChanged
        AddHandler watcher.Created, AddressOf OnChanged
        AddHandler watcher.Deleted, AddressOf OnChanged
        AddHandler watcher.Renamed, AddressOf OnRenamed

        ' Begin watching.
        watcher.EnableRaisingEvents = True

        ' Wait for the user to quit the program.
        'Console.WriteLine("Press 'q' to quit the sample.")
        'While Chr(Console.Read()) <> "q"c
        'End While
        'Return True
        btnOnOff.Text = "OFF"
    End Sub

    Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)
        ' Specify what is done when a file is changed, created, or deleted.
        txtFolder.Text = txtFolder.Text + "File: " & e.FullPath & " " & e.ChangeType '<<<< error on this line
    End Sub

The error message is
error BC30369: Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.

Other than the obvious, drop the "Shared" keyword, you can use any of the reference syntaxes to the form shown below.

Jim is correct that you will bump into cross thread issues with the FileSystemWatcher events, so I include an example of that technique as well.

Public Class Form1

   'create a delegate template for the method
   Delegate Sub dummy(ByVal caller As TextBox, ByVal text As String)

   ' a method that matches the template defined above
   Sub settext(ByVal caller As TextBox, ByVal text As String)
      caller.Text = text
   End Sub

   Private Shared Sub test()
      My.Forms.Form1.TextBox1.Text = "wilma"
      CType(My.Application.OpenForms.Item("Form1"), Form1).TextBox1.Text = "barney"

      ' Since you are handling events, these events my arrive on a thread other than the UI thread.
      ' To prevent a cross thread error, check if an Invoke is needed
      If Form1.TextBox1.InvokeRequired Then
         ' invoke a call on the UI thread
         Form1.TextBox1.Invoke(New dummy(AddressOf Form1.settext), New Object() {Form1.TextBox1, "dino"})
      Else
         Form1.TextBox1.Text = "fred"
      End If
   End Sub

End Class

Edit: The reference methods shown above assume that you have the "Application Framework" enabled.

commented: Very good suggestion :) +14

In VB.NET, we can use className (i.e. name of Form class - eg Form1.Text) to access the instance members from within the static methods or other forms/classes. And we use Me keyword if we want to use members of FORM from within the instance methods of current form.

So the we can access controls and their properties via:

form1.TextBox1.Text="Hello" 

However, you can't use this methods (above mentioned) to access/change the controls properties outside the UI thread(into the handlers). You need to call Control.Invoke or Control.BeginInvoke method so that way you can update UI from secondary thread.

So I'd like to suggest two solutions:

Workaround First : Make that handler non-static (non-shared)

Private Sub OnChanged(source As Object, e As FileSystemEventArgs)
        TextBox1.Invoke(Sub()
                            TextBox1.AppendText(e.FullPath & vbCrLf)
                        End Sub)
End Sub

Workaround second : For static event handler, declare shared form reference variable,

Shared frm As Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    TurnOn()
    frm = Me
End Sub
Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)
    frm.TextBox1.Invoke(Sub()
                            frm.TextBox1.AppendText(e.FullPath & vbCrLf)
                        End Sub)
End Sub

In VB.Net (with explicit type) the syntax would be

Dim reftext As TextBox = Me.Controls("TextBox1")
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.