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






4.84/5 (15投票s)
一个类似 ComboBox 的下拉控件,可以在设计时将其他控件放置并排列在其上。
引言
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
PanelSize
PanelGradientType
,PanelBackColorA
,PanelBackColorB
PanelBorderColor
AutoControlMargin
PanelCornerRadius
,TextBoxCornerRadius
TextBoxGradientType
,TextBoxBackColorA
,TextBoxBackColorB
TextBoxBorderColor
ButtonShape
ShowPushPin
GraphicBorderColor
GraphicImage
GraphicWidth
GraphicAutoWidth
DropDownPanel
加载时是打开还是关闭。
获取或设置 DropDownPanel
的大小。
用于填充面板的颜色和混合类型。
获取或设置 DropDownPanel
的边框颜色。
获取或设置控件的右侧和底部边距。
获取或设置圆角半径。
用于填充文本框的颜色和混合类型。
获取或设置文本框的边框颜色。
获取或设置下拉按钮的形状。
获取或设置图钉是否可见。
获取或设置图形周围的边框颜色。
获取或设置文本框旁边的图像。
获取或设置图形图像的宽度。
获取或设置根据图像纵横比自动调整宽度。
鼠标事件
跟踪光标是否在按钮或图钉等区域上方。
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 月。