2010-06-15 73 views
9

我遇到了Windows窗體應用程序的問題。Windows窗體中我的跨線程調用有什麼問題?

表單必須從另一個線程顯示。因此,在窗體類,我有以下代碼:

private delegate void DisplayDialogCallback(); 

public void DisplayDialog() 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new DisplayDialogCallback(DisplayDialog)); 
    } 
    else 
    { 
     this.ShowDialog(); 
    } 
} 

現在,我每次運行這個時候,一個InvalidOperationException被拋出就行this.ShowDialog();

「跨線程操作無效:控制'SampleForm'從一個線程訪問,而不是它創建的線程。「

這段代碼有什麼問題?這不是一個有效的方式來進行跨線程調用嗎? ShowDialog()有什麼特別的嗎?

+3

出於好奇另一種方法,是什麼IsHandleCreated節目? – 2010-06-15 14:48:18

+0

@Marc Gravell:IsHandleCreated爲false。所以自然而然地,正如幾個人所說,代碼在表單顯示之前執行。 – 2010-06-15 16:19:13

回答

4

試試這個:

private delegate void DisplayDialogCallback(); 

public void DisplayDialog() 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new DisplayDialogCallback(DisplayDialog)); 
    } 
    else 
    { 
     if (this.Handle != (IntPtr)0) // you can also use: this.IsHandleCreated 
     { 
      this.ShowDialog(); 

      if (this.CanFocus) 
      { 
       this.Focus(); 
      } 
     } 
     else 
     { 
      // Handle the error 
     } 
    } 
} 

請注意,InvokeRequired回報

如果真控件的句柄 創建在不同於 調用線程(indi說明你必須通過調用方法 調用控件);否則,是錯誤的。

因此,如果控件尚未創建,返回值將是false

8

您可能在表單顯示之前執行此代碼。
因此,InvokeRequired正在返回false

5

我相信這裏發生的是這個代碼在Form被顯示之前運行。

當在.Net中創建Form時,它不會立即獲得特定線程的關聯。只有當某些操作像展示或抓取手柄一樣進行時,纔會獲得親和力。在此之前,InvokeRequired難以正常運行。

在這種特殊情況下,沒有建立親和關係,也不存在父控制,所以InvokeRequired由於無法確定原始線程而返回false。

解決此問題的方法是在您的控件在UI線程上創建時建立關聯。做到這一點的最好方法就是要求控件處理屬性。

var notUsed = control.Handle; 
+0

奇怪,但獲得'control.Handle'不會改變任何東西:異常仍然被拋出。相反,洛倫佐的建議效果很好。恕我直言,這是由於編譯器剛剛刪除了'var notUsed = control.Handle;'行,而'notUsed'變量不被使用。 – 2010-06-15 15:41:47

0

您可以隨時嘗試使用不同的控件進行測試。

例如,您可以訪問Application.Forms收藏

public Control GetControlToInvokeAgainst() 
{ 
    if(Application.Forms.Count > 0) 
    { 
     return Application.Forms[0]; 
    } 
    return null; 
} 
在DisplayDialog()方法

然後,試圖執行一個invokerequired調用之前調用GetControlToInvokeAgainst()和測試空。

0

最有可能的控制手柄尚未創建,在這種情況下Control.InvokeRequired返回false

檢查Control.IsHandleCreated屬性以查看是否屬於這種情況。

0

我也認爲SLak是正確的。從msdn(http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx):

如果找不到合適的句柄,則InvokeRequired方法返回false。

如果在你的情況下可能,我會嘗試將創建和顯示控制結合在一個單一的方法,即:

public DisplayDialog static Show() 
{ 
    var result = new DisplayDialog; //possibly cache instance of the dialog if needed, but this could be tricky 
    result.ShowDialog(); 
    return result; 
} 

你可以調用顯示從不同的線程

1

在表單顯示之前,您可能會看到此代碼,因此尚未創建窗口句柄。

您可以將代碼之前添加此代碼和所有應該不錯:

if (! this.IsHandleCreated) 
    this.CreateHandle(); 

編輯:有你的代碼的另一個問題。一旦顯示錶單,就不能再次調用ShowDialog()。你會得到一個無效的操作異常。您可能想要像其他人所提議的那樣修改此方法。

你可能會得到更好的服務直接調用類調用的ShowDialog(),並有BringToFront()或類似的東西...