繁体   English   中英

如果在外部单击,则关闭面板

[英]Close panel if clicked outside

我正在制作一个自定义的ComboBox,当用户单击外部时,我正在努力使其下拉菜单(面板)关闭。 众所周知,使用LostFocus事件是不够的,因为当用户单击例如scroolbar或表单本身时,控件不会失去焦点。 我尝试使用IMessageFilter ,但是我认为我不了解它是如何工作的。 我还尝试使用Capture属性,强制下拉菜单捕获鼠标,但这也迫使鼠标在下拉菜单(Panel)本身上发生单击,这意味着如果用户单击了下拉菜单中的项目,则单击会获胜不行

让我澄清一下:

我正在制作一个自定义控件,即UserControl,它已编译为.DLL,并且可以像其他Winforms控件一样从Toolbox拖放到窗体上。 该控件是一个组合框。 我想使Combobox的下拉菜单(这是一个在运行时创建的面板)在用户单击它时关闭,就像普通的ComboBox一样。 那是什么问题呢? 检测面板外部的点击,然后将其关闭。

->使用LostFocus事件:不够。 如果用户单击表单上的空白,则不会触发LostFocus 我知道我可以在表单上放置一个LostFocus事件并将ActiveContrl设置为Nothing ,但这将要求我自定义ComboBox的用户(开发人员)在使用控件的每种表单上放置相同的代码。

->使用Capture属性:到目前为止,已获得最佳结果。 但是仍然存在一些问题。 Capture设置为True ,将属性设置为True的控件确实捕获了鼠标。 因此,应用程序上的任何其他控件都不会检测到鼠标活动,只有捕获了鼠标的控件才能检测到。 仅在用户单击时释放鼠标。 这会导致一些问题:

  • 如果用户单击下拉菜单上的项目( Button ),并且下拉菜单( Panel )的Capture属性设置为true,则单击将被“忽略”,因为它将由下拉菜单处理,而不是在Button上,用户其实是想点击。
  • 如果用户单击下拉列表的scroolbar,则该单击也将被“忽略”,就像上面解释的那样。
  • 当用户将鼠标移动到下拉菜单(ComboBox的项目)内的Button上方时,它们不会突出显示,因为没有为它们触发MouseEnter ,因为鼠标被下拉菜单捕获了。

有一种方法可以解决第一个问题:使用Control.MousePosition可以找到鼠标指向哪个按钮,然后强制单击它。 但是,我还没有找到解决第二个和第三个问题的方法。 我想到的一种可能的解决方案是,当鼠标进入下拉菜单时将其Capture属性设置为False。 因此,这是它的工作方式:

  • 用户单击组合框。 下拉列表打开,捕获鼠标;
  • 然后,用户将鼠标移到Combobox的下拉列表中。 将Capture设置为false,从而使用户可以单击下拉菜单中的任何按钮或下拉菜单的scroolbar等。 当用户将鼠标移到下拉菜单上方时,下拉菜单中的按钮也会正确突出显示。
  • 用户将鼠标移到Combobox的下拉列表之外,Capture再次设置为true,现在用户在其外部进行的任何单击都将被“忽略”,并且该下拉列表将被关闭。 就像普通的组合框一样。

但是,当我尝试执行此操作时,又出现了另一个问题:当控件的Capture属性设置为True时,它将不断触发MouseEnter事件。 MouseEnter事件不使用真实的鼠标指针位置。 即使鼠标指针实际上位于控件外部,但如果其Capture属性设置为True,则该控件也会认为鼠标实际上位于控件内部。

Edit2:这是用于处理某些不同类型事件的代码(现在应适用于所有情况)。

