Help with updating the GUI thread to get proper access to my TextBox.Text property

rickywh

Newcomer
Joined
Feb 13, 2009
Messages
3
I'm trying to watch the directory where my application .exe runs from. Watching for directory changes, filename changes, etc etc

I'm using the FileSystemWatcher class and set the Path property to the right directory just fine. When I ran my original application, i used just a regular method for handling the Changed event and tried updating my textBox1.Text property to show the Name property however I was receiving the cross thread exception that shows that your not supposed to do it that way unless you want possible instability....

So I read the MSDN article about how to make thread-safe calls to update GUI controls. There are two known methods for doing so as shown in the MSDN article. http://msdn.microsoft.com/en-us/library/ms171728.aspx

1. Invoke

2. BackgroundWorker


After reading the Invoke example, I get lost at the part where it says this....

Code:
SetTextCallback d = new SetTextCallback(SetText);   this.Invoke(d, new object[] { text });
As shown above, it's implying we know what the SetTextCallback(SetText) method is supposed to do and how it's implemented but since i'm new, i have no idea what this is about....

Instead, I chose to try the BackgroundWorker example instead so after implementing the BackgroundWorker method, I end up receiving the very same cross thread exception as originally when I tried to do it just by trying to set the Text property in the Changed event handler.

So i've implemented the BackgroundWorker method for making the aparently thread-safe update to my TextBoxes Text propertly as shown on the MSDN site, but I still receive the cross thread exception error!

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Net;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace FileSystemWatcherTest1
{
    public partial class Form1 : Form
    {
        BackgroundWorker myWorker1 = new BackgroundWorker();
        FileSystemWatcher myWatcher = new FileSystemWatcher();

        Regex r = new Regex(@".*\..*");
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Set methods for Background worker's DoWork and RunWorkerCompleted events.
            myWorker1.DoWork += new DoWorkEventHandler(myWorker1_DoWork);
            myWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(myWorker1_RunWorkerCompleted);

            // Get this applications path.
            string path = Application.ExecutablePath.ToString();
            string[] results = path.Split(new char[] { '\\' }, StringSplitOptions.None);
            int num = results.Length - 1;
            results.SetValue("", num);
            string finalPath = String.Join("\\", results);

            // set the Path property
            myWatcher.Path = finalPath;
            myWatcher.IncludeSubdirectories = true;
            myWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.LastWrite
                | NotifyFilters.Attributes | NotifyFilters.LastAccess | NotifyFilters.Size;

            // add even handlers
            myWatcher.Changed += new FileSystemEventHandler(myWatcher_Changed);

            // Enable raising events
            myWatcher.EnableRaisingEvents = true;

            //textBox1.Text = "Path has been set on the FileSystemWatcher";
        }

        void myWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // update the UI control here, still gets a cross thread exception...
            this.textBox1.Text += (string)e.Result + Environment.NewLine;
        }

        void myWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bgworker = sender as BackgroundWorker;
            e.Result = (string)e.Argument;
        }

        void myWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            myWorker1.RunWorkerAsync(e.Name);
        }
    }
}
Any help,

Much appreciated,
 
Are you doing anything else involving threads? I can't see why either your original code or the above background worker code would throw a cross thread exception.
 
Thanks for your reply,

My code definitely produces an exception as shown in my screen shot below.

invalidexception1hx4.jpg


Also, I uploaded my whole project in case you want to load it up to see the rest of the code...

http://www.megaupload.com/?d=1474S0I4
 
try
C#:
using System;
using System.ComponentModel;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace FileSystemWatcherTest1
{
    public partial class Form1 : Form
    {
        private readonly FileSystemWatcher myWatcher = new FileSystemWatcher();
        private BackgroundWorker myWorker1 = new BackgroundWorker();

        private Regex r = new Regex(@".*\..*");

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {}

        private void Form1_Load(object sender, EventArgs e)
        {
            // Get this applications path.
            string path = Application.ExecutablePath;
            string[] results = path.Split(new[]{'\\'}, StringSplitOptions.None);
            int num = results.Length - 1;
            results.SetValue("", num);
            string finalPath = String.Join("\\", results);

            // set the Path property
            myWatcher.Path = finalPath;
            myWatcher.SynchronizingObject = this;
            myWatcher.IncludeSubdirectories = true;
            myWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.LastWrite |
                                     NotifyFilters.Attributes | NotifyFilters.LastAccess | NotifyFilters.Size;

            // add even handlers
            myWatcher.Changed += myWatcher_Changed;

            // Enable raising events
            myWatcher.EnableRaisingEvents = true;

            //textBox1.Text = "Path has been set on the FileSystemWatcher";
        }

        private void myWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            MakeChanges(e.Name);
        }

        private void MakeChanges(string result)
        {
            textBox1.Text += result + Environment.NewLine;
        }
    }
}

The key is the line
C#:
myWatcher.SynchronizingObject = this;
as this forces the event to be raised on the UI thread.Due to the fact this class may be used in several different scenarios forcing this single threaded event behaviour would be a major performance hit when direct UI interaction isn't required - setting this property http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.synchronizingobject.aspx gives the full details.
 
Hey PlausiblyDamp, thanks!

Yes that worked, I would have never have otherwise found this out. Yes it solved my problem, may I ask how you find the answer to this one??
 
Back
Top