Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Multiselect Combobox (Windows Custom Control) simulates a ComboBox with a CheckedListBox as a Dropdown

26 Jul 2017CPOL2 min read 49.4K   237  
This is a windows forms custom usercontrol that I used to create a Multiselect combobox with checkboxes to select one or more options. It is created using a blend of telerik controls as Telerik itself doesn't have a Multiselect Dropdownlist winforms control.

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Introduction

There are two attached zipped folders, the first named "KhaledMultiSelectCombo"  which contains the custom usercontrol with the combobox that allows Multiselection. The second zipped folder contains a windows forms application that uses the combobox on a windows form.

Background

User should only have a background in .NET and Windows forms and I will explain step by step how to use the custom usercontrol on a windows form.

Using the code

At first the following image is the MultiCombobox that will show for the end user.

Image 1

Now let's explain step by step how this could be done

Step1: the attached folder "KhaledMultiSelectCombo" contains  my custom usercontrol (Multicombo) which is composed of 4 telerik controls as shown in the following image.

Control1:Radtextbox to show Selected values.

Control2:Radtextbox to show selected texts.

Control3:Radtreeview to display options that can be selected using checkboxes.

Control4:A combobox that is shrinked and used only to expand the Radtree with the available options.

Image 2

Step2: I shrinked the usercontrol width and hide to be similar to Microsoft combobox initial size and I fixed the Dock, Anchor and visible properties of the four controls so as to show Radtextbox that displays selectedtexts on the top as shown in the following image.

Image 3

Step3: I divided my code into 3 regions, the first to set the public end user properties, the second is for the public end user events (newly created and raised events) and the third is the functions that acts as an engine to my logic of multi selection and enables the multicombo to show its list of options even if was put inside a groupbox(or any container) as its list of options will be displayed on the top of any parent container border.

Image 4

//
//here is the properties region

#Region "Setting UserControl Properties"

    Public Overrides Property Font As System.Drawing.Font
        Get
            Return RadTreeView.Font
        End Get
        Set(ByVal value As System.Drawing.Font)

            RadTextBoxValue.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
            btnFakeList.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
            RadTextBoxDisplay.Font = value
            RadTreeView.Font = value
        End Set
    End Property

    Public Property Datasource() As Object
        Get
            Return dt
        End Get
        Set(ByVal value As Object)
            dt = value
            RadTreeView.DataSource = dt

        End Set
    End Property

    Private dispMember As String
    Public Property DisplayMember() As String
        Get
            Return dispMember
        End Get
        Set(ByVal value As String)
            dispMember = value
            RadTreeView.DisplayMember = value
        End Set
    End Property

    Private valMember As String
    Public Property ValueMember() As String
        Get
            Return valMember
        End Get
        Set(ByVal value As String)
            valMember = value
            RadTreeView.ValueMember = value
        End Set
    End Property

    Public Property SelectedText() As String
        Get
            Return RadTextBoxDisplay.Text
        End Get
        Set(ByVal value As String)
            Dim s As String = ""
            For Each n As RadTreeNode In RadTreeView.Nodes
                If n.Text = value Then
                    n.Checked = True
                    s = value
                Else
                    n.Checked = False
                End If
            Next

            RadTextBoxDisplay.Text = s

        End Set
    End Property

    Public Property SelectedValue() As String
        Get
            Return RadTextBoxValue.Text
        End Get
        Set(ByVal value As String)
            Dim s As String = ""
            If Not value = "0" Then
                Dim st() As String = value.Trim.Split(",")
                For Each el As String In st
                    For Each n As RadTreeNode In RadTreeView.Nodes
                        If n.Value = el Then
                            n.Checked = True
                            If s = "" Then
                                s = el
                            Else
                                s += "," + el
                            End If
                        Else
                            n.Checked = False
                        End If
                    Next
                Next
                RadTextBoxValue.Text = s.Trim
            Else
                RadTreeView.Nodes(0).Checked = True
            End If

        End Set
    End Property

    Public ReadOnly Property SelectedCount() As Integer
        Get
            Dim cheked As Integer
            For Each n As RadTreeNode In RadTreeView.Nodes
                If n.Checked And n.Text <> "All" Then
                    cheked += 1
                End If
            Next
            Return cheked
        End Get

    End Property

    Public Property MultiSelect() As Boolean
        Get
            Return RadTreeView.MultiSelect
        End Get
        Set(ByVal value As Boolean)
            RadTreeView.MultiSelect = value
            If value = False Then
                For Each n As RadTreeNode In RadTreeView.Nodes
                    n.Checked = False
                Next
                RadTextBoxDisplay.Text = ""
                RadTextBoxValue.Text = ""
            End If

        End Set
    End Property

    Public Sub Collapse()
        RadTreeView.Visible = False
        Me.Height = 20
    End Sub
#End Region

//
//The Events Region looks as follows

#Region "Setting UserControl Events"

    Public Event SelectedIndexChanged()
    Private Sub RadTextBoxRegion_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles RadTextBoxValue.TextChanged
        RaiseEvent SelectedIndexChanged()
    End Sub

    Public Event MouseLeaves()
    Private Sub RadTreeView_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RadTreeView.MouseLeave
        RaiseEvent MouseLeaves()
    End Sub
#End Region
//
//The following is the Internal functions region
#Region "UserControl Internal Functions"

    Private Sub RadTreeView_NodeCheckedChanged(ByVal sender As System.Object, ByVal e As Telerik.WinControls.UI.RadTreeViewEventArgs) Handles RadTreeView.NodeCheckedChanged, RadTreeView.SelectedNodeChanged

        If Not _settingRadTreeSourceReg And Not _settingRadTreeSource Then
            Dim ss As String = ""
            Dim Al As Boolean = False
            If RadTreeView.Nodes.Count > 0 Then
                If RadTreeView.Nodes(0).Value = "0" And RadTreeView.Nodes(0).Checked Then
                    Al = True
                End If
                _settingRadTreeSourceReg = True
                Dim chkdCapture As Integer = RadTreeView.CheckedNodes.Count
                If RadTreeView.Nodes(0).Value = "0" And Not RadTreeView.Nodes(0).Checked And chkdCapture = RadTreeView.Nodes.Count - 1 Then
                    If RadTreeView.Nodes.Count > 0 Then
                        If e.Node.Value <> 0 Then
                            RadTreeView.Nodes(0).Checked = True
                            Al = True
                        End If
                    End If
                ElseIf RadTreeView.Nodes(0).Value = "0" And RadTreeView.Nodes(0).Checked And chkdCapture = RadTreeView.Nodes.Count - 1 Then
                    If RadTreeView.Nodes.Count > 0 Then
                        If e.Node.Value <> 0 Then
                            RadTreeView.Nodes(0).Checked = False
                            Al = False
                        End If
                    End If
                ElseIf chkdCapture = 0 Then
                    e.Node.Checked = True
                End If
                For Each it As RadTreeNode In RadTreeView.Nodes
                    If Al Then
                        If Not it.Checked Then
                            it.Checked = True
                        End If
                    Else
                        If RadTreeView.Nodes.Count > 1 Then
                            If it.Checked And it.Index <> 1 And e.Node.Value = "0" Then 'when user uncheck all option
                                it.Checked = False
                            ElseIf it.Index = 1 And e.Node.Value = "0" And chkdCapture = 0 Then
                                it.Checked = True
                                Me.SubReg = it.Value
                                Me.RadTextBoxValue.Text = it.Value
                                Me.RadTextBoxDisplay.Text = it.Text
                            End If
                        End If
                    End If
                    If Not RadTreeView.MultiSelect And Not it.Value = e.Node.Value Then
                        it.Checked = False
                    End If
                    If it.Checked Then
                        If it.Value <> "0" Then
                            If String.IsNullOrEmpty(ss) Then
                                ss = it.Value.ToString
                                RadTextBoxDisplay.Text = it.Text
                            Else
                                ss += "," + it.Value.ToString
                                RadTextBoxDisplay.Text += "," + it.Text
                            End If
                        End If
                    End If
                Next
                Me.SubReg = ss
                Me.RadTextBoxValue.Text = ss
                _settingRadTreeSourceReg = False
                CountEdits += 1
            End If
        End If

    End Sub

    Private Sub btnFakeList_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFakeList.Click

        If Me.Height = 160 Then
            btnFakeList.Focus()
            RadTreeView.Visible = False
            Me.Height = 20
            CountEdits = 0
            'Me.Focus()
        Else
            btnFakeList.Focus()
            Me.Height = 160
            RadTreeView.Height = 140
            RadTreeView.Visible = True
            CountEdits = 0

        End If

    End Sub

    Private Sub RadTextBoxValue_KeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles RadTextBoxValue.KeyPress
        e.Handled = True
    End Sub

    Private Function getnewloc(ByRef c As System.Windows.Forms.Control, ByRef loc As System.Drawing.Point)
        If TypeOf c Is System.Windows.Forms.GroupBox Or TypeOf c Is System.Windows.Forms.Panel Then
            loc.X += c.Left
            loc.Y += c.Top
            getnewloc(c.Parent, loc)
        End If
        Return loc
    End Function

    Public Sub AdjustLoc(ByVal c As System.Windows.Forms.Control, ByVal f As System.Windows.Forms.Form)
        Dim loc As System.Drawing.Point = Nothing
        loc = getnewloc(c, loc)
        Me.Parent = f
        Me.Left += loc.X
        Me.Top += loc.Y
        Me.BringToFront()

    End Sub

    Public Sub AdjustLoc(ByVal c As System.Windows.Forms.Control, ByVal h As System.Windows.Forms.UserControl)
        Dim loc As System.Drawing.Point = Nothing
        loc = getnewloc(c, loc)
        Me.Parent = h
        Me.Left += loc.X
        Me.Top += loc.Y
        Me.BringToFront()

    End Sub

    Public Sub ShowInFront(ByVal f As System.Windows.Forms.Form)
        AdjustLoc(Me.Parent, f)
    End Sub

    Public Sub ShowInFront(ByVal h As System.Windows.Forms.UserControl)
        AdjustLoc(Me.Parent, h)
    End Sub

    Private Sub btnFakeList_LostFocus(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFakeList.LostFocus, RadTreeView.LostFocus, RadTextBoxDisplay.LostFocus

        If Not RadTextBoxDisplay.Focused And Not btnFakeList.Focused And Not RadTreeView.Focused Then
            RadTreeView.Visible = False
            Me.Height = 20
        End If

    End Sub

    Public Sub Clear()
        Me.RadTreeView.Nodes.Clear()
        Me.RadTextBoxDisplay.Text = ""

    End Sub

    Public Sub AddItem(ByVal item As String)
        Me.RadTreeView.Nodes.Add(item)
    End Sub

    Public Sub AddItem(ByVal txt As String, ByVal value As String)
        Dim newNode As New RadTreeNode()
        newNode.Value = value
        newNode.Text = txt
        RadTreeView.Nodes.Add(newNode)
    End Sub

    Private Sub MultiSelectionCombo_FontChanged(sender As System.Object, e As System.EventArgs) Handles MyBase.FontChanged
        Me.RadTreeView.Font = MyBase.Font
        Me.RadTextBoxDisplay.Font = MyBase.Font
        MyBase.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        RadTextBoxValue.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        btnFakeList.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
    End Sub

#End Region

Step4: How to add the Multiselect combo to your windows forms? this will be explained in details through the following points and you can use the second attached folder "MultiComboDLLOnAnotherProject" that has a windows form sample with the Multicombo added to it.

Simply, in your windows form application do the following to add and use the Multicombo custom control:

* Create a folder named "ref" then right click it and press "add existing items" to add the usercontrol dll "khaledMultiComboControl.dll" from the bin folder in the "KhaledMultiSelectCombo" folder and also add Telerik dlls from there.

*There is a referrence folder, right click it then press "Add referrence" to add the dlls just added in your "ref" folder in the previous point.

*Open your Toolbox and right click it then press "choose Items" and in the ".NET framework components" tab browse the Multicombobox dll "khaledMultiComboControl.dll" from the "ref" folder then press ok. The Multicombobox control will show on your toolbox and you will be able to drag it on your form.

* The final point is the following sample code that shoes how to populate and use the Multiselect combobox on your windows forms.

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Populate()
     
    End Sub

    Sub Populate()
        Dim dt As DataTable = GetTable(MultiSelectionCombo1.MultiSelect)
        MultiSelectionCombo1.Datasource = dt
        MultiSelectionCombo1.DisplayMember = "Text"
        MultiSelectionCombo1.ValueMember = "ID"
        MultiSelectionCombo1.ShowInFront(Me)
        MultiSelectionCombo1.SelectedValue = "0"

    End Sub
    Function GetTable(ByVal EnableMultiselect As Boolean) As DataTable
        ' Create new DataTable.
        Dim table As New DataTable
        ' Create 2 typed columns in the DataTable.
        table.Columns.Add("ID", GetType(Integer))
        table.Columns.Add("Text", GetType(String))
        ' Add  rows
        table.Rows.Add(0, "All")
        table.Rows.Add(50, "Khaled")
        table.Rows.Add(10, "Ezzat")
        table.Rows.Add(21, "AbdelFattah")
        table.Rows.Add(100, "AbdelGawad")
        table.Rows.Add(1, "Abdelraouf")
        table.Rows.Add(2, "Esmael")
        table.Rows.Add(3, "Kareem")
        table.Rows.Add(4, "Janet")
        table.Rows.Add(1500, "Mary")
        Return table
    End Function

    Private Sub MultiSelectionCombo1_SelectedIndexChanged() Handles MultiSelectionCombo1.SelectedIndexChanged
        TextBox1.Text = MultiSelectionCombo1.SelectedText
        TextBox2.Text = MultiSelectionCombo1.SelectedValue
        lblcount.Text = MultiSelectionCombo1.SelectedCount
        lbltxt.Text = MultiSelectionCombo1.SelectedText
    End Sub

    Private Sub MultiSelectionCombo1_MouseLeaves() Handles MultiSelectionCombo1.MouseLeaves
        MultiSelectionCombo1.Collapse()
    End Sub

    Private Sub chkMultiSelect_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkMultiSelect.CheckedChanged
        If chkMultiSelect.Checked Then
            MultiSelectionCombo1.MultiSelect = True

        Else
            MultiSelectionCombo1.MultiSelect = False
            Populate()
        End If

    End Sub
End Class

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)