Public Class Form1

    Private Shared mouseNotify() As Int32 = {&H201, &H204, &H207} ' WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN
    Private Shared scrollNotify() As Int32 = {&H114, &H115} ' WM_HSCROLL, WM_VSCROLL
    Private Shared scrollCommands() As Int32 = {0, 1, 2, 3, 4, 5} ' SB_LINEUP, SB_LINEDOWN, SB_PAGEUP, SB_PAGEDOWN, SB_THUMBTRACK, SB_THUMBPOSITION

    Private Sub baseLoad(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
        AutoScroll = True
        Controls.Add(myPanel)
        Controls.Add(myTextBox4)
        myPanel.myTextBox1.Focus()
    End Sub

    Private myTextBox4 As New customTextBox(300)
    Private myPanel As New customPanel

    Protected Overrides Sub OnScroll(ByVal se As ScrollEventArgs)
        MyBase.OnScroll(se)
        ActiveControl = Nothing
    End Sub

    Protected Overrides Sub OnMouseWheel(ByVal e As MouseEventArgs)
        MyBase.OnMouseWheel(e)
        ActiveControl = Nothing
    End Sub

    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        MyBase.OnResize(e)
        ActiveControl = Nothing
    End Sub

    Protected Overrides Sub OnMove(ByVal e As System.EventArgs)
        MyBase.OnMove(e)
        ActiveControl = Nothing
    End Sub

    Friend Shared Function isOverControl(ByRef theControl As Control) As Boolean
        Return theControl.ClientRectangle.Contains(theControl.PointToClient(Cursor.Position))
    End Function

    Protected Overrides Sub WndProc(ByRef m As Message)
        If mouseNotify.Contains(CInt(m.Msg)) Then
            If Not isOverControl(myPanel) Then
                ActiveControl = Nothing
            Else
                myPanel.myTextBox1.Focus()
            End If
        End If
        MyBase.WndProc(m)
    End Sub

    Friend Class customPanel : Inherits Panel

        Friend myTextBox1 As New customTextBox(20)
        Private myTextBox2 As New customTextBox(60)
        Private myTextBox3 As New customTextBox(200)

        Friend Sub New()
            AutoScroll = True
            Location = New Point(0, 100)
            Size = New Size(200, 100)
            Controls.Add(myTextBox1)
            Controls.Add(myTextBox2)
            Controls.Add(myTextBox3)
        End Sub

        Protected Overrides Sub OnLeave(ByVal e As EventArgs)
            MyBase.OnLeave(e)
            myTextBox1.Text = "false"
            myTextBox2.Text = "false"
            BackColor = Color.Green
        End Sub

        Protected Overrides Sub OnEnter(ByVal e As EventArgs)
            myTextBox1.Text = "true"
            myTextBox2.Text = "true"
            BackColor = Color.Gold
            MyBase.OnEnter(e)
        End Sub

        Protected Overrides Sub WndProc(ByRef m As Message)
            If mouseNotify.Contains(CInt(m.Msg)) Then
                If isOverControl(Me) Then Form1.WndProc(m)
            End If
            MyBase.WndProc(m)
        End Sub

    End Class

    Friend Class customTextBox : Inherits TextBox

        Friend Sub New(ByVal y As Integer)
            Location = New Point(10, y)
            Size = New Size(100, 30)
        End Sub

        Protected Overrides Sub OnLeave(ByVal e As EventArgs)
            MyBase.OnLeave(e)
            BackColor = Color.Blue
        End Sub

        Protected Overrides Sub OnEnter(ByVal e As EventArgs)
            BackColor = Color.Red
            MyBase.OnEnter(e)
        End Sub

    End Class

End Class

如果在所有情况下都无法正常运行,则可能必须将事件附加到窗体上所有无法/不会关注鼠标事件的控件上。

同样,这取决于控件的类型而略有不同。 richtextbox例如使用OnHScroll和OnVscroll,而不是OnScroll。 您还可以使用CInt(m.WParam.ToInt32 >> 16)获得拇指位置,仅对SB_THUMBTRACK,SB_THUMBPOSITION有效。

另外,这里还有一些有趣的技术: 在C#面板内的任何地方处理click事件

这实际上是从WndProc的MSDN页面上获取的: http : //msdn.microsoft.com/en-us/library/system.windows.forms.control.wndproc%28v=vs.110%29.aspx

以及NativeWindow类的页面: http : //msdn.microsoft.com/zh-cn/library/system.windows.forms.nativewindow.aspx

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM