繁体   English   中英

来自非UI线程的TabControl对UI调用的处理不一致

[英]Inconsistent handling of UI calls from non-UI thread for TabControl

这不是重复项。 WinForms TabControl的这种特殊行为尚未在StackOverflow上进行探讨。

查看以下示例:

我在.NET 4.0中有一个带有两个选项卡的TabControl 每个选项卡中都有一个标签 当我单击Button时 ,我启动了一个BackgroundWorker ,它现在正在非UI线程上运行。 如果我尝试从tabPage1更改Label ,则由于跨线程调用而收到InvalidOperationException 但是修改tabPage2上的Label的第二行运行得很好,没有例外。

简单的测试表格来演示问题

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;
    }
}

为什么在tabPage2上允许这样做,但在tabPage1上不允许这样做。 在这两种情况下,我们似乎都在从非UI线程修改UI。

这是 Control.Text属性的设置器:

set
{
    // some code omitted

    this.WindowText = value;
    this.OnTextChanged(EventArgs.Empty);

    // some code omitted
}

它只是转发到Control.WindowText属性。 让我们检查一下:

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;
            }
        }
    }
}

InvalidOperationException源自Handle属性的getter,该方法被调用以获得本机方法调用的HandleRef实例。

get {
    if (checkForIllegalCrossThreadCalls &&
        !inCrossThreadSafeCall &&
        InvokeRequired) {
        throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall, Name));
    }

    // further code omitted
}

仅当IsHandleCreatedtrue时,才可以访问Handle属性。 在您的情况下,第二个标签位于尚未显示子项的选项卡项目上,因此第二个标签的IsHandleCreatedfalse 这意味着,第二个标签的Handle属性不能在BackgroundWorker的线程上访问。 而是将文本值仅缓存在控件内的text字段中。 因此-也不例外。

激活第二个选项卡项目时,将创建Label的句柄,.NET Framework代码从text字段中获取缓存的文本值,并将其应用于标签。 这发生在UI线程上,因此再次出现-那里没有异常。

您可以切换到第二个选项卡项目, 然后按一下按钮。 您将观察到异常。 这是因为在这种情况下,第二个标签的句柄将已经创建。

作为一般的注意-你永远不应该从工作线程访问UI元素,无论观察那些异常与否。 对于与其他线程与UI元素的所有交互,请使用同步: Control.InvokeControl.BeginInvokeSynchronizationContextTaskScheduler等。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM