It is quite annoying when a .net program try to update user interface control from a seperate long run task thread.
The runtime give me exception “Control control name accessed from a thread other than the thread it was created on.”
If it is in visual studio 2005, a lot of place recommend use "BackgroundWorker" to solve the problem.
But it is also not flexiable, It works fine with progressbar control only. If I need to display something on textbox, It is not straigt forward. For example, I want to change textbox background color.
I created my own skeleton code which can easily access any user interface control from seperate thread.
The key part is in UpdateUI_handler subroutine.
And if you want to access one control, you just need tell
- what control you want to access,
- what property of the control you want to access,
- and what value you want to set.
That is very easy approach to solve the problem.
Below is my sample application snapshot
You can follow 3 steps below to create the project
- Create vb winform application in visual studio 2005.
- Drag a textbox, a label, a progressbar and a button on the form.
- Copy the code below and overwrite form1.vb
1: Imports System
2: Imports System.Threading
3: Imports system.reflection
4: Public Class Form1
5: Delegate Sub UpdateUI(ByVal UpdateParameter As UIUpdateParameter)
6: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
7: Dim index As Int32
8: index = 10
9: Me.ProgressBar1.Maximum = index
10: Debug.WriteLine("Main Form thread: " & AppDomain.GetCurrentThreadId)
11: Dim thread As Thread
12: thread = New Thread(AddressOf DoTask)
13: thread.IsBackground = True
14: thread.Start()
15: End Sub
16:
17: Private Sub UpdateUI_handler(ByVal UpdateParameter As UIUpdateParameter)
18: Debug.WriteLine("Update progress bar thread: " & AppDomain.GetCurrentThreadId)
19: Dim theControlType As Object = Nothing
20: Dim theControl As Control = Nothing
21: For Each p As System.Reflection.PropertyInfo In Me.Controls(UpdateParameter.controlName).GetType().GetProperties()
22: theControl = Me.Controls(UpdateParameter.controlName)
23: theControlType = Convert.ChangeType(theControl, theControl.GetType)
24: If Not IsNothing(p.GetValue(theControlType, Nothing)) Then
25: If UCase(p.Name.ToString) = UCase(UpdateParameter.PropertyName) Then
26: p.SetValue(theControlType, Convert.ChangeType(UpdateParameter.value, p.PropertyType), Nothing)
27: End If
28: End If
29: Next
30: End Sub
31: Private Sub DoTask(ByVal t)
32: For i As Integer = 1 To 10
33: Thread.Sleep(500)
34: Debug.WriteLine("Do Task thread: " & AppDomain.GetCurrentThreadId)
35:
36: Dim uiUpdate2 As UpdateUI
37: uiUpdate2 = New UpdateUI(AddressOf UpdateUI_handler)
38:
39: Dim UpdateParameter As New UIUpdateParameter
40: UpdateParameter.controlName = "ProgressBar1"
41: UpdateParameter.PropertyName = "value"
42: UpdateParameter.value = i
43: Me.Invoke(uiUpdate2, UpdateParameter)
44:
45: UpdateParameter.controlName = "Label1"
46: UpdateParameter.PropertyName = "Text"
47: UpdateParameter.value = i
48: Me.Invoke(uiUpdate2, UpdateParameter)
49:
50: UpdateParameter.controlName = "Textbox1"
51: UpdateParameter.PropertyName = "backcolor"
52: If i Mod 2 = 0 Then
53: UpdateParameter.value = Color.Blue
54: Else
55: UpdateParameter.value = Color.Yellow
56: End If
57:
58: Me.Invoke(uiUpdate2, UpdateParameter)
59: Next i
60: End Sub
61: End Class
62:
63: Public Class UIUpdateParameter
64: Public controlName As String
65: Public PropertyName As String
66: Public value As Object
67: End Class