65.9K
CodeProject 正在变化。 阅读更多。
Home

DropDownPanel - 带有图形和图钉的自定义下拉面板 (VB.NET)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (15投票s)

2008 年 10 月 1 日

CPOL

4分钟阅读

viewsIcon

71882

downloadIcon

2670

一个类似 ComboBox 的下拉控件,可以在设计时将其他控件放置并排列在其上。

DropDownPanel

引言

DropDownPanel 是一个继承自 Panel 的用户控件。我想要一个类似 ComboBox 的下拉控件,以便可以在设计时将其他控件放置并排列在其上。我还希望下拉部分能够与头部部分具有不同的尺寸。

标题栏 (Header)

  • 图钉 - 将面板锁定在打开状态,使其在失去焦点时不会关闭。
  • 下拉按钮 - 打开和关闭面板。
  • 文本框 - 显示字符串信息。
  • 图形 - 在文本框旁边显示一个小图像。

下拉面板

  • PanelSize 属性定义的、用于容纳子控件的区域。

事件

  • Public Event DropDown(ByVal sender As Object, ByVal IsOpen As Boolean)
  • 当点击下拉按钮时,将触发此事件。

  • Public Event TextBoxChanged(ByVal sender As Object)
  • 当文本框中的文本更改时,将触发此事件。

控件设计器方法

  • SizePanelToControl
  • 将面板区域的大小调整为适合控件的当前边界。

  • SizeToChildControls
  • 将面板区域的大小调整为适合子控件,并带有 AutoControlMargin 的边距。

如何使用它

将控件拖到窗体上,并将头部区域的大小调整为所需的宽度。对于面板区域,打开它并使用 PanelSize 属性调整其大小,或者使用上面的设计器方法之一。将您想要的控件拖到上面,并调整外观属性以达到您想要的外观。就是这么简单。

代码 #区域

初始化

由于 Panel 没有 Load 事件,我将加载代码放在 HandleCreated 事件中,这得到了相同的结果。

Private Sub DropDownPanel_HandleCreated(ByVal sender As Object, _
      ByVal e As System.EventArgs) Handles Me.HandleCreated
    'I put this here because there is no Load Event for a Panel
    If Not Me.DesignMode Then

        Select Case Me.StartUpState
            Case eStartUpState.Closed
                Me.CloseDropDown()

            Case eStartUpState.Open
                Me.OpenDropDown()
        End Select

    ResizeMe()
    End If
End Sub

如果您不想使用上述事件,请删除它,并将以下代码添加到每个父窗体中

Private Sub Form1_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles MyBase.Load
    CloseDropdowns(Me.Controls)
End Sub

'Recursive Sub to Close all Dropdowns
Sub CloseDropdowns(ByVal cc As System.Windows.Forms.Control.ControlCollection)
    For Each c As Control In cc
        If c.GetType Is GetType(DropDownPanel.DropDownPanel) Then
            Dim ddp As DropDownPanel.DropDownPanel = c
            If ddp.StartUpState = _
              DropDownPanel.DropDownPanel.eStartUpState.Closed Then
                ddp.CloseDropDown()
            Else
                ddp.OpenDropDown()
            End If
        End If
        If c.HasChildren Then CloseDropdowns(c.Controls)
    Next
End Sub

控件属性

以下是主要属性列表

  • StartUpState
  • DropDownPanel 加载时是打开还是关闭。

  • PanelSize
  • 获取或设置 DropDownPanel 的大小。

  • PanelGradientType, PanelBackColorA, PanelBackColorB
  • 用于填充面板的颜色和混合类型。

  • PanelBorderColor
  • 获取或设置 DropDownPanel 的边框颜色。

  • AutoControlMargin
  • 获取或设置控件的右侧和底部边距。

  • PanelCornerRadius, TextBoxCornerRadius
  • 获取或设置圆角半径。

  • TextBoxGradientType, TextBoxBackColorA, TextBoxBackColorB
  • 用于填充文本框的颜色和混合类型。

  • TextBoxBorderColor
  • 获取或设置文本框的边框颜色。

  • ButtonShape
  • 获取或设置下拉按钮的形状。

  • ShowPushPin
  • 获取或设置图钉是否可见。

  • GraphicBorderColor
  • 获取或设置图形周围的边框颜色。

  • GraphicImage
  • 获取或设置文本框旁边的图像。

  • GraphicWidth
  • 获取或设置图形图像的宽度。

  • GraphicAutoWidth
  • 获取或设置根据图像纵横比自动调整宽度。

鼠标事件

跟踪光标是否在按钮或图钉等区域上方。

Private Function IsMouseOverArea(ByVal X As Integer, _
      ByVal Y As Integer, ByVal GP As GraphicsPath) As Boolean
    'Convert to Region.
    Using Area As New Region(GP)
        'Is the point inside the region.
        Return Area.IsVisible(X, Y)
    End Using
End Function

绘制

包含绘制控件所有部分的例程。面板中位于头部区域的任何子控件都将被自动向下移动。要覆盖此行为,请在子控件的 Tag 属性中添加字符串 "IgnoreMe",它将被忽略。

Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    'Draw the Graphic if available and resize
    If _GraphicImage IsNot Nothing Then
        Dim GW As Integer = IIf(_GraphicAutoWidth, 20 * _
            (_GraphicImage.Width / _GraphicImage.Height), _GraphicWidth)
        e.Graphics.DrawImage(_GraphicImage, 0, 0, GW, 20)
        e.Graphics.DrawRectangle(New Pen(_GraphicBorderColor), 0, 0, GW, 19)
    End If

    'Draw the Text Box
    If rectTextBox.Width > _TextBoxCornerRadius * 2 Then

        If Me._TextBoxGradientType = eGradientType.Solid Then
            e.Graphics.FillPath(New SolidBrush(_TextBoxBackColorA), _
                GetRectPath(rectTextBox, TextBoxCornerRadius))
        Else
            Using lgbr As LinearGradientBrush = New LinearGradientBrush _
              (rectTextBox, _TextBoxBackColorA, _TextBoxBackColorB, _
              CType([Enum].Parse(GetType(LinearGradientMode), _
              _TextBoxGradientType.ToString), LinearGradientMode))

                e.Graphics.FillPath(lgbr, GetRectPath(rectTextBox, _
                    TextBoxCornerRadius))

            End Using
        End If

        e.Graphics.DrawPath(New Pen(_TextBoxBorderColor), _
            GetRectPath(rectTextBox, TextBoxCornerRadius))

        'Draw the Text if Available
        If _Text <> "" Then

            Using sf As StringFormat = New StringFormat
                sf.Alignment = StringAlignment.Center
                sf.LineAlignment = StringAlignment.Center
                e.Graphics.DrawString(_Text, Me.Font, _
                    New SolidBrush(Me.ForeColor), rectTextBox, sf)
            End Using

        End If
    End If

    'If the Panel is Open Paint the Panel Box
    If IsOpen Then
        Dim rect As Rectangle = New Rectangle(0, 21, _
            Me.PanelSize.Width - 1, Me.PanelSize.Height - 1)
        If Me._PanelGradientType = eGradientType.Solid Then
            e.Graphics.FillPath(New SolidBrush(_PanelBackColorA), _
                GetRectPath(rect, PanelCornerRadius))
        Else
            Using lgbr As LinearGradientBrush = New LinearGradientBrush _
              (rect, _PanelBackColorA, _PanelBackColorB, _
              CType([Enum].Parse(GetType(LinearGradientMode), _
              _PanelGradientType.ToString), LinearGradientMode))

                e.Graphics.FillPath(lgbr, GetRectPath(rect, _
                    PanelCornerRadius))

            End Using
        End If

        e.Graphics.DrawPath(New Pen(_PanelBorderColor), _
            GetRectPath(rect, PanelCornerRadius))
    End If

    DrawDropDownButton(e.Graphics)

    If _ShowPushPin Then e.Graphics.DrawImage(bmpPushPin, rectPushPin)

    'Adjust any miss placed control positioned under the Header
    For Each c As Control In Me.Controls
        If c.Location.Y < 21 Then
            If c.Tag IsNot "IgnoreMe" Then _ 
                c.Location = New Point(c.Location.X, 21)
        End If
    Next
