停止ShowDialog从父窗体中删除焦点

时间:2012-05-03 13:13:51

标签: .net winforms focus showdialog

我正在研究多线程TDI UI(如果您感兴趣,请使用C1 DockingTabs)。到目前为止,我已经设法让每个窗口在一个单独的线程中打开,并使用SetParent Win32 API将其放在适当的选项卡中。我还设法在选项卡中显示模态对话框,并且不阻止其他选项卡工作(通过在对话框窗体上的Shown事件上添加处理程序再次调用SetParent - 一些涉及打开和关闭的小提琴选项卡中的表单上的TopLevel,但它的工作原理)。

现在,发生的事情有点令人讨厌的是对话框正在打开,这会从TDI父窗体中移除焦点,然后立即放回焦点。如果我在显示之前调用SetParent,我只是得到一个异常,因为你不能在具有父级的表单上有一个模式对话框。我已经设法绕过窗口动画幻灯片/淡入淡出,给它大小为0,0直到它在选项卡内,但我无法弄清楚如何停止焦点轻弹和重新打开主要的父母形式。

我想有两种可能的方法:

  1. 禁用窗口效果,使其看起来像是丢失了焦点(阻止窗口消息可能?)
  2. 实际上真的让它失去了焦点
  3. 我很欣赏这是一个不寻常的查询,所以非常高兴有任何帮助!


    编辑:

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

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

2 个答案:

答案 0 :(得分:0)

谢谢你的笑声。听起来令人印象深刻。你最终可能会对所有这些黑客行为造成的后果感到后悔,但我并没有因为无所畏惧的努力让你失去信心:)我假设你有充分的理由这样做,或者你是在改变它的过程中走得太远,但它确实让我感到奇怪的是你要经历如此多的麻烦迫使UI变成多线程而不是仅仅将业务逻辑包装成一些异步方法。如果所有业务逻辑都是异步的,则所有表单都可以在一个线程中。但是,尽管如此,我唯一的建议是添加到你所说的内容,如果用Show而不是ShowDialog显示表单会导致同样的问题。如果Show没有问题,您可以实现自己的ShowDialog同步方法来阻止线程,直到用户关闭表单。在方法内部,它可以展示自己,然后坐在一个循环,而形式是可见的做睡眠和doevents,我认为这将具有相同的影响。尽管如此,这让我有点畏缩。

答案 1 :(得分: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