2012-05-03 60 views
1

我正在開發多線程TDI UI(如果感興趣,請使用C1 DockingTabs)。到目前爲止,我已經設法讓每個窗口在單獨的線程中打開,並使用SetParent Win32 API將其放入適當的選項卡中。我還設法讓模式對話框在標籤中顯示,並且不阻止其他標籤的工作(通過在對話框窗體上的Shown事件中添加一個處理程序來再次調用SetParent - 一些涉及打開和關閉的小工具TopLevel在選項卡內的表單上,但它起作用)。停止ShowDialog從父窗體中移除焦點

現在,發生什麼有點討厭的是,對話框正在打開,從TDI父窗體移除焦點,然後焦點立即被放回。如果我在顯示它之前調用SetParent,那麼我只是得到一個異常,因爲在具有父級的表單上不能有模態對話框。我已經設法繞過窗口動畫幻燈片/淡入和淡出,通過給它一個0,0的大小,直到它在標籤內,但我無法制定出如何停止焦點輕彈和回到主要父母形式。

我想有兩種可能的途徑:(?也許阻塞窗口消息)

  1. 禁用窗口作用,這使它看起來像它已失去焦點
  2. 居然真的停止它失去焦點

我明白這是一個不尋常的查詢,所以真的很高興任何幫助!


編輯:

要澄清這一點在鍛鍊 - 我有一個標籤基於UI其中每個選項卡實際上是獨立的。每次有人調用ShowDialog時,我都有來自最終用戶的抱怨,它阻止了整個應用程序,而不是僅僅一個選項卡。我可以看到解決這個問題的唯一方法就是爲每個選項卡分配一個UI線程,並在選項卡中加載對話框,以便用戶仍然可以訪問其他選項卡。我已經設法在一定程度上消除了一些黑客行爲,並且現在解決了大部分問題(只是玩了更多)。實際上,我已經設法通過在主窗體上阻止WM_NCACTIVATE消息來解決問題,雖然這有點麻煩,因爲現在它從不顯示爲已停用。我想我必須檢測激活的表單是否是此對話框的子對象,以決定是否激活。我也試圖解決一些閃爍問題,但它看起來好多了。我會張貼代碼,但有3種形式涉及到這麼短的上傳項目,它會有點混亂。如果有人好奇,我會看看我能否減少它?

我現在正在玩它作爲概念證明 - 如果我得到這個工作,那麼我需要改造它到我現有的應用程序,這是真正的樂趣開始的地方!儘管我有一個控制TDI方面的框架,所以從這方面來看,它應該是相當直接的。真正的噩夢是審計整個事情,以找出跨不同線程的可能同步問題,因爲有一些共享資源本質上不是線程安全的。

+0

如果你不真的需要'ShowDialog'的行爲,就好像不使用'ShowDialog'會更簡單。你能否不把對話框顯示爲非模態,並禁用父窗體,從對話框的關閉事件中重新啓用它?如果你這樣做,你也不需要從一個單獨的線程打開每個窗口。 – hvd

+0

這種行爲沒有任何意義,發佈重現問題的代碼片段。 –

+0

@HansPassant它對我來說非常有意義。如果表單A包含表單B,表單B具有焦點,則將表單B從表單A中移除並放回,表單A暫時未聚焦,然後重新聚焦。而我所建議的意思是表格B不需要首先從表格A中刪除。 – hvd

回答

0
Friend NotInheritable Class Win32 
    Public Const WM_NCACTIVATE = &H86 
    Public Const WM_SDLG_ACTIVATE = &H8000 

    <DllImport("user32.dll")> Public Shared Function GetForegroundWindow() As IntPtr 
    End Function 

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _ 
    Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr 
    End Function 
End Class 

要停止的主要形式出現失去焦點時,在對話框中 - 在主窗體: -

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) 

    Select Case m.Msg 
     Case Win32.WM_NCACTIVATE 
      Dim m2 As New System.Windows.Forms.Message() 
      m2.HWnd = m.HWnd 
      m2.Msg = m.Msg 
      m2.LParam = m.LParam 

      Dim fgwh = Win32.GetForegroundWindow() 

      m2.WParam = If(fgwh = Handle, 1, 0) 'title bar state - TRUE for active 
      m.Result = 1 'TRUE to do default processing, FALSE to block 
      MyBase.WndProc(m2) 
      Exit Sub 

     Case Win32.WM_SDLG_ACTIVATE 
      Dim m2 As New System.Windows.Forms.Message() 
      m2.HWnd = m.HWnd 
      m2.Msg = Win32.WM_NCACTIVATE 
      m2.LParam = m.LParam 

      Dim fgwh = Win32.GetForegroundWindow() 

      If m.WParam = 0 Then 
       m2.WParam = If(fgwh = Handle, 1, 0) 'title bar state - TRUE for active 
      Else 
       m2.WParam = 1 
      End If 

      m.Result = 1 'TRUE to do default processing, FALSE to block 
      MyBase.WndProc(m2) 
      Exit Sub 


    End Select 

    MyBase.WndProc(m) 
End Sub 

而在子形式: -

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message) 
    If m.Msg = Win32.WM_NCACTIVATE Then 

     Dim fgwh = Win32.GetForegroundWindow() 
     Dim wParam As IntPtr 
     If fgwh = Me.Handle OrElse fgwh = MainForm.Instance.Handle Then 
      wParam = 1 
     Else 
      wParam = 0 
     End If 

     Win32.SendMessage(MainForm.Instance.Handle, Win32.WM_SDLG_ACTIVATE, wParam, m.LParam) 
    End If 

    MyBase.WndProc(m) 
End Sub 

要停止主窗體被停用並獲取,請在對話窗體類中獲得: -

Protected Overrides ReadOnly Property ShowWithoutActivation As Boolean 
    Get 
     Return True 
    End Get 
End Property 
0

謝謝你的笑。這聽起來很不錯。你可能最終會後悔所有這些黑客行爲的後果,但我並沒有在上面給你無畏的嘗試做這項工作的榮譽:)我假設你有這樣做的好理由,或者你是在改變它的道路上還有很長的路要走,但這確實讓我覺得奇怪,你會經歷很多麻煩才能迫使UI成爲多線程,而不是僅僅將業務邏輯封裝到某些異步方法中。如果所有業務邏輯都是異步的,則所有表單都可以在一個線程中。然而,儘管如此,我唯一的建議就是添加你所說的內容,然後詢問是否用Show而不是ShowDialog顯示窗體會導致同樣的問題。如果Show沒有問題,則可以實現自己的ShowDialog同步方法,該方法在用戶關閉表單之前阻塞該線程。在方法內部,它可以顯示自己,然後坐在一個循環中,同時表單可以看到做一個睡眠和一個doevents,我認爲這會有相同的影響。不過,這讓我感到有點不高興。

+0

問題的一部分是我已經有相當多的業務邏輯,我真的找到它不得不分裂它。另外,我希望這個框架可以重用於其他應用程序。 TDI現在是網頁瀏覽器的標準接口,所以我認爲它對用戶來說更自然 – wizzardmr42

+0

順便說一句,我一直是我一生中約86%的編碼器,所以我很清楚我得到的是什麼進! – wizzardmr42

+0

是的,我印象深刻。這不是一個新手甚至會嘗試的東西。它讓我想起了幾年前在VB6中工作的一個項目,在那裏我創建了一個WindowPane控件,該控件基本上是一個容納vb6表單的容器控件。它沒有多線程問題,但我有焦點和活動標題欄等非常類似的問題,最終我得到它工作得很好...... :) –