Search code examples
c#winformsbackgroundworkerfilesystemwatchercontextmenustrip

C# WinForm: contextMenuStrip throws InvalidOperationException after .Show() method when called from BackgroundWorker_ProgressChanged event


I have a WindowsForm in C# with a TrayIcon including a contextMenuStrip with a ToolStripTextBox, a FileSystemWatcher and a BackgroundWorker.

The FileSystemWatcher throws an event when a new file is created which then starts the BackgroundWorker. The BackgroundWorker reports progress which updates a ToolStripTextBox on the TrayIcon in the ProgressChanged event handler.

Curiously this works fine as long as the ToolStripMenu has not been visible since the start of the program. As soon as I right-click on the TrayIcon to show the ToolStripMenu (regardless of the BackgroundWorker being idle or not), the ToolStripTextBox starts to throw an InvalidOperationException (invalid cross-thread operation) every time I try to update its .Text property. System.InvalidOperationException is thrown here

When I start the BackgroundWorker from a button click event it all works fine, too. I can see the ToolStripTextBox updating.

What is different when I start the BackgroundWorker from the FileSystemWatcher event? Or rather what is different after showing the contextMenuStrip? Does the contextMenuStrip belong to another thread after showing?

I might find another way to show the progress instead of the ToolStripTextBox but I am curious to know what causes this. I'd be very glad if you could help.

Minimal code example below.


public partial class Form1 : Form
{
    FileSystemWatcher watcher = new FileSystemWatcher();
    string watcherpath = @"C:\Temp\files";
    BackgroundWorker bgw = new BackgroundWorker();
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        watcher.Path = watcherpath;
        watcher.Created += Watcher_Created;
        watcher.EnableRaisingEvents = true;
        bgw = new BackgroundWorker();
        bgw.DoWork += Bgw_DoWork;
        bgw.RunWorkerCompleted += Bgw_RunWorkerCompleted;
        bgw.WorkerSupportsCancellation = true;
        bgw.WorkerReportsProgress = true;
        bgw.ProgressChanged += Bgw_ProgressChanged;
    }

    private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        toolStripTextBox1.Text = $"Progress: {e.ProgressPercentage}%";
    }

    private void Bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        MessageBox.Show("Done!");
    }

    private void Bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        for (int i = 1; i <= 10; i++)
        {
            Thread.Sleep(1000);
            (sender as BackgroundWorker).ReportProgress(i * 10);
        }
    }

    private void Watcher_Created(object sender, FileSystemEventArgs e)
    {
        File.Delete(e.FullPath);
        bgw.RunWorkerAsync();
    }

    private void toolStripMenuItem1_Click(object sender, EventArgs e)
    {
        this.Close();
    }

    private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Right)
            contextMenuStrip1.Show(Cursor.Position);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        bgw.RunWorkerAsync();
    }
}***

Solution

  • Please try below code

        private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
             SetToolStripText(e.ProgressPercentage)
        }
    
        private void SetToolStripText(object ProgressPercentage)
        {
                this.BeginInvoke((MethodInvoker)delegate { this.toolStripTextBox1.Text = $"Progress: {e.ProgressPercentage}%";
        }