如果在外面点击关闭面板

时间:2014-05-24 03:05:38

标签: vb.net user-controls

我制作了一个自定义的ComboBox,当用户点击外部时,我很难让它的下拉菜单(Panel)关闭。正如大家可能都知道的那样,使用LostFocus事件是不够的,因为当用户点击例如:scroolbar或表单本身时,控件不会失去焦点。我尝试使用IMessageFilter,但我不认为我理解它是如何工作的。我还尝试使用Capture属性,强制下拉列表捕获鼠标,但这也强制鼠标单击在下拉列表(Panel)本身上发生,这意味着如果用户点击了下拉列表中的项目,点击不会起作用。

让我澄清一下:

我正在创建一个自定义控件,一个UserControl,它被编译为.DLL,并且可以从工具箱中拖放到表单上,就像任何其他Winforms控件一样。 该控件是Combobox。 我想让用户在其外部点击时关闭Combobox的下拉列表(这是一个在运行时创建的面板),就像普通的ComboBox一样。 问题是什么?检测面板外部的点击,然后关闭它。

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

- >使用Capture属性:到目前为止已经取得了最好的结果。但仍有一些问题。当Capture设置为True时,鼠标确实被控件捕获,该控件将该属性设置为True。因此,应用程序上的任何其他控件都不会感测到鼠标活动,而只会捕获捕获鼠标的控件。它只会在用户执行单击时释放鼠标。这会导致一些问题:

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

有一种方法可以解决第一个问题:您可以使用Control.MousePosition找到鼠标指向哪个按钮,然后强行单击它。 虽然我还没有找到解决第二和第三期问题的方法。我想到的一个可能的解决方案是当鼠标进入时将下拉列表的Capture属性设置为False。所以,这就是它的工作原理:

  • 用户点击Combobox。下拉列表打开,捕获鼠标;
  • 用户然后将鼠标移动到Combobox的下拉列表中。 Capture设置为false,从而使用户可以单击下拉列表或下拉列表中的任何按钮,依此类推。当用户将鼠标移到它们上方时,下拉列表中的按钮也会正确突出显示;
  • 用户将鼠标移动到Combobox的下拉列表中,Capture再次设置为true,现在用户在其外部执行的任何点击将被忽略",并且下拉列表将被关闭。就像普通的Combobox一样。

但是当我尝试这样做时,又出现了另一个问题:当控件将其Capture属性设置为True时,它会不断触发MouseEnter个事件。 MouseEnter事件未使用真正的鼠标指针位置。即使鼠标指针实际位于Control之外,如果其Capture属性设置为True,该控件也会认为鼠标实际位于其中。

1 个答案:

答案 0 :(得分:1)

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有效。

另外,这里有一些有趣的技巧:Handling a click event anywhere inside a panel in C#

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

和NativeWindow类的页面:http://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow.aspx