Introduction
The purpose of this article is to discuss the construction of a couple of simple 
custom controls that extend both the Combobox and the Listbox controls to allow 
them to be associated with both a text and a value property for each item in the 
control's list.  Both 
controls will normally allow the developer to assign a value property and a text 
property to each item in the list but only if the control is bound to a data 
source. If the control is not bound, the 
option to immediately assign a value to an item in the list is not available.
If one requires that both text and a value be assigned to each item in the list, 
it is easy enough define a class containing a text and value property of some 
type and to then create a collection of objects of that type and bind that 
collection to the control.  In 
this approach, the developer may need also to need to write handlers to get the 
value associated with a selected item, to remove items form the list, and to add 
items to the list (along with a value). If there are a large number controls, 
writing these additional functions to handle each specific control can be 
somewhat tedious.
There are a number of reasons why it could be beneficial to supply both a text 
and value property to an unbound list based control item. In 
general, if it is not productive, 
expedient, or even possible to bind a list based control to a data source and 
the value property is meaningless to the user but essential to the application, 
having the means to supply the value property along with the user friendly text 
can be pretty handy.
This demo includes a 
project with two extended, list based controls capable of holding both a text 
and a value property without binding, and it contains a demonstration project in 
which the two controls are used.
![Dem- Project-in-VB.NET.jpg]()
Figure 1:  Demo Project with Extended 
Combobox and Listbox in Use. 
The demo application shown if figure 1 uses both the extended Combobox and 
Listbox controls; the extended Listbox contains the alphabet in phonetic format; 
the text property contains the phonetic alphabet whilst the value property 
contains a not to user friendly pipe delimited string of dots and dashes used to 
define the Morse code equivalent for the letter. I 
picked on the Morse code to supply the value because whilst most people would 
easily recognize that Alpha stands for 'A' and Bravo stands for 'B'; they would 
not so easily recognize that |...|---|...| means "SOS" or that |...|-.-.|.|-|-| 
represents "Scott" in Morse code.
Just for fun, when the user selects one of the options from the extended 
Listbox, the application displays both the phonetic (text) property and Morse 
code (value) property in the two textboxes below the extended Combobox. Also, 
when an item is selected, the application will decipher the Morse code (value) 
and play it aloud so the user can hear the Morse code whilst seeing the phonetic 
alphabet character and string representation of the Morse code. The 
two buttons located adjacent to each text box are used to retrieve the value 
property from the text property or the text property from the value property. The 
only purpose of either is to demonstrate that it can be done.
The extended Combobox control is used in a manner similar to the extended 
Listbox but it does not play any Morse code through the speakers. It 
displays the names of different truck manufacturers along with a number used to 
identify that manufacturer. As with the 
Listbox, the Combobox contains two text boxes beneath it which are used to 
display the text and value properties for the selected item. It 
also uses two button controls to either pull up the value from the text or the 
text from the value. 
Getting Started
The solution contains 
two projects; one is a class library project containing two custom controls and 
the other is a demonstration Windows application that contains a single 
form. The two controls contained in the class library project are an extended 
version of each the ComboBox and the ListBox control. The form in the 
demonstration application contains both types of custom control along with some 
additional controls used to exercise the custom controls.
![Solution_Explorer-in-VB.NET.jpg]()
Figure 2:  Solution Explorer. 
Project:  Extended 
List Controls
The extended list controls project is a class library project that contains two 
custom controls: ExComboBox.cs and ExListBox.cs.  The 
controls are extended versions of the standard toolbox controls; the purpose of 
the extension of each control was to add in a collection (a SortedList) and to 
provide the additional code necessary to give access to the collection and to 
display the members of the collections is the control's standard list. Since 
both controls are essentially the same except for the type of control extended, 
I will only describe one of the two control's code; the code for the 
ExComboBox.cs control is as follows:
The class contains the following references, namespace and class declaration; 
note the addition of System.Collections and System.Windows.Forms:
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.Collections 
 
Public Class ExComboBox
    Inherits System.Windows.Forms.ComboBox 
The class declaration shows that the class is based upon and extends the 
standard Combobox control. By inheriting 
the combobox control, we pick up all of the functionality associated with that 
control. Following the class 
declaration, a sorted list is declared, and in the constructor, a new instance 
of the sorted list is created:
''' <summary>
''' sorted list used to retain
''' text and value pairs for the unbound
''' extended combobox control
''' </summary>
''' <remarks></remarks>
Private mListCollection As SortedList 
 
''' <summary>
''' Constructor
''' </summary>
''' <remarks></remarks>
Public Sub New()
 
    'Instance 
the collection in
    'the 
constructor
    mListCollection = New SortedList()
 
End Sub
The sorted list, "mListCollection" will be used to contain the text and value 
pairs assigned to each item in the control's list.  Following 
the constructor, the first method is defined: 
''' <summary>
''' Provides public method to both add items to private
''' collection of text-value pairs and to reload the
''' combobox list with the items from the key side of the
''' sortedlist items
''' </summary>
''' <param 
name="sText"></param>
''' <param 
name="sValue"></param>
''' <remarks></remarks>
Public Sub AddListItem(ByVal sText As String, ByVal sValue As String)
 
    Try
        mListCollection.Add(sText, 
sValue)
    Catch
        Return
    End Try
 
    Me.Items.Clear()
 
    Dim de As DictionaryEntry
    For Each de In mListCollection
        Me.Items.Add(de.Key.ToString())
    Next
 
End Sub
The AddListItem method defined above is used to add key-value pairs to the 
sorted list. The attempt to add an item 
is wrapped in a try catch block; this will prevent attempts to load duplicate 
keys from interfering with the application; if the user does attempt to enter 
duplicate keys, the attempt will fail the application will return from the 
method call without changing the sorted list.One could rig this method to return 
Boolean value indicating the success of the operation. After 
the item has been added to the sorted list, the combobox is cleared of all items 
and each item from the newly updated sorted list is then added back into the 
list.
The next method defined is used to remove an item from the sorted list; this 
method works very much the same as the code used to add an item:
''' <summary>
''' Remove the key item from the private sortedlist if
''' the item matches the text passed into this
''' public subroutine
''' </summary>
''' <param 
name="sText"></param>
''' <remarks></remarks>
Public Sub RemoveListItem(ByVal sText As String)
 
    Try
        mListCollection.Remove(sText)
    Catch
        Return
    End Try
 
    Me.Items.Clear()
 
    Dim de As DictionaryEntry
    For Each de In mListCollection
        Me.Items.Add(de.Key.ToString())
    Next
End Sub
The next bit of code in the control is used to get the value associated with an 
item contained in the list. Selection of the returned item is based upon the 
current selected index value from the control's standard list:
''' <summary>
''' Get the value associated with a control list item
''' based upon the selected index value
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetValue() As String
 
    Try
        Return Convert.ToString(mListCollection.GetByIndex 
_(Me.SelectedIndex))
    Catch
        Return String.Empty
    End Try
 
End Function
If the item is not found in the list, the method returns an empty string.
The next method in the class is used to return the value associated with a list 
item's text. The value is obtained by 
examining the contents of the list until the value text is found and then, per 
the found text, the value is obtained and returned as a string. If the value is 
not located, an empty string is returned:
''' <summary>
''' A public method to return the value associated with the
''' text (key) from the private sorted list based upon a
''' match of the string passed to this method against the
''' key values in the sorted list; if nothing matches, an
''' emptry string will be returned.
''' </summary>
''' <param 
name="sText"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetValueByText(ByVal sText As String) As String
 
    Dim tmp As String = String.Empty
 
    Try
        Dim de As DictionaryEntry
 
        For Each de In mListCollection
            If de.Key.ToString 
= sText Then
                tmp 
= de.Value.ToString()
            End If
        Next
        Return tmp
    Catch
        Return tmp
    End Try
 
End Function
The last method in the control's code is used to obtain the text for a list item 
based upon its value:
''' <summary>
''' Return the text of an item based upon its value; if the
''' items is not found, return an empty string.
''' </summary>
''' <param 
name="sValue"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetTextByValue(ByVal sValue As String) As String
 
    Dim tmp As String = String.Empty
 
    Try
        Dim de As DictionaryEntry
 
        For Each de In mListCollection
            If de.Value.ToString 
= sValue Then
                tmp = de.Key.ToString()
            End If
        Next
        Return tmp
    Catch
        Return tmp
    End Try
 
End Function
Note that since only the text side is guaranteed to be unique, if the collection 
contained multiple values of the same content, this method will return the first 
item found that meets the criteria. The 
important method is therefore one that retrieves the value associated with the 
unique key (text) property. The value of 
this last function will be limited unless the user of the control limits the 
list to items unque as both text and value.
That wraps up the code in the custom control. As 
mention previously, the extended ListBox control works in exactly the same 
manner with the same methods and properties available. 
Project:  Test 
Collection Controls (Demonstration Project)
This project contains a single form and one of each of the two custom controls 
(along with some other controls used in conjunction with those custom controls.
The form contains two group boxes; in the top box is an extended ListBox control 
along with two text boxes and two buttons.  When 
a user makes a selection from the custom control, the event handler for that 
control will be used to place the related text and value properties into each of 
the two text boxes. The two buttons are 
used along with text boxes to pull up the value from the text or the text from 
the value. In this case, the custom 
control is populated with all of the letters defined in the phonetic alphabet on 
the text side and the Morse code representation of the letter in the value side. Morse 
code values are displayed in the form of a pipe delimited string made up of dots 
and dashes. Further, when the item is 
selected, the value is sent to a method that deciphers the Morse code and plays 
it aloud through the speakers.
The second group box contains an extended ComboBox control; this control is 
populated with a list comprised of the names of truck manufacturers along with 
an arbitrary six digit identifier. This 
group box also contains two text boxes and two buttons that are also used to 
display the value and text properties associated with items contained in the 
control's sorted list. The buttons here 
are used to display the value associated with the selected text and the text 
associated with selected value.
The form class code starts out with the following references, namespace, and 
class declaration:
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.Runtime.InteropServices 
 
''' <summary>
''' Demo application used to test the extended listbox
''' and combobox controls
''' </summary>
''' <remarks></remarks>
Public Class Form1
Note the addition of a reference to System.Runtime.InteropServices; this is used 
to support the DLL Import of the Beep method which is used to play the Morse 
code sounds.
After the class declaration, the following code is inserted to import the beep 
method from the kernel32 DLL:
''' <summary>
''' Import a kernel32.dll function to play beeps;
''' used with the morse code player function
''' </summary>
''' <param 
name="frequency"></param>
''' <param 
name="duration"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Declare Function Beep Lib "kernel32.DLL" _
(ByVal frequency As Integer, ByVal duration As Integer) As Boolean
The next bit of code is used to manage the form load event; the constructor is 
in the default configuration whilst in the form load event, the custom controls 
are manually populated with text and values using each control's public "AddListItem" 
method; this method accepts two arguments, the key and the value, which are 
loaded into the control's internal sorted list. When 
the sorted list is updated, the contents of the controls visible list is updated 
to display text added through the method:
Private Sub Form1_Load(ByVal sender As System.Object, 
_
ByVal e As System.EventArgs) Handles MyBase.Load 
 
    ' 
manually populate the list with the key and
    ' 
value pairs - This loads the extended
    ' 
combobox with the phonetic alphabet and
    ' 
the Morse code equivalent represented
    ' 
by a pipe delimited string containing
    ' 
dots and dashes
    ExListBox1.AddListItem("Alpha", "|.|-|")
    ExListBox1.AddListItem("Bravo", "|-|.|.|.|")
    ExListBox1.AddListItem("Charlie", "|-|.|-|.|")
    ExListBox1.AddListItem("Delta", "|-|.|.|")
    ExListBox1.AddListItem("Echo", "|.|")
    ExListBox1.AddListItem("Foxtrot", "|.|.|-|.|")
    ExListBox1.AddListItem("Golf", "|-|-|.|")
    ExListBox1.AddListItem("Hotel", "|.|.|.|.|")
    ExListBox1.AddListItem("India", "|.|.|")
    ExListBox1.AddListItem("Juliette", "|.|-|-|-|")
    ExListBox1.AddListItem("Kilo", "|-|.|-|")
    ExListBox1.AddListItem("Lima", "|.|-|.|.|")
    ExListBox1.AddListItem("Mike", "|-|-|")
    ExListBox1.AddListItem("November", "|-|.|")
    ExListBox1.AddListItem("Oscar", "|-|-|-|")
    ExListBox1.AddListItem("Papa", "|.|-|-|.|")
    ExListBox1.AddListItem("Quebec", "|-|-|.|-|")
    ExListBox1.AddListItem("Romeo", "|.|-|.|")
    ExListBox1.AddListItem("Sierra", "|.|.|.|")
    ExListBox1.AddListItem("Tango", "|-|")
    ExListBox1.AddListItem("Uniform", "|.|.|-|")
    ExListBox1.AddListItem("Victor", "|.|.|.|-|")
    ExListBox1.AddListItem("Whiskey", "|.|-|-|")
    ExListBox1.AddListItem("Xray", "|-|.|.|-|")
    ExListBox1.AddListItem("Yankee", "|-|.|-|-|")
    ExListBox1.AddListItem("Zulu", "|-|-|.|.|") 
 
    ' 
Populate the extended listbox control with
    ' 
the names of some trucks along with some otherwise
    ' 
meaningless values
    ExComboBox1.AddListItem("Mack", "012393")
    ExComboBox1.AddListItem("Peterbuilt", "234234")
    ExComboBox1.AddListItem("International 
Harvester", "345432")
    ExComboBox1.AddListItem("White 
Freightliner", "213453")
    ExComboBox1.AddListItem("Kenworth", "856745")
    ExComboBox1.AddListItem("GMC 
General", "234865")
    ExComboBox1.AddListItem("Ford", "328975")
    ExComboBox1.AddListItem("Ivenco", "675474")
    ExComboBox1.AddListItem("Magirus", "666873")
    ExComboBox1.AddListItem("Isuzu", "343445")
    ExComboBox1.AddListItem("Volvo", "648857")
    ExComboBox1.AddListItem("Dodge", "111349")
 
End Sub
 
The next item up in the form code is the selected index changed event handler 
for the custom control; this handler sets the text showing the selected text and 
the associatd value for the selected item. It 
further sends the value to a separate method that is used to play the Morse code 
version of the selected item.
''' <summary>
''' Using the custom listbox control to retrieve
''' text-value pairs
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub ExListBox1_SelectedIndexChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _ExListBox1.SelectedIndexChanged
 
    txtSelectText.Text 
= ExListBox1.SelectedItem.ToString()
    txtSelectValue.Text 
= ExListBox1.GetValue()
    Refresh()
    PlayMorseCode(txtSelectValue.ToString())
 
End Sub
The two button handlers for the custom Listbox control are next, the first is 
used to get the value associated with the text contained in the text box whilst 
the second does the opposite and gets the text associated with the  value. Both 
are displayed in a message box once they have been retrieved from the custom 
control's internal sorted list through the appropriate method call.
''' <summary>
''' Get the value associated with the key
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub btnGetValue_Click(ByVal sender As System.Object, 
_
ByVal e As System.EventArgs) Handles btnGetValue.Click
 
    Dim tmp As String = String.Empty
 
    If txtSelectText.Text 
<> String.Empty Then
        tmp 
= ExListBox1.GetValueByText(txtSelectText.Text.ToString())
    End If
 
    MessageBox.Show("'" + 
tmp + "' 
is the matching value.", _"Get 
Value from Text")
 
End Sub 
 
''' <summary>
''' Get the key associated with the value
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub btnGetKey_Click(ByVal sender As System.Object, 
_
ByVal e As System.EventArgs) Handles btnGetKey.Click
 
    Dim tmp As String = String.Empty
 
    If txtSelectValue.Text 
<> String.Empty Then
        tmp 
= ExListBox1.GetTextByValue(txtSelectValue.Text.ToString())
    End If
 
    MessageBox.Show("'" + 
tmp + "' 
is the matching value.", _"Get 
Text from Value")
 
End Sub
The next item up is the selected index changed event handler for the custom 
ComboBox control.
''' <summary>
''' Using the custom combobox control to retrieve
''' text-value pairs
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub ExComboBox1_SelectedIndexChanged(ByVal sender As _
System.Object, ByVal e As System.EventArgs) Handles _ExComboBox1.SelectedIndexChanged
 
    ' 
put the combobox text and value into a couple
    ' 
of textboxes for display
    txtSelectText2.Text 
= ExComboBox1.SelectedItem.ToString()
    txtSelectValue2.Text 
= ExComboBox1.GetValue()
End Sub
After that, the next two items handle the button click events for the two 
buttons used with the custom ComboBox control and its associated text boxes.
''' <summary>
''' Retrieve the value from the key
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub btnGetValue2_Click(ByVal sender As System.Object, ByVal e 
_
As System.EventArgs) Handles btnGetValue2.Click
 
    Dim tmp As String = String.Empty
 
    If txtSelectText2.Text 
<> String.Empty Then
        tmp 
= ExComboBox1.GetValueByText(txtSelectText2.Text.ToString())
    End If
 
    MessageBox.Show("'" + 
tmp + "' 
is the matching value.", _"Get 
Value from Text")
 
End Sub 
 
''' <summary>
''' Retrieve the key from the value
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub btnGetKey2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnGetKey2.Click
 
    Dim tmp As String = String.Empty
 
    If txtSelectValue2.Text 
<> String.Empty Then
        tmp 
= ExComboBox1.GetTextByValue(txtSelectValue2.Text.ToString())
    End If
 
    MessageBox.Show("'" + 
tmp + "' 
is the matching value.", _"Get 
Text from Value")
 
End Sub
After the event handlers, the next bit of code is a method used to play the 
Morse code equivalent of letter selected from the alphabet. The 
method parses the contents of the Morse code string passed to it and plays the 
appropriate sequence of dits an and da's aloud.
''' <summary>
''' Use the passed in value string containing the dots and
''' dashes defining the Morse code representation of the
''' character and play the Morse code version of the letter
''' aloud through the speakers.
''' </summary>
''' <param 
name="sMorseCode"></param>
''' <remarks></remarks>
Private Sub PlayMorseCode(ByVal sMorseCode As String)
 
    Dim ditAndDa As String() 
= sMorseCode.Split("|")
    Dim s As String
 
    For Each s In ditAndDa
        Select Case (s)
            Case "."
                Beep(900, 
100)
                System.Threading.Thread.Sleep(50)
            Case "-"
                Beep(900, 300)
                System.Threading.Thread.Sleep(50)
            Case Else
                'do 
nothing
        End Select
    Next
 
    ' 
maintain a little separation between letters
    System.Threading.Thread.Sleep(50)
 
End Sub
The last in the form class is merely used to handle the exit button click event.
''' <summary>
''' Exit the application
''' </summary>
''' <param 
name="sender"></param>
''' <param 
name="e"></param>
''' <remarks></remarks>
Private Sub btnExit_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles btnExit.Click
 
    Application.Exit()
 
End Sub
Summary.
This article was intended to demonstrate one approach to building a custom 
control that would permit the user to associate a value property with the items 
contained in the list of an unbound list based control such as ComboBox or 
ListBox. The method demonstrated 
consisted of constructing a simple custom control that maintains a sorted list 
associated with the control's standard list. The 
custom control also provides a nominal number of methods used to obtain the 
value or text associated with an item selected from the list. Since 
the standard list based controls do not support a value property for the control 
unless the control is bound, the approach demonstrated offers a simple work 
around that can be used if it is considered desirable to apply a value to each 
list item contained in an unbound control.
The control presented is quite simple and could be improved upon greatly; for 
example if one were to modify the control to permit the user of the control to 
edit the collection through a collection editor at design time that may be more 
appealing to some than manually keying in the collection.