1

我正在开发一个多线程 TDI UI(如果您有兴趣,可以使用 C1 DockingTabs)。到目前为止,我已经设法让每个窗口在单独的线程中打开,并使用 SetParent Win32 API 将其放入相应的选项卡中。我还设法让模态对话框也显示在选项卡中,并且不会阻止其他选项卡工作(通过在对话框表单上的 Shown 事件上添加处理程序以再次调用 SetParent - 一些涉及打开和关闭的摆弄TopLevel 在选项卡内的表单上,但它有效)。

现在,有点烦人的是对话框正在打开,它从 TDI 父窗体中移除焦点,然后焦点立即被放回。如果我在显示之前调用 SetParent,我只会得到一个异常,因为在具有父级的表单上不能有模式对话框。我已经设法绕过窗口动画滑动/淡入淡出,方法是给它一个 0,0 的大小,直到它在选项卡内,但我不知道如何停止焦点闪烁并重新回到主要父窗体。

我想有两种可能的方法:

  1. 禁用窗口效果,使其看起来失去焦点(可能阻止窗口消息?)
  2. 实际上真的阻止它失去焦点

我很欣赏这是一个不寻常的查询,非常高兴得到任何帮助!


编辑:

为了澄清练习中的要点 - 我有一个基于选项卡的 UI,其中每个选项卡实际上是独立的。我收到最终用户的投诉,每次调用 ShowDialog 时,它都会阻止整个应用程序,而不仅仅是一个选项卡。我能看到解决这个问题的唯一方法(缺少像 Google Chrome 这样的多进程)是给每个选项卡一个单独的 UI 线程并在选项卡内加载对话框,以便用户仍然可以访问其他选项卡。我已经设法在某种程度上消除了一些 hackiness 并且现在解决了大部分问题(只是玩了更多)。实际上,我已经设法通过阻止主窗体上的 WM_NCACTIVATE 消息来解决我提出的问题,尽管这有点混乱,因为现在它从未显示为已停用。我想我 必须检测激活的表单是否是该表单的子对话框,以决定是否激活。我也有一些闪烁要尝试解决,但它看起来好多了。我会发布代码,但是涉及 3 种表单,因此上传项目太短了,会有点混乱。如果有人好奇,我会看看我是否可以减少它?

我目前只是在使用它作为概念证明——如果我让它工作,那么我需要将它改装到我现有的应用程序中,这才是真正有趣的开始!我有一个控制 TDI 方面的框架,所以从这方面来说它应该是相当简单的。真正的噩梦将是审计整个事情以解决不同线程之间可能存在的同步问题,因为有一些共享资源本质上不是线程安全的。

4

2 回答 2

0

谢谢你的笑声。听起来很令人印象深刻。你可能最终会后悔所有这些黑客行为的后果,但我并不认为你无所畏惧地尝试完成这项工作:)我假设你有充分的理由这样做,或者你是改变它的路太远了,但它确实让我感到奇怪,你会经历这么多麻烦来强制 UI 成为多线程而不是仅仅将业务逻辑包装到一些异步方法中。如果所有业务逻辑都是异步的,那么所有表单都可以在一个线程中。但是,尽管如此,我唯一的建议是添加到您所说的内容中,即询问使用 Show 而不是 ShowDialog 显示表单是否会导致相同的问题。如果Show没有问题,您可以实现自己的 ShowDialog 同步方法,该方法会阻塞线程,直到用户关闭表单。在方法内部,它可以显示自己,然后在表单可见时处于循环状态,执行睡眠和执行事件,我认为这会产生相同的影响。不过,这让我有点畏缩。

于 2012-05-03T13:41:34.170 回答
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
于 2012-05-04T14:00:14.410 回答