Updating UI Elements from a Background Thread – Part 4

In this post, I will discuss how to update UI elements from a background thread in WPF. In the previous three posts about multithreading, I provided solution for WinForm applications. These techniques can also be applied to the WPF Windows applications. The ReportProgress method in WinFowm is almost identical to that in the WPF, so I’ll not discuss it here.

In WinForm, we can use the delegate + Invoke method to update UI elements. In WPF, we have got something similar but more powerful – the Dispatcher. Note that every Visual in WPF inherits from DispatcherObject, which allows you to get a hold of the UI thread’s Dispitcher. Anytime you update a Visual, the work is sent through the Dispatcher to the UI thread.

In the following I present a WPF example. The layout in this example is similar to that used in Part 3. Here is the layout created using the following XAML

<Window x:Class="WpfBackgroundworkerTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="289" Width="305">
    <Grid>
        <TextBlock Height="23" HorizontalAlignment="Left"
                   Margin="28,32,0,0" Name="textBlock1"
                   Text="TextBlock" VerticalAlignment="Top" />
        <TextBox Height="23" HorizontalAlignment="Left"
                 Margin="28,61,0,0" Name="textBox1"
                 VerticalAlignment="Top" Width="227" />
        <TextBox Height="23" HorizontalAlignment="Left"
                 Margin="28,90,0,0" Name="textBox2"
                 VerticalAlignment="Top" Width="227" />
        <TextBlock Height="23" HorizontalAlignment="Left"
                   Margin="28,129,0,0" Name="textBlock2"
                   Text="TextBlock" VerticalAlignment="Top" />
        <Button Content="Start" Height="23"
                HorizontalAlignment="Left" Margin="180,169,0,0"
                Name="btnStart" VerticalAlignment="Top" Width="75"
                Click="btnStart_Click" />
        <Button Content="Cancel" Height="23"
                HorizontalAlignment="Left" Margin="180,198,0,0"
                Name="btnCancel" VerticalAlignment="Top" Width="75"
                Click="btnCancel_Click" />
    </Grid>
</Window>

And below shows code-behind file:


using System;
using System.Windows;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;

namespace WpfBackgroundworkerTest
{
    public partial class MainWindow : Window
    {
        private delegate void UpdateDelegate(object[] x);
        private BackgroundWorker worker1;

        public MainWindow()
        {
            InitializeComponent();
            worker1 = new BackgroundWorker();
            worker1.WorkerReportsProgress = true;
            worker1.WorkerSupportsCancellation = true;
            worker1.DoWork += new DoWorkEventHandler(worker1_DoWork);
                       worker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
                worker1_RunWorkerCompleted);
        }

        void worker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
              textBlock2.Text = "Calculation Completed!";
            else
              textBlock2.Text = "Calculation Cancelled!";

            btnStart.IsEnabled = true;
            btnCancel.IsEnabled = false;

        }
        void worker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                Thread.Sleep(100);
                if (worker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                object[] x = new object[] { i, Math.Sin(1.0 * i / 10.0),
                       Math.Cos(1.0 * i / 10.0) };

                UpdateDelegate update = new UpdateDelegate(UpdateUIElements);
                this.Dispatcher.BeginInvoke(update, new object[] { x });
            }
        }

         private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            textBlock2.Text = "Calculation Start!";
            worker1.RunWorkerAsync();
            btnStart.IsEnabled = false;
            btnCancel.IsEnabled = true;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            worker1.CancelAsync();
        }

        private void UpdateUIElements(object[] x)
        {
            textBlock1.Text = " i = " + x[0].ToString();
            textBox1.Text = "Sin =  " + x[1].ToString();
            textBox2.Text = "Cos =  " + x[2].ToString();
        }
    }
}

There are two things I want to point out here. First, the BackgroundWorker is not a WPF component, so you won’t see it in the toobox, which means you cannot create a backgroundworker object visually in the Designer. You have to create it in the code-behind file. The other point is that you need to replace the Invoke method in WinForm by the Dispatcher.BeginInvoke.

Running this project produces the similar results as in Part 3.

Posted in .NET and CSharp | 1 Comment

VBA: Export/Import Excel Files

You will often need to import or export Excel files from/to another workbook when you develop Excel applications using VBA. Here I present two Subs which can help you to perform this kind of job more easily.

Sub ImportExcelFile(ByVal sh As Worksheet, ByVal fileName As String)
    Dim wb As Workbook
    Set wb = Workbooks.Open(fileName, , False)
    wb.Sheets(1).UsedRange.Copy sh.Cells(1, 1)
    wb.Close SaveChanges:=False
    Set wb = Nothing
    Set Rng = Nothing
End Sub

Sub ExportExcelFile(ByVal sh As Worksheet, ByVal fileName As String, _
                           ByVal fileFormat As Integer)
    Dim wb As Workbook
    Set wb = Workbooks.Add
    sh.UsedRange.Copy wb.Sheets(1).Cells(1, 1)
    wb.SaveAs fileName:=fileName, fileFormat:=fileFormat
    wb.Close
    Set wb = Nothing
End Sub

The first Sub is used to import the first worksheet of an Excel file to the current opened workbook. To use this Sub, you need to specified the worksheet in the current workbook, (this worksheet is used to hold the imported data), and the file name to be imported.

The second Sub is used to export a specified worksheet in the current workbook to a output file. You also need to specify the file format. As you probably know, there are so many new file formats in Excel 2007 – 2010. In order to export data in a correct file format, you need to provide the file format as an input paramter. Please note that it is always better to use the file format numbers instead of the defined constants. Here I list some frequently used file formats and their corresponding file format numbers:

  • 50 = .xlsb (binary Excel file without macro, Excel 2007-2010)
  • 51 = .xlsx (standard Excel file without macro, Excel 2007-2010)
  • 52 = .xlsm (standard Excel file with macro, Excel 2007-2010)
  • 56 = .xls (standard Excel file, Excel 1997-2003)
  • 6 = .csv (comma delimited CSV file)
  • -4158 = .txt (standard text file)
  • 36 = .prn (file used for printer)

It is easy to use these two Subs in your VBA applications. Here is an example:

Sub Button1_Click()
    Application.DisplayAlerts = False

    Dim sh1, sh2, sh3 As Worksheet
    Set sh1 = Sheets("Sheet1")
    Set sh2 = Sheets("Sheet2")
    Set sh3 = Sheets("Sheet3")

    ExportExcelFile sh3, sh1.Range("exportFile").Value, 6

    sh2.Cells.Clear
    ImportExcelFile sh2, sh1.Range("importFile").Value
End Sub

For your reference, I attach this Excel application here. You can view the VBA code and play it around to see how it works.

Posted in Excel and .NET | Leave a comment

CSV File and DataTable Conversion

Many readers asked me how to convert the comma delimited CSV file to a ADO.NET Datatable. This conversion is needed more frequently in the financial field. As a .NET developer, you may surprise at first if you see so many folks in the front/back office using Excel spreadsheets. You have to deal with interoperations between Excel spreadsheets and .NET applications daily. One of these interoperations is the CSV to datatable conversion or vice versa.

In fact, the conversion between CSV and datatable is very simple and there are several technologies available for the conversion. Here I present a method for the conversion by simply using a StreamWriter. If you need to write a text or CSV file in your C# project, the StreamWriter is the best available method. It is a convenient and powerful way to write and append to text or CSV files.

Here I present the code list for CSV to Datatable and Datatable to CSV conversions:


        public void datatable_to_csv(DataTable dt, string csvFile)
        {
            // Create the CSV file to which Datatable data will be exported.
            StreamWriter sw = new StreamWriter(csvFile, false);

            int iColCount = dt.Columns.Count;

            // First we will write the headers if IsFirstRowColumnNames is true:
            for (int i = 0; i < iColCount; i++)
            {
                sw.Write(dt.Columns[i]);
                if (i < iColCount - 1)
                {
                    sw.Write(',');
                }
            }
            sw.Write(sw.NewLine);

            // Now write all the rows.
            foreach (DataRow dr in dt.Rows)
            {
                for (int i = 0; i < iColCount; i++)
                {
                    if (!Convert.IsDBNull(dr[i]))
                    {
                        sw.Write(dr[i].ToString());
                    }
                    if (i < iColCount - 1)
                    {
                        sw.Write(',');
                    }
                }
                sw.Write(sw.NewLine);
            }
            sw.Close();
        }

        public DataTable csv_to_datatable(string csvFile)
        {
            FileStream fs = new FileStream(csvFile, FileMode.Open, FileAccess.Read,
                                                      FileShare.ReadWrite);
            StreamReader sr = new StreamReader(fs);
            string[] Lines = File.ReadAllLines(csvFile);

            string[] Fields = Lines[0].Split(new char[] { ',' });
            DataTable dt = new DataTable();
            int Cols = Fields.GetLength(0);

            //1st row must be column names.
            for (int i = 0; i < Cols; i++)
                dt.Columns.Add(Fields[i], typeof(string));

            DataRow Row;
            for (int i = 1; i < Lines.GetLength(0); i++)
            {
                Fields = Lines[i].Split(new char[] { ',' });
                Row = dt.NewRow();
                for (int j = 0; j < Cols; j++)
                    Row[j] = Fields[j];
                dt.Rows.Add(Row);
            }
            return dt;
        }

The above methods are applied to the comma delimited CSV files. If you are dealing with the other delimiters other than comma, you can simply change the code in red to any delimiter or just place the delimiter as a input parameter to be applicable for any delimiter.

In the following, I'll create a WinForm application to show you how to use the CSV/Datatable conversion methods. The following image is the form1 layout:

The layout contains two TextBox controls used to specify the input and export CSV files respectively. The dataGridView1 in the layout is used to display the datatable from the input CSV file. Here is the code:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Windows.Forms;

namespace CsvDatatableTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetInputData_Click(object sender, EventArgs e)
        {
            DataTable dt = csv_to_datatable(textBoxInputCsv.Text);
            dataGridView1.DataSource = dt;
            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
        }

        private void btnExportData_Click(object sender, EventArgs e)
        {
            DataTable dt = (DataTable)dataGridView1.DataSource;
            datatable_to_csv(dt, textBoxExportCsv.Text);
            MessageBox.Show("Exporting data to a CSV file finished.");
        }

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

You can see how easy it is to perform the CSV/Datatable conversion in your C# project. Also note that you can make any changes to the data in the dataGridView1 directly, then cast the datGridView1's DataSource property to a Datatable using the statement:

DataTable dt = (DataTable)dataGridView1.DataSource;

and finally export this Datatable to a CSV file.

Another thing I should point out is that the input/export CSV file name used in our CSV/Datatable conversion methods MUST be the full path + file name. In our example above, the CSV files are locacted at "[Application directory]\Bin\Debug\" directory, which is why you can use the file name only. Otherwise, you must use the full file path + file name.

Posted in .NET and CSharp, Excel and .NET | 1 Comment

Updating UI Elements from a Background Thread – Part 3

In my previous post, Part 2 , I demonstrated how to update a single UI element from a background thread. In this post, I’ll show you the way to update multiple UI elements from a background thread. We first use the delegate + Invoke method. The layout is still the same as the form1 in Part 1 . Here I first list the code:


