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.
#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
#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
#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