This is not a duplicate. This particular behavior of a WinForms TabControl has not been explored on StackOverflow.
Check out the example below:
I have a TabControl in .NET 4.0 with two tabs. Each tab has a Label placed within it. When I click the Button , I launch a BackgroundWorker which is now operating on a non-UI thread. If I try to change the Label from tabPage1, I get an InvalidOperationException because of the cross-thread call. But that second line which modifies the Label on tabPage2 runs perfectly fine - no exceptions.
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += Bgw_DoWork;
bgw.RunWorkerAsync();
}
private void Bgw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
label1.Text = "Testing tabPage1"; // This is sitting on tabPage1 - THROWS CROSS THREAD OPERATION
label2.Text = "Testing tabPage2"; // This is sitting on tabPage2 - RUNS FINE
}
catch (Exception ex)
{
throw ex;
}
}
Why is this allowed on tabPage2 but disallowed on tabPage1. In both instances, we are seemingly modifying the UI from a non-UI thread.
This is the setter of the Control.Text
property:
set
{
// some code omitted
this.WindowText = value;
this.OnTextChanged(EventArgs.Empty);
// some code omitted
}
It just forwards to the Control.WindowText
property. Let's inspect that one:
set
{
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
The InvalidOperationException
originates from the Handle
property getter, which is called to obtain a HandleRef
instance for the native method call.
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall, Name));
}
// further code omitted
}
The Handle
property will be only accessed when IsHandleCreated
is true
. In your case, the second label is located on a tab item whose children are not displayed yet, so IsHandleCreated
for the second label is false
. That means, the Handle
property of the second label is not accessed on the BackgroundWorker
's thread. Instead, the text value is just cached in a text
field inside the control. Therefore - no exception.
When you activate the second tab item, the Label
's handle is created, and the .NET Framework code takes the cached text value from the text
field and applies it to the label. This occurs on the UI thread, so again - no exceptions there.
You can first switch to the second tab item and then press your button. You will observe the exception. That is because the handle for the second label will be already created in this case.
As a general note - you should never access the UI elements from worker threads, regardless whether you observe those exceptions or not. For all interaction with the UI elements from other threads, use synchronization: Control.Invoke
or Control.BeginInvoke
, SynchronizationContext
, TaskScheduler
etc.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.