CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 3 of 3
  1. #1
    Join Date
    Dec 2008
    Location
    Canada, Saskatchewan province
    Posts
    29

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

    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,
    Ricky,

  2. #2
    Join Date
    Jan 2006
    Location
    Fox Lake, IL
    Posts
    15,007

    Re: Help with updating the GUI thread to get proper access to my TextBox.Text propert

    I wrote one in VB.Net that watches .htm files

    Code:
    Imports System.Security.Permissions
    Imports System.IO
    
    Public Class Watcher
    
        Public Shared Sub Main()
    
            Run()
    
        End Sub
    
        <PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
        Private Shared Sub Run()
    
            Dim args() As String = System.Environment.GetCommandLineArgs()
            ' If a directory is not specified, exit the program.
            If args.Length <> 2 Then
                ' Display the proper way to call the program.
                Console.WriteLine("Usage: Watcher.exe (directory)")
                Return
            End If
    
            ' Create a new FileSystemWatcher and set its properties.
            Dim watcher As New FileSystemWatcher()
            watcher.Path = args(1)
            ' 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)
            ' Only watch text files.
            watcher.Filter = "*.htm"
    
            ' 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
        End Sub
    
        ' Define the event handlers.
        Private Shared Sub OnChanged(ByVal source As Object, ByVal e As FileSystemEventArgs)
            ' Specify what is done when a file is changed, created, or deleted.
            Dim wct As WatcherChangeTypes = e.ChangeType
            Console.WriteLine("File: " & e.FullPath & " " & wct.ToString)
        End Sub
    
        Private Shared Sub OnRenamed(ByVal source As Object, ByVal e As RenamedEventArgs)
            ' Specify what is done when a file is renamed.
            Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath)
        End Sub
    
    End Class
    David

    CodeGuru Article: Bound Controls are Evil-VB6
    2013 Samples: MS CODE Samples

    CodeGuru Reviewer
    2006 Dell CSP
    2006, 2007 & 2008 MVP Visual Basic
    If your question has been answered satisfactorily, and it has been helpful, then, please, Rate this Post!

  3. #3
    Join Date
    May 2007
    Posts
    1,546

    Re: Help with updating the GUI thread to get proper access to my TextBox.Text propert

    Quote Originally Posted by RickyWh View Post

    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....
    It's a terrible example, they never defined what SetTextCallback is. I believe it's definition should be:

    Code:
    public delegate void SetTextCallback (string text)
    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.
    Which is exactly what I'd expect to happen. The background worker is generally speaking poorly explained and often mis-used. If you check up the docs on the background worker, it explains exactly how it is supposed to be used. Here's the important part:

    http://msdn.microsoft.com/en-us/libr...undworker.aspx
    You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the ProgressChanged and RunWorkerCompleted events.
    One option is to have a class like this:

    Code:
        public delegate void Task();
    
        public partial class MainForm : Form
        {
            public MainForm()
            {
                InitializeComponent();
                ThreadPool.QueueUserWorkItem(DoTask);
            }
    
            private void DoTask(object o)
            {
                int count = 5;
                while (count > 0)
                {
                    // The anonymous method will automagically convert into a 'Task' object
                    // You can also write: Task t = delegate { textbox.Text = count.ToString() };  InvokeTask (t);
                    InvokeTask(delegate { textbox.Text = count.ToString(); });
                    System.Threading.Thread.Sleep(1000);
                    count--;
                }
            }
    
            void InvokeTask (Task task)
            {
                this.Invoke(task);
            }
        }
    Then whenever you want to safely touch the GUI (read *or* write) you can just call InvokeTask with a delegate like I've done in the above. This is pretty much exactly what the BackgroundWorker does with its two events.
    Last edited by Mutant_Fruit; February 15th, 2009 at 03:13 PM.
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured