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






3.92/5 (12 votes)
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.
- Download khaledmultiselectcombo.rar - 215.5 KB
- Download MultiComboDLLOnAnotherProject.rar - 130.6 KB
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.
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.
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.
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.
// //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