A Checked Listbox Collection Editor
Implements a custom, checked listbox editor in a property grid.

Introduction
This code demonstrates how to create a custom property editor particularly suitable for key/value pairs of information. It implements IWindowsFormsEditorService
and UITypeEditorEditStyle
, which are used to create a custom property grid control.
Background
Several existing projects demonstrate how to create a custom ListBox
, but I could not find any that demonstrated a checked ListBox
, so I came up with my own.
Using the Code
The My.PropertyGridControls.CheckedListBoxEditor
custom property editor is used in conjunction with a custom properties class bound to a property grid.

Imports System
Imports System.ComponentModel
Imports System.Windows.Forms.Design
Imports System.Windows.Forms
Imports System.Collections.Specialized
Namespace My.PropertyGridControls
#Region "CheckedListBoxEditor"
''' <summary>
''' The control displayed in the property grid
''' </summary>
''' <remarks></remarks>
Public Class CheckedListBoxEditor
''' <summary>
''' The default text displayed in the property grid "value" column
''' </summary>
''' <remarks></remarks>
Private _strValue As String = "(Collection)"
''' <summary>
''' Creates a custom property editor using the CheckedListBoxEditor class
''' as the UITypeEditor
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
<Description("This property contains the checked ListBox collection."), _
EditorAttribute(GetType(CheckedListBoxUITypeEditor), _
GetType(System.Drawing.Design.UITypeEditor))> _
Public Property CheckedListBoxCollectionProperty() As String
Get
Return _strValue
End Get
Set(ByVal value As String)
_strValue = "(Collection)"
End Set
End Property
End Class
#End Region
#Region "CheckedListBoxUITypeEditor"
''' <summary>
''' Custom, editable checked ListBox control
''' </summary>
''' <remarks>This demo loads a comma-delimited string collection of URLs
''' and boolean values to set the checked state for each item.</remarks>
Public Class CheckedListBoxUITypeEditor
Inherits System.Drawing.Design.UITypeEditor
Public WithEvents cbx As New CheckedListBox
Private es As IWindowsFormsEditorService
''' <summary>
''' Override the UITypeEditorEditStyle to return the editor style:
''' drop-down, modal, or none
''' </summary>
''' <param name="context"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Overrides Function GetEditStyle_
(ByVal context As System.ComponentModel.ITypeDescriptorContext) _
As System.Drawing.Design.UITypeEditorEditStyle
'returns the editor style: drop-down, modal, or none
Return System.Drawing.Design.UITypeEditorEditStyle.DropDown
End Function
''' <summary>
''' Override whether or not the ListBox control should be resizable
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Overrides ReadOnly Property IsDropDownResizable() As Boolean
Get
'if set to true, adds a grip to the lower left portion of the ListBox,
'which makes the ListBox resizable as run time
Return True
End Get
End Property
''' <summary>
''' Override the default method for editing values in the ListBox
''' </summary>
''' <param name="context"></param>
''' <param name="provider"></param>
''' <param name="value"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Overrides Function EditValue_
(ByVal context As System.ComponentModel.ITypeDescriptorContext, _
ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
'instantiate the custom property editor service provider
es = DirectCast(provider.GetService(GetType(IWindowsFormsEditorService)), _
IWindowsFormsEditorService)
If es IsNot Nothing Then
'load the ListBox items
LoadListBoxItems()
'sort the items
cbx.Sorted = True
'show the control
es.DropDownControl(cbx)
End If
'ensure function returns a value on all code paths
Return Nothing
End Function
''' <summary>
''' Save the ListBox key/value pairs to My.Settings.UrlList
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub bx_Leave(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cbx.Leave
'clear the old list
My.Settings.UrlsList.Clear()
With cbx
'load the ListBox key/value pairs
For i As Integer = 0 To .Items.Count - 1
Dim txt As String = .Items(i).ToString
Dim chk As String = .GetItemChecked(i).ToString
'concatenate the key/value pair
Dim combined As String = LCase(txt) & "," & LCase(chk)
If .Items(i).ToString IsNot "" Then
'add the concatenated string to the "UrlsList" string collection
My.Settings.UrlsList.Add(combined)
End If
Next
End With
'save the config file
My.Settings.Save()
End Sub
''' <summary>
''' Loads My.Settings.UrlList comma-delimited string collection
''' into the custom collection editor.
''' </summary>
''' <remarks></remarks>
Private Sub LoadListBoxItems()
'create an array list
Dim a As New ArrayList
'load the config file "UrlsList" string collection into the array
For Each s As String In My.Settings.UrlsList
'split the URL from the checked value
a.Add(Split(s, ","))
Next
'create a hashtable, so we can refer to the items in a key/value pair format
Dim h As New Hashtable
'load the array into the hashtable
For i As Integer = 0 To a.Count - 1
'add the first array item as the key, the second as the value
h.Add(CType(a.Item(i), Array).GetValue(0).ToString, _
CType(a.Item(i), Array).GetValue(1).ToString)
Next
'dispose of the array list
a = Nothing
'clear the ListBox items
cbx.Items.Clear()
'index the hashtable
For Each de As DictionaryEntry In h
'add the key/value pairs to the ListBox
cbx.Items.Add(de.Key, CBool(de.Value))
Next
'dispose of the collection
h = Nothing
End Sub
End Class
#End Region
End Namespace
A StringCollection
from the app.config settings file is used to load the items displayed in the ListBox
. My.Settings.UrlsList
items are stored as comma-delimited key/value pairs.
<userSettings>
<CheckedListBoxCollectionEditor.My.MySettings>
<setting name="UrlsList" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>http://www.thefreedictionary.com/,true</string>
<string>http://medical-dictionary.thefreedictionary.com/,
true</string>
<string>http://legal-dictionary.thefreedictionary.com/,true</string>
<string>http://financial-dictionary.thefreedictionary.com/,
true</string>
<string>http://acronyms.thefreedictionary.com/,true</string>
<string>http://idioms.thefreedictionary.com/,true</string>
<string>http://encyclopedia2.thefreedictionary.com/,true</string>
<string>http://encyclopedia.thefreedictionary.com/,true</string>
<string>http://www.m-w.com/dictionary/,true</string>
<string>http://www.freedictionary.org/search/,true</string>
<string>http://www.yourdictionary.com/,true</string>
<string>http://en.wikipedia.org/wiki/,true</string>
</ArrayOfString>
</value>
</setting>
</CheckedListBoxCollectionEditor.My.MySettings>
</userSettings>
To use the control:
- Create a form
- Add a property grid
- Instantiate the custom property editor, and
- Bind it to the property grid
Remember to change the values in My.Settings.UrlsList
accordingly. You can also replace LoadListBoxItems()
with your own ListBox
item-loading function.
''' <summary>
''' Sample form with a property grid whose selected object is the custom ListBox editor
''' </summary>
''' <remarks></remarks>
Public Class Form1
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'create the custom checked ListBox editor
Dim c As New My.PropertyGridControls.CheckedListBoxEditor
'bind it to the property grid
Me.PropertyGrid1.SelectedObject = c
End Sub
End Class

The ListBox
is made resizable by overriding the IsDropDownResizable()
property, which is set to True
in the demo.
''' <summary>
''' Override whether or not the ListBox control should be resizable
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Overrides ReadOnly Property IsDropDownResizable() As Boolean
Get
Return True
End Get
End Property

If you prefer a non-resizable ListBox
, simply change IsDropDownResizable()
to False
.
''' <summary>
''' Override whether or not the ListBox control should be resizable
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Overrides ReadOnly Property IsDropDownResizable() As Boolean
Get
'if set to true, adds a grip to the lower left portion of the ListBox,
'which makes the ListBox resizable as run time
Return False
End Get
End Property

To Do
- Allow in-place editing of the text strings
- Add custom "add record" control to the bottom of the
ListBox
History
- Initial release