March 20, 2018

Textual description of firstImageUrl

C# - Update Text Boxes from Task Threads

1. Update UI From Threads


Updating a User Interface from a thread is not straight-forward. Say for instance, let us consider a form-based application which spawns a thread. The thread wants to report the progress to the progress bar control in the form. If we try that (Updating the progress from worker thread), we will be getting an error which mentions that thread is not the owner of the progress bar. Then how do we update the UI from a thread.


Once you go through the example, you will know how to do that.

2. Thread Updating Textboxes – About the Example


The below sample application which we are going to develop is going to demonstrate updating the User Interface element from a Thread . Have a look at the screen-shot:

Pic 1. Updating the UI Elements from Thread - About the Example
Pic 1. Updating the UI Elements from Thread - About the Example

The Max Counter textbox (Marked as 1) is used to get upper bounds of the counter. For example, if user gives 150 here, the threads which counts the number continue till 150 starting from Zero. The current counter textboxes (Marked as 2 and 3) are used to display the current counting from the threads. The Start Counter buttons (Marked as 4 and 5) are used to spawn thread to begin counting. The Clear All (Marked as 6) is used to clear all the text boxes.

Note that when we click the buttons 4,5 in immediate successions, we can see two threads counting their numbers and updating the UI (2,3).  

3. Start Coding the Example


3.1 TextBox validation - Range of Values


To reduce the errors on the Max Counter TextBox, we should allow only numbers. At the same time to demonstrate the example, we need a decent value. If a value is too less, we can not see thread updating the UI and if the value is too high then we are hanging the sample for a long time. 

Considering all these, the “Leave Event” of the textbox is handled.  Have a look at the code below:

Pic 2. Max Counter Validation
Pic 2. MaxCounter Validation

In the above code, we are using “TryParse()” function to retrieve the integer value from the textbox. When text box does not have a valid integer value, we end-up in the else box as TryParse fails. In the else box we are setting the textbox to empty and setting the focus on the same control. This makes user stays at the control and they can move out of this control, only after putting down a valid value. 

When TryParse is valid, we are checking that value specified in the textbox is within the acceptable range for the Example (Shown in Green Box). We are allowing the value should be in the range of 200 to 2000. 

3.2) Start the Threads


To use the threads, first include the required namespace into the project. The code is below:

//Sample 02: Required NameSpace
using System.Threading;

After this, we have to create thread objects in the StartCounter button click event handler. Since there are two buttons in the form, we will see CPU serving both the threads by slicing it service time while user clicks the button quick successions. Now have a look at the code below:

Pic 3. Starting the Threads
Pic 3. Starting the Threads

Here, we are making use of the Max Counter textbox content and making sure parse succeed (It is a double check).  The code highlighted in yellow shows that we are creating the “Thread” object by supplying a function to “ThreadStart” delegate. 

“Start()” function call marked as Black box actually starts the background Task. The function supplied to the ThreadStart delegate is called by the Thread.Start() and runs that function in the created thread's context. This way, the Function Threaded_Counter1 runs in a separate thread. 

3.3) Define UI Update Delegates for Threads


We want to update the Thread counting in the UI. But, this is not allowed by the operating system. We will get error stating that the access to UI elements are not allowed to the Threads. This is because the Main Thread is the owner of the UI elements as it created it (& owns it). Now, we need a way that child thread asking the main thread to do the UI update. Using delegate we can ask the main thread to update the UI. Right, look at the delegate declaration below:

//Sample 04: Create a Delegate to Update the UI
private delegate void UI_UpdateDelegate(int NewValue);


The above delegate states that the function returns void and accepts an integer parameter. The integer argument is supplied by the Thread at runtime and the function will make use of the supplied value to show the counting on the fly. Have a look the counting function below:

//Sample 05: UI update functions (Used by Delegate)
private void Update_Counter1(int NewValue)
{
    txtCounter.Text = NewValue.ToString();
}

private void Update_Counter2(int NewValue)
{
    txtCounter2.Text = NewValue.ToString();
}

Both the UI update functions which comply to delegate UI_UpdateDelegate are used to update the counter display Text Boxes. Note that both the delegate functions are running in the Main Thread context and hence it won’t have any trouble in accessing the Text Boxes.

3.4) Crete Counting Threads and Update UI


OK. Now let us implement the Thread Task and send update notifications to Main thread Functions. Have a look at the code below:

Pic 4 Updating UI from Thread
Pic 4 Updating UI from Thread

In the above code, First, we are creating the delegates which points to our UI Update functions created in the previous section  3.3 (Marked as 1). We know that our UI update function expects integer as parameter. The code snippet 2 shows creating a parameter array and setting the integer value to first parameter in the array. Our delegate does not require any other parameter and hence out parameter array has only one element.

The code marked as 3 shows, how we invoke the delegate function.  We are making use “Invoke()” method and passing the delegate to it as first parameter. The parameters required for the delegate function is passed as second parameter. The invoke will call the Update_Counter1() from the Main thread context.  After invoking the UI Update function, we asking the thread to sleep for 5 Milli-seconds so that Main thread can do the UI update and we see the counter incrementing in the corresponding text boxes.

Video 1: Update UI from Threads




4. Code Listings

Listing 1


//Sample 01: Allow valid integer value between 200 to 2000
private void txtMax_Leave(object sender, EventArgs e)
{
    int max_counter;
    if (int.TryParse(txtMax.Text, out max_counter))
    {
        if (max_counter > 2000 || max_counter < 200)
        {
            MessageBox.Show("Set Max Counter between 200 to 2000");
            txtMax.Focus();
        }
    }
    else
    {
        txtMax.Text = string.Empty;
        txtMax.Focus();
    }
}

Listing 2


//Sample 03a: Start Thread1
private void btnStart_Click(object sender, EventArgs e)
{
    long max_counter;
    if (long.TryParse(txtMax.Text, out max_counter))
    {
        Thread T = new Thread(new ThreadStart(Threaded_Counter1));
        T.Start();
    }
}

//Sample 03b: Start Thread2
private void btnStart2_Click(object sender, EventArgs e)
{
    long max_counter;
    if (long.TryParse(txtMax.Text, out max_counter))
    {
        Thread T = new Thread(new ThreadStart(Threaded_Counter2));
        T.Start();
    }
}

Listing 3


//Sample 06a: Threaded Function1 with UI Update
public void Threaded_Counter1()
{
    int max_counter;
    if (int.TryParse(txtMax.Text, out max_counter))
    {
        for (int i = 0; i <= max_counter; i++)
        {
            UI_UpdateDelegate U1_Del =
                     new UI_UpdateDelegate(Update_Counter1);
            object[] delegateParams = new object[1];
            delegateParams[0] = i;
            this.Invoke(U1_Del, delegateParams);
            Thread.Sleep(5);
        }
    }
}

//Sample 06b: Threaded Function2 with UI Update
public void Threaded_Counter2()
{
    int max_counter;
    if (int.TryParse(txtMax.Text, out max_counter))
    {
        for (int i = 0; i <= max_counter; i++)
        {
            UI_UpdateDelegate U2_Del =
           new UI_UpdateDelegate(Update_Counter2);
            object[] delegateParams = new object[1];
            delegateParams[0] = i;
            this.Invoke(U2_Del, delegateParams);
            Thread.Sleep(4);
        }
    }
}


Source Code: Download

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Friend :)