214 lines
9.9 KiB
VB.net
214 lines
9.9 KiB
VB.net
Imports System.Windows.Forms.VisualStyles
|
|
|
|
Public Class SplitButton
|
|
Inherits Button
|
|
|
|
Private _state As PushButtonState
|
|
Private Const PushButtonWidth As Integer = 14
|
|
Private Shared BorderSize As Integer = SystemInformation.Border3DSize.Width * 2
|
|
Private skipNextOpen As Boolean = False
|
|
Private dropDownRectangle As New Rectangle
|
|
Private _showSplit As Boolean = True
|
|
|
|
Public Sub New()
|
|
Me.AutoSize = True
|
|
End Sub
|
|
Public Property ShowSplit() As Boolean
|
|
Get
|
|
Return Me._showSplit
|
|
End Get
|
|
Set(ByVal value As Boolean)
|
|
If value <> Me._showSplit Then
|
|
Me._showSplit = value
|
|
Me.Invalidate()
|
|
If Me.Parent IsNot Nothing Then
|
|
Me.Parent.PerformLayout()
|
|
End If
|
|
End If
|
|
End Set
|
|
End Property
|
|
Private Property State() As PushButtonState
|
|
Get
|
|
Return Me._state
|
|
End Get
|
|
Set(ByVal value As PushButtonState)
|
|
If value <> Me._state Then
|
|
Me._state = value
|
|
Me.Invalidate()
|
|
End If
|
|
End Set
|
|
End Property
|
|
Public Overrides Function GetPreferredSize(ByVal proposedSize As System.Drawing.Size) As System.Drawing.Size
|
|
GetPreferredSize = MyBase.GetPreferredSize(proposedSize)
|
|
If Me._showSplit AndAlso Not String.IsNullOrEmpty(Me.Text) AndAlso TextRenderer.MeasureText(Me.Text, Me.Font).Width + PushButtonWidth > GetPreferredSize.Width Then
|
|
GetPreferredSize += New Size(PushButtonWidth + BorderSize * 2, 0)
|
|
End If
|
|
End Function
|
|
Protected Overrides Function IsInputKey(ByVal keyData As System.Windows.Forms.Keys) As Boolean
|
|
If (keyData.Equals(Keys.Down) AndAlso Me._showSplit) Then Return True
|
|
Return MyBase.IsInputKey(keyData)
|
|
End Function
|
|
Protected Overrides Sub OnGotFocus(ByVal e As System.EventArgs)
|
|
If Not Me._showSplit Then
|
|
MyBase.OnGotFocus(e)
|
|
ElseIf Not State = PushButtonState.Pressed AndAlso Not State = PushButtonState.Disabled Then
|
|
State = PushButtonState.Default
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnLostFocus(ByVal e As System.EventArgs)
|
|
If Not Me._showSplit Then
|
|
MyBase.OnLostFocus(e)
|
|
ElseIf State <> PushButtonState.Pressed AndAlso State <> PushButtonState.Disabled Then
|
|
State = PushButtonState.Normal
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnKeyDown(ByVal kevent As System.Windows.Forms.KeyEventArgs)
|
|
If Me._showSplit Then
|
|
If kevent.KeyCode = Keys.Down Then
|
|
ShowContextMenuStrip()
|
|
ElseIf kevent.KeyCode = Keys.Space AndAlso kevent.Modifiers = Keys.None Then
|
|
State = PushButtonState.Pressed
|
|
End If
|
|
End If
|
|
MyBase.OnKeyDown(kevent)
|
|
End Sub
|
|
Protected Overrides Sub OnKeyUp(ByVal kevent As System.Windows.Forms.KeyEventArgs)
|
|
If kevent.KeyCode = Keys.Space Then
|
|
If Control.MouseButtons = MouseButtons.None Then
|
|
State = PushButtonState.Normal
|
|
End If
|
|
End If
|
|
MyBase.OnKeyUp(kevent)
|
|
End Sub
|
|
Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
|
|
If Not Me._showSplit Then
|
|
MyBase.OnMouseDown(e)
|
|
ElseIf dropDownRectangle.Contains(e.Location) Then
|
|
ShowContextMenuStrip()
|
|
Else
|
|
State = PushButtonState.Pressed
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
|
|
If Not Me._showSplit Then
|
|
MyBase.OnMouseUp(e)
|
|
ElseIf ContextMenuStrip Is Nothing OrElse Not ContextMenuStrip.Visible Then
|
|
SetButtonDrawState()
|
|
If Bounds.Contains(Parent.PointToClient(Cursor.Position)) AndAlso Not dropDownRectangle.Contains(e.Location) Then
|
|
OnClick(New EventArgs())
|
|
End If
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnMouseEnter(ByVal e As System.EventArgs)
|
|
If Not ShowSplit Then
|
|
MyBase.OnMouseEnter(e)
|
|
ElseIf State <> PushButtonState.Pressed AndAlso State <> PushButtonState.Disabled Then
|
|
State = PushButtonState.Hot
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnMouseLeave(ByVal e As System.EventArgs)
|
|
If Not ShowSplit Then
|
|
MyBase.OnMouseLeave(e)
|
|
ElseIf State <> PushButtonState.Pressed AndAlso State <> PushButtonState.Disabled Then
|
|
If Focused Then
|
|
State = PushButtonState.Default
|
|
Else
|
|
State = PushButtonState.Normal
|
|
End If
|
|
End If
|
|
End Sub
|
|
Protected Overrides Sub OnPaint(ByVal pevent As System.Windows.Forms.PaintEventArgs)
|
|
MyBase.OnPaint(pevent)
|
|
If Me._showSplit Then
|
|
Dim g As Graphics = pevent.Graphics
|
|
Dim bounds As Rectangle = Me.ClientRectangle
|
|
|
|
'draw the button background as according to the current state.
|
|
If State <> PushButtonState.Pressed AndAlso IsDefault AndAlso Not Application.RenderWithVisualStyles Then
|
|
Dim backgroundBounds As Rectangle = bounds
|
|
backgroundBounds.Inflate(-1, -1)
|
|
ButtonRenderer.DrawButton(g, backgroundBounds, State)
|
|
'button renderer doesnt draw the black frame when themes are off =(
|
|
g.DrawRectangle(SystemPens.WindowFrame, 0, 0, bounds.Width - 1, bounds.Height - 1)
|
|
Else
|
|
ButtonRenderer.DrawButton(g, bounds, State)
|
|
End If
|
|
|
|
'calculate the current dropdown rectangle.
|
|
dropDownRectangle = New Rectangle(bounds.Right - PushButtonWidth - 1, BorderSize, PushButtonWidth, bounds.Height - BorderSize * 2)
|
|
Dim internalBorder As Integer = BorderSize
|
|
Dim focusRect As New Rectangle(internalBorder, internalBorder, bounds.Width - dropDownRectangle.Width - internalBorder, bounds.Height - (internalBorder * 2))
|
|
Dim drawSplitLine As Boolean = State = PushButtonState.Hot OrElse State = PushButtonState.Pressed OrElse Not Application.RenderWithVisualStyles
|
|
If RightToLeft = Windows.Forms.RightToLeft.Yes Then
|
|
dropDownRectangle.X = bounds.Left + 1
|
|
focusRect.X = dropDownRectangle.Right
|
|
If drawSplitLine Then
|
|
'draw two lines at the edge of the dropdown button
|
|
g.DrawLine(SystemPens.ButtonShadow, bounds.Left + PushButtonWidth, BorderSize, bounds.Left + PushButtonWidth, bounds.Bottom - BorderSize)
|
|
g.DrawLine(SystemPens.ButtonFace, bounds.Left + PushButtonWidth + 1, BorderSize, bounds.Left + PushButtonWidth + 1, bounds.Bottom - BorderSize)
|
|
End If
|
|
ElseIf drawSplitLine Then
|
|
'draw two lines at the edge of the dropdown button
|
|
g.DrawLine(SystemPens.ButtonShadow, bounds.Right - PushButtonWidth, BorderSize, bounds.Right - PushButtonWidth, bounds.Bottom - BorderSize)
|
|
g.DrawLine(SystemPens.ButtonFace, bounds.Right - PushButtonWidth - 1, BorderSize, bounds.Right - PushButtonWidth - 1, bounds.Bottom - BorderSize)
|
|
End If
|
|
|
|
'Draw an arrow in the correct location
|
|
Dim middle As New Point(dropDownRectangle.Left + dropDownRectangle.Width / 2, dropDownRectangle.Top + dropDownRectangle.Height / 2)
|
|
'if the width is odd - favor pushing it over one pixel right.
|
|
middle.X += dropDownRectangle.Width Mod 2
|
|
Dim arrow As Point() = New Point() {New Point(middle.X - 2, middle.Y - 1), New Point(middle.X + 3, middle.Y - 1), New Point(middle.X, middle.Y + 2)}
|
|
g.FillPolygon(SystemBrushes.ControlText, arrow)
|
|
|
|
'Figure out how to draw the text
|
|
Dim formatFlags As TextFormatFlags = TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter
|
|
'If we dont' use mnemonic, set formatFlag to NoPrefix as this will show ampersand.
|
|
If Not UseMnemonic Then
|
|
formatFlags = formatFlags Or TextFormatFlags.NoPrefix
|
|
ElseIf Not ShowKeyboardCues Then
|
|
formatFlags = formatFlags Or TextFormatFlags.HidePrefix
|
|
End If
|
|
If Not String.IsNullOrEmpty(Me.Text) Then
|
|
TextRenderer.DrawText(g, Text, Font, focusRect, SystemColors.ControlText, formatFlags)
|
|
End If
|
|
|
|
'draw the focus rectangle.
|
|
If State <> PushButtonState.Pressed AndAlso Focused Then
|
|
ControlPaint.DrawFocusRectangle(g, focusRect)
|
|
End If
|
|
End If
|
|
End Sub
|
|
Private Sub ShowContextMenuStrip()
|
|
If skipNextOpen Then
|
|
'we were called because we're closing the context menu strip
|
|
'when clicking the dropdown button.
|
|
skipNextOpen = False
|
|
Else
|
|
State = PushButtonState.Pressed
|
|
If ContextMenuStrip IsNot Nothing Then
|
|
AddHandler ContextMenuStrip.Closing, AddressOf ContextMenuStrip_Closing
|
|
ContextMenuStrip.Show(Me, New Point(0, Height), ToolStripDropDownDirection.BelowRight)
|
|
End If
|
|
End If
|
|
End Sub
|
|
Private Sub ContextMenuStrip_Closing(ByVal sender As Object, ByVal e As ToolStripDropDownClosingEventArgs)
|
|
Dim cms As ContextMenuStrip = CType(sender, ContextMenuStrip)
|
|
If cms IsNot Nothing Then
|
|
RemoveHandler cms.Closing, AddressOf ContextMenuStrip_Closing
|
|
End If
|
|
SetButtonDrawState()
|
|
If e.CloseReason = ToolStripDropDownCloseReason.AppClicked Then
|
|
skipNextOpen = dropDownRectangle.Contains(Me.PointToClient(Cursor.Position))
|
|
End If
|
|
End Sub
|
|
Private Sub SetButtonDrawState()
|
|
If Bounds.Contains(Parent.PointToClient(Cursor.Position)) Then
|
|
State = PushButtonState.Hot
|
|
ElseIf Focused Then
|
|
State = PushButtonState.Default
|
|
Else
|
|
State = PushButtonState.Normal
|
|
End If
|
|
End Sub
|
|
End Class
|