I'm currently working on a WinForms App in vb.net, where you can select different data in a combobox. The items inside these combobox can be edited or deleted, so heres my question: Is there a way to add icons, e.g. a pencil and a trash, for each item to show the user "Click here to edit" or "Click here to delete"?
In my head it looks kind of the following picture:
Thank you very much :)
I created a new combo box control by inheriting a class from ComboBox
.
Imports System.ComponentModel
Public Class ComboBoxEx
Inherits ComboBox
...
End Class
The idea is to use the DrawMode
DrawMode.OwnerDrawFixed
and to do all the drawing in code. This allows us to draw the images representing the clickable buttons. I added two images as resources to the project (My.Resources.pencil
and My.Resources.Trash_16x16
, yours might have different names).
Const IconSize = 20
Dim stringFormat As StringFormat = New StringFormat() With {.LineAlignment = StringAlignment.Center}
Public Sub New()
DrawMode = DrawMode.OwnerDrawFixed
DropDownStyle = ComboBoxStyle.DropDownList
ItemHeight = 21
End Sub
Protected Overrides Sub OnDrawItem(e As DrawItemEventArgs)
e.DrawBackground()
If e.Index >= 0 Then
Dim g As Graphics = e.Graphics
Dim brushColor = If(((e.State And DrawItemState.Selected) <> 0),
SystemColors.Highlight,
e.BackColor)
Using brush As Brush = New SolidBrush(brushColor)
g.FillRectangle(brush, e.Bounds)
End Using
Using textBrush As Brush = New SolidBrush(e.ForeColor)
g.DrawString(Items(e.Index).ToString(), e.Font, textBrush, e.Bounds, stringFormat)
End Using
' Skip the default item at index = 0 and the text box area (DrawItemState.ComboBoxEdit)
If e.Index > 0 And (e.State And DrawItemState.ComboBoxEdit) = 0 Then
Dim image = My.Resources.pencil
Dim point = New Point(
Width - 2 * IconSize + (IconSize - image.Width) \ 2,
e.Bounds.Y + (ItemHeight - image.Height) \ 2)
g.DrawImage(image, point)
image = My.Resources.Trash_16x16
point = New Point(
Width - IconSize + (IconSize - image.Width) \ 2,
e.Bounds.Y + (ItemHeight - image.Height) \ 2)
g.DrawImage(image, point)
End If
End If
e.DrawFocusRectangle()
End Sub
This was the visual part. Now we must detect mouse clicks on the buttons in the drop down and also raise events when they are clicked.
Dim isDroppedDown As Boolean
Public Event Button1Clicked()
Public Event Button2Clicked()
Protected Overrides Sub OnDropDown(e As EventArgs)
isDroppedDown = True
MyBase.OnDropDown(e)
End Sub
Protected Overrides Sub OnDropDownClosed(e As EventArgs)
isDroppedDown = False
MyBase.OnDropDownClosed(e)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_COMMAND = &H111
If LicenseManager.UsageMode = LicenseUsageMode.Runtime And isDroppedDown And
m.Msg = WM_COMMAND And (CType(m.WParam, Int64) >> 16) = 1 Then
Dim button = ButtonClicked()
' If the user clicked a button (skipping default item)
If button <> 0 And SelectedIndex > 0 Then
m.Result = New IntPtr(1)
If button = 1 Then
RaiseEvent Button1Clicked()
Else
RaiseEvent Button2Clicked()
End If
Return
End If
End If
MyBase.WndProc(m)
End Sub
Private Function ButtonClicked() As Integer
Dim pos = PointToClient(MousePosition)
If pos.X > Size.Width - IconSize Then
Return 2
ElseIf pos.X > Size.Width - 2 * IconSize Then
Return 1
End If
Return 0
End Function
After compiling your project, this new ComboBoxEx
appears in the winforms toolbox and you can drag and drop it to your form.
In the form you can then handle the button events
Private Sub ComboBoxEx1_Button1Clicked() Handles ComboBoxEx1.Button1Clicked
Label1.Text = $"Pen clicked. Item = {ComboBoxEx1.SelectedItem.ToString()}"
End Sub
Private Sub ComboBoxEx1_Button2Clicked() Handles ComboBoxEx1.Button2Clicked
Label1.Text = $"Trash bin clicked. Item = {ComboBoxEx1.SelectedItem.ToString()}"
End Sub
You may have to tweak the icon size, text size etc. to fit your needs.