簡體   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