Home > Back-end >  Is there a way to add a clickable Icon to ComboBox?
Is there a way to add a clickable Icon to ComboBox?

Time:10-18

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:

enter image description here

Thank you very much :)

CodePudding user response:

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.

  • Related