using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace BkgroundWorkerTest01
{
    private delegate void UpdateDelegate(object[] x);

    public partial class Form1 : Form
    {
         public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerSupportsCancellation = true;
                 backgroundWorker1.DoWork += new DoWorkEventHandler(
                           backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                           backgroundWorker1_RunWorkerCompleted);
        }
        void backgroundWorker1_RunWorkerCompleted(object sender,
                 RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
                label2.Text = "Calculation Completed!";
            else
                label2.Text = "Calculation Cancelled!";
        }

        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                Thread.Sleep(100);
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                object[] x = new object[] { i, Math.Sin(1.0 * i / 10.0),
                            Math.Cos(1.0 * i /10.0) };
                UpdateDelegate update = new UpdateDelegate(UpdateUIElements);
                textBox1.Invoke(update, new object[] { x });
            }
        }

        private void UpdateUIElements(object[] x)
        {
            label1.Text = " i = " + x[0].ToString();
            textBox1.Text = "Sin =  " + x[1].ToString();
            textBox2.Text = "Cos =  " + x[2].ToString();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

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

You can see from the above code that the first change you need to make is the delegate function where the input variable becomes the object array. You can report progress for any number of parameters with any data type by using this object array, and then update any number of UI elements using these parameters inside the UpdateUIElements method.

Running this project by pressing F5 produces the results shown in the following figure:

In the following, we will use the ReportProgress method to update the multiple UI elements. Her is the code list:


using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace BkgroundWorkerTest01
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerSupportsCancellation = true;
            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.DoWork += new DoWorkEventHandler(
                  backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                backgroundWorker1_RunWorkerCompleted);
            backgroundWorker1.ProgressChanged +=
                new ProgressChangedEventHandler(
                backgroundWorker1_ProgressChanged);
        }

        void backgroundWorker1_ProgressChanged(object sender,
               ProgressChangedEventArgs e)
        {
               UpdateUIElements((object[])e.UserState);
        }

        void backgroundWorker1_RunWorkerCompleted(object sender,
               RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
                label2.Text = "Calculation Completed!";
            else
                label2.Text = "Calculation Cancelled!";
        }

        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                Thread.Sleep(100);
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                object[] x = new object[] { i, Math.Sin(1.0 * i / 10.0),
                       Math.Cos(1.0 * i / 10.0) };

                backgroundWorker1.ReportProgress(0, x);
            }
        }

        private void UpdateUIElements(object[] x)
        {
            label1.Text = " i = " + x[0].ToString();
            textBox1.Text = "Sin =  " + x[1].ToString();
            textBox2.Text = "Cos =  " + x[2].ToString();
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

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

Running this application, you will get the same results as shown in the above delegate + Involke method.

Posted in .NET and CSharp | Leave a comment

Updating UI Elements from a Background Thread – Part 2

In the previous post, I showed you the possible issue associated with updating UI Elements from the background thread. In this post, I will show you two easy ways to fix the problem.

The first method is to use the delegate function and the UI elements’ Invoke method. In C#, a delegate is very similar to a function pointer in C and C++, and it encapsulates a reference to a method inside a delegate object. This delegate object can be passed to code that calls the reference method. Unlike function pointer in C or C++, a delegate is object-oriented, type-safe, and more secure.

There are several types of the Invoke methods in .NET. I list them here for your reference:

Delegate.Invoke is used to execute a delagate on the current thread.

Delegate.BeginInvoke is used to execute a delegate on a separate thread. This means you can start an operation which won’t block your current thread and it will be executed on its own thread.

Control.Invoke is used to executed a delegate on the UI thread of that element. If you have a delegate which updates the user interface, you can call this method from the other thread to execute the update operation on the UI thread.

Control.BeginInvoke is similar to the above method, but it does in an asynchronous way. This means while Control.Invoke waits until the UI thread has finished executing the delegate, Control.BinginInvoke returns immediately.

Here I’ll show you how to use the delegate and UI elements’ Invoke method to fix the problem in my previous post. Start with a new Winform project and use the same layout of the form1 as in Part 1. The code is almost identical to Part 1, except for adding a delegate function, a updating UI element method, and a UI element’s Invoke method, as shown in red in the following code list:


using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace BkgroundWorkerTest01
{
    public partial class Form1 : Form
    {
        private delegate void UpdateDelegate(double x);
        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerSupportsCancellation = true;
            backgroundWorker1.DoWork += new DoWorkEventHandler(
                    backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                backgroundWorker1_RunWorkerCompleted);
         }

        void backgroundWorker1_RunWorkerCompleted(object sender,
                RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
                label2.Text = "Calculation Completed!";
            else
                label2.Text = "Calculation Cancelled!";
        }

        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                Thread.Sleep(100);
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                Double x = Math.Sin(1.0 * i / 10.0);
                UpdateDelegate update = new UpdateDelegate(UpdateUIElement);
                textBox1.Invoke(update, new object[] { x });
                //textBox1.Text = x.ToString();

            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

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

        private void UpdateUIElement(double x)
        {
            textBox1.Text = x.ToString();
        }
    }
}

Now, running the program by pressing F5 and clicking on the “Start” button, you will find the textbox1 is updating the calculation results from the background thread.

In the following, I’ll discuss another method for updating UL elements from the background thread. The BackgroundWorker has a special method called ReportProgress. If you need the background computations to report on its progress, you can call this method to raise the ProgressChanged event. Remember that in order to use this method, you must set the WorkerReportsProgress property value to true, otherwise ReportProgress will throw an InvalidOperationException.

Using the same form1 layout. The following code shows you how to update UI elements using the ReportProgress method:


using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace BkgroundWorkerTest01
{
    public partial class Form1 : Form
    {
      //private delegate void UpdateDelegate(double x); 

        public Form1()
        {
            InitializeComponent();
            backgroundWorker1.WorkerSupportsCancellation = true;
            backgroundWorker1.DoWork += new DoWorkEventHandler(
                 backgroundWorker1_DoWork);
            backgroundWorker1.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                backgroundWorker1_RunWorkerCompleted);
           backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.ProgressChanged +=
                new ProgressChangedEventHandler(
                backgroundWorker1_ProgressChanged);
        }

        void backgroundWorker1_ProgressChanged(object sender,
                          ProgressChangedEventArgs e)
        {
          UpdateUIElement(Convert.ToDouble(e.UserState));
        }

        void backgroundWorker1_RunWorkerCompleted(object sender,
               RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
                label2.Text = "Calculation Completed!";
            else
                label2.Text = "Calculation Cancelled!";
        }

        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 500; i++)
            {
                Thread.Sleep(100);
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                    return;
                }

                Double x = Math.Sin(1.0 * i / 10.0);
                backgroundWorker1.ReportProgress(0, x);
                //UpdateDelegate update = new UpdateDelegate(UpdateUIElement);
                //textBox1.Invoke(update, new object[] { x });

                //textBox1.Text = x.ToString();

            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            backgroundWorker1.CancelAsync();
        }

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

        private void UpdateUIElement(double x)
        {
            textBox1.Text = x.ToString();
        }
    }
} 

The red part in the above code shows the difference between the Invoke and ReportProgress methods. Now, running this program again, you will see the textBox1 is updating the results from the background thread. Here we use the UserState to report the progress.

You may notice that both the above examples only update a single UI element, textBox1, from the background thread. I also got several emails asking how to update multiple UI elements from a background thread. This will be the topic of my next post.

Posted in .NET and CSharp | Leave a comment

Updating UI Elements from a Background Thread – Part 1

I got many emails from some new C# developers who asked me the same question – how to update the UI Elements from the backgroundworker. Well, I made the same mistake as you did before. Let’s start with an example using C# and Winform. The form layout is very simple, and it contains two Labels, a TextBox, and two Buttons, as shown below:

In addition, I also added a BackgroundWorker to the Form at design-time using the Visual Studio Designer. Here is the code implemented by a .NET beginner:

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace BkgroundWorkerTest01
{
    public partial class Form1 : Form
    {
        public Form1()
        {
             InitializeComponent();
             backgroundWorker1.WorkerSupportsCancellation = true;
             backgroundWorker1.DoWork += new DoWorkEventHandler(
                 backgroundWorker1_DoWork);
             backgroundWorker1.RunWorkerCompleted +=
                 new RunWorkerCompletedEventHandler(
                        backgroundWorker1_RunWorkerCompleted);
        }

        void backgroundWorker1_RunWorkerCompleted(object sender,
             RunWorkerCompletedEventArgs e)
        {
            if (!(e.Cancelled))
                label2.Text = "Calculation Completed!";
            else
                label2.Text = "Calculation Cancelled!";
        }

        void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 0; i < 100; i++)
            {
                Thread.Sleep(100);
                 if (backgroundWorker1.CancellationPending)
                 {
                     e.Cancel = true;
                     return;
                 }
                 Double x = Math.Sin(1.0 * i / 10.0);
                 textBox1.Text = x.ToString();
            }
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
             backgroundWorker1.CancelAsync();
        }

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

When running the program by pressing F5 and clicking on the “Start” button, you will get the following error:

“Cross-thread operation not valid: Control ‘textBox1′ accessed from a thread other than the thread it was created on.”

The reason for this error message is that controls in .NET are not thread safe. (Console application is an exception, which is thread safe. This is why you can report the background thread progress directly using the Console.WriteLine method). That means you cannot access a control from a thread other than the one where it lives, and the UI elements can be only updated in the UI thread rather than the BackgroundWorker. In Winform, there are two easy ways to fix the problem. I will report the solutions in next post!

Posted in .NET and CSharp | Leave a comment