End Sub

方法

Private

  • UpdateTextArea - 设置头部显示的内容。
  • UpdateRegion - 设置头部和面板区域,以显示通过镂空区域的控件后面的内容。
  • ResizeMe - 调整控件大小以适应当前状态。

Public

  • OpenDropDown - 打开面板。
  • CloseDropDown - 仅当图钉向上时关闭面板。
  • ForceCloseDropDown - 向上翻转图钉并关闭面板。

控制事件

包含 DropDownPanel 的事件。

DropDownPanelDesigner

创建智能标签的控件设计器。有关设计器的操作方法,请访问:UITypeEditorsDemo[^]。

结束区域

锚定和调整大小

这曾经令人头疼,但最终我找到了一个方法。根据设计,如果面板打开或关闭,调整大小的处理方式是不同的。如果面板关闭,则 HeaderWidth 会随控件的宽度调整,但如果面板打开,则 HeaderWidth 不会改变。这在 Sub ResizeMe 中处理。其中有一个变量 blnIsResizeOK,它被设置为 False,以在所有手动调整大小完成后(例如设置 IsOpen 属性)绕过 Resize 事件中的此例程,然后将 blnIsResizeOK 设置回 True 并手动调用 ResizeMe 以正确调整大小。直到我尝试将左右两侧锚定下来,使控件随窗体一起增长和收缩,一切才顺利。如果面板在窗体调整大小时是打开的,那么面板将不会调整大小。因此,我需要确保在调整大小发生之前关闭 DropDownPanel。不幸的是,面板没有 ResizeBegin 事件,因此您必须将此代码添加到任何锚定的 DropDownPanel 的父窗体中。

Private Sub Form_ResizeBegin(ByVal sender As Object, _
  ByVal e As System.EventArgs) Handles Me.ResizeBegin

    CloseDDP()

End Sub

Private Sub CloseDDP()

    If DropDownPanelAnchored.IsOpen Then _
        DropDownPanelAnchored.ForceCloseDropDown()
    If DropDownPanelDocked.IsOpen Then _
        DropDownPanelDocked.ForceCloseDropDown()

End Sub

现在,在开始调整大小时,DropDownPanel 会被关闭,一切都很好……直到我最大化或最小化窗体。这不会触发窗体的 Resize 事件。要捕获这一点,您必须重写 MessageWindow.WndProc 方法。

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    'MessageWindow.WndProc Method 
    If (m.Msg = WM_SYSCOMMAND) Then
        If m.WParam.ToInt64 = SC_MAXIMIZE _
            Or m.WParam.ToInt64 = SC_RESTORE _
            Or m.WParam.ToInt64 = SC_MINIMIZE _
            Or m.WParam.ToInt64 = SC_DblClckTitleBarMAX _
            Or m.WParam.ToInt64 = SC_DblClckTitleBarRestore Then
            CloseDDP()
        End If
    End If
    'Call the base
    MyBase.WndProc(m)
End Sub

现在又一切都很好……直到窗体被最小化。WndProc 正在工作,但在最小化过程中,控件以一种糟糕的方式自行调整了大小。因此,我需要阻止在最小化时进行调整大小。

Private Sub DropDownPanel_Resize( _
  ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
    'Block resizing when the parent form minimizes 
    Try
        If Me.FindForm.WindowState <> FormWindowState.Minimized _
          And blnIsResizeOK Then
            ResizeMe()
        End If
    Catch ex As Exception
    End Try
End Sub

有关示例,请参阅 Form2

历史

  • 版本 1.0 - 2008 年 10 月。
© . All rights reserved.