Return to Snippet

Revision: 39446
at January 18, 2011 00:45 by kyrathaba


Initial Code
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.ComponentModel;
using System.Threading;
using System.IO;

namespace CrossThreadCallsToAControl {
    public partial class Form1 : Form {

        // This delegate enables asynchronous calls for setting
        // the text property on a TextBox control.
        delegate void SetTextCallback(string text);

        // This thread is used to demonstrate both thread-safe and
        // unsafe ways to call a Windows Forms control.
        private Thread demoThread = null;

        // This BackgroundWorker is used to demonstrate the 
        // preferred way of performing asynchronous operations.
        private BackgroundWorker backgroundWorker1;

        private delegate void DelegateOpenFile(String s);
        DelegateOpenFile _openFileDelegate;

        public const string appTitle = "My App Title";       

        public Form1() {
            InitializeComponent();
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Text = appTitle;
            this.AllowDrop = true;
            _openFileDelegate = new DelegateOpenFile(this.OpenFile);
            backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.RunWorkerCompleted += 
                new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
        }

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

        private void Form1_MouseClick(object sender, MouseEventArgs e) {
            switch (e.Button) {
                case MouseButtons.Left:
                    break;
                case MouseButtons.Right:
                    string path = Path.GetDirectoryName(Application.ExecutablePath);
                    System.Diagnostics.Process.Start("explorer.exe", path);
                    break;
                default:
                    break;
            }
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Escape) { Application.Exit(); }
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e) {
            OpenFileDialog openDlg = new OpenFileDialog();
            openDlg.Filter = "Any File (*.*)|*.*";

            openDlg.FileName = "";
            openDlg.CheckFileExists = true;
            openDlg.CheckPathExists = true;

            if (openDlg.ShowDialog() != DialogResult.OK)
                return;

            OpenFile(openDlg.FileName);
        }

        private void OpenFile(string sFile) {
            //insert appropriate file-opening code here...
            MessageBox.Show("\"" + sFile + "\" will be opened.");
        }

        private void Form1_DragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

        private void Form1_DragDrop(object sender, DragEventArgs e) {
            try {
                Array a = (Array)e.Data.GetData(DataFormats.FileDrop);
                if (a != null) {
                    // Extract string from first array element
                    // (ignore all files except first if number of files are dropped).
                    string s = a.GetValue(0).ToString();
                    // Call OpenFile asynchronously.
                    // Explorer instance from which file is dropped is not responding
                    // the entire time that the DragDrop handler is active, so we need to return
                    // immidiately (especially if OpenFile shows MessageBox).
                    this.BeginInvoke(_openFileDelegate, new Object[] { s });
                    this.Activate();        // in the case Explorer overlaps this form
                }
            }
            catch (Exception ex) {
                Trace.WriteLine("Error in DragDrop function: " + ex.Message);
                // don't show MessageBox here - Explorer is waiting !
            }
        }

        private void setTextUnsafeBtn_Click(object sender, EventArgs e) {
            this.demoThread =
                new Thread(new ThreadStart(this.ThreadProcUnsafe));

            this.demoThread.Start();
        }

        // This method is executed on the worker thread and makes
        // an unsafe call on the TextBox control.
        private void ThreadProcUnsafe() {
            this.textBox1.Text = "This text was set unsafely.";
        }

        private void setTextSafeBtn_Click(object sender, EventArgs e) {
            this.demoThread =
                new Thread(new ThreadStart(this.ThreadProcSafe));

            this.demoThread.Start();
        }

        // This method is executed on the worker thread and makes
        // a thread-safe call on the TextBox control.
        private void ThreadProcSafe() {
            this.SetText("This text was set safely.");
        }

        // This method demonstrates a pattern for making thread-safe
        // calls on a Windows Forms control. 
        //
        // If the calling thread is different from the thread that
        // created the TextBox control, this method creates a
        // SetTextCallback and calls itself asynchronously using the
        // Invoke method.
        //
        // If the calling thread is the same as the thread that created
        // the TextBox control, the Text property is set directly. 

        private void SetText(string text) {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.textBox1.InvokeRequired) {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else {
                this.textBox1.Text = text;
            }
        }

        private void setTextBackgroundWorkerBtn_Click(object sender, EventArgs e) {
            this.backgroundWorker1.RunWorkerAsync();
        }

        // This event handler sets the Text property of the TextBox
        // control. It is called on the thread that created the 
        // TextBox control, so the call is thread-safe.
        //
        // BackgroundWorker is the preferred way to perform asynchronous
        // operations.

        private void backgroundWorker1_RunWorkerCompleted(
            object sender,
            RunWorkerCompletedEventArgs e) {
            this.textBox1.Text =
                "This text was set safely by BackgroundWorker.";
        }


    }

}

Initial URL


Initial Description
demonstrates safe versus unsafe ways to make cross-thread calls

Initial Title
Safe vs. UnSafe cross-thread call

Initial Tags


Initial Language
C#