65.9K
CodeProject is changing. Read more.
Home

MCCB - Multi Column ComboBox - How to create a simple (UserControl) DLL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (14 votes)

Mar 9, 2016

CPOL

12 min read

viewsIcon

23732

downloadIcon

1449

Remembering the first time, being already an experienced programmer in VB and VBA, I felt the need to build my first reusable DLL.

First of all

CodeProject is for programmers of all levels. This article is intended only for beginners and VB programmers who have never built a DLL. For a more complex ComboBox, see my first article: ExCB - Extended Multi Column ComboBox

Introduction

The downloadable files contain complete projects, but I'll show the basic steps in creating this Control, one ComboBox able to permanently display one or more Columns (say 'field boxes', labeled or not) and a List for 'record' selection.

A DLL is often a Control, made up with other System.Windows.Form Controls, created by the user for a particular purpose, often flexible, parametrizable, and ready to execute several tasks.

Like any other Control, a UserControl has its own Properties, Methods and Events, some of them created by us.

When developing a DLL, it's good practice to have another simple WinForms project for testing the Control, both projects in a Solution, so we can deeply test and debug the Control's behavior.

Building the UserControl

  1. Open Visual Studio, Start a New Project, choose Class Library and give it the Name MCCB, which will be the Project name, the Assembly name and the Root namespace - but VS gives you a VB Public Class Class1
  2. Menu PROJECT, choose Add User Control and give it the Name MCCB, which will be the future Name of your Control - VS creates another Class and shows the template - a 150x150 square, more like a Panel than like a Form...

In this example, the goal is to create a ComboBox that expose permanently one or more columns, with labels. We create the first column, all others will be created at runtime. We also need a drop-down List and a Button for opening the List.

  1. Resize the Control to appx. 200x200 and change its BackColor to any other (slight). At runtime, this color will be changed to Color.Control, but at design time we need a visual reference, when resizing the Control.
  2. Lets start with the Label (Title/Header): Pick a Label from the Toolbox. Change the following properties: Name="Hdr0", AutoSize=False, BackColor=WhiteSmoke, BorderStyle=FixedSingle, Location=0;0, Size=100;19, Text="Hdr0", TextAlign=MiddleLeft
  3. Now the Box to show the field value. Instead of a TextBox, we'll use a Label... is a lighter control, furthermore, values are not to be written or changed. Pick another Label and change the following properties: Name="Box0", AutoSize=False, BackColor=White, BorderStyle=FixedSingle, ForeColor=Navy, Location=0;18, Size=100;20, Text="Box0", TextAlign=MiddleLeft
  4. Next, the List. Pick a ListView and change the properties: Name="MyLV", View=Details, BorderStyle=FixedSingle, FullRowSelect=True, GridLines=True, HeaderStyle=None, Location=0;37, MultiSelect=False, Size=119;138. Its a List with 8 Rows, without Header ... We'll also create the first Column: Add a Column and change the properties: Name="lvCol0", Text="", Width=97. Why 97? To perfectly align the lines, the ListView Column's Width must be the Width of the corresponding Label/Box minus 1, except the first Column, which must be minus 3...
  5. Finally, the Button, which must show any kind of "down arrow". It can be a Button, a PictureBox, even a simple Label. Lets do it with a Label. Pick a Label and change properties: Name="LblDrop", AutoSize=False, BackColor=WhiteSmoke, BorderStyle=FixedSingle, Location=99;18, Size=20;20, Text="". In property Image - Local resource - Import: DArrow.png, located in folder .\Image

  1. Everything is OK and aligned, right? But when the Control is to be placed on a Form, the List must be hidden... So, to prevent vertical resizing at design time, and because the actual height of Header+Box is 38, add this very first code, in the UserControl's Resize Event:
Private Sub MCCB_Resize(sender As Object, e As EventArgs) Handles Me.Resize
	Me.Height = 38
End Sub
  1. Its time to create the project for testing and debugging the Control. Save the Project and start a New Project of the type Windows Forms Application, naming it as MCCB_Test - VS gives you a Form template (Form1). Save this 2nd project.
  2. Add the UserControl project: Menu FILE - Add - Existing Project - Browse MCCB project and open MCCB.vbproj - A Solution 'MCCB_Test' is created, with both projects in Solution Explorer. Select MCCB project, Menu BUILD - Build MCCB. Open the Toolbox and look at the top ... The Control MCCB is already there. Double-click it - the Control is placed in the Form, with the Name Mccb1. Resize it - as you can see, only horizontal resizing is allowed. Start the Solution - Great, the Control is there!!! Now you can manage both projects. Insert a piece of code, Menu BUILD - Rebuild MCCB, if necessary check or change Control Properties in Form1, define breakpoint(s) for Debug porposes, if needed, and Start the Solution. Easy.

We're about to create a set of Properties. Usually, Properties must be Public and specify its Data Type. Custom Properties values can be viewed and changed, like any other Property in the IDE Properties Window, or at runtime. All of our Properties, except one, are likely to be read and altered in the IDE Properties Window. Some of them will alter the Control's appearance: Height of Header(s) and Box(es), BorderStyle, ForeColor and BackColor of Header(s), Box(es) and List. These Properties imply the Control to be redrawn, we'll see later. They have their defined Data Types - Integer, Byte, Color, BorderStyle - which can be changed with pre-defined Dialogs, ComboBoxes or TextBoxes. But, although the properties ListFontStyle and LayerOrder deal with Byte values, instead of a simple TextBox for typing a digit, we prefer a ComboBox with descriptive text for the allowed values. For that, we have to declare Data Types of our Enumeration lists. Finally, property SelectedIndex is to be used only at runtime, i.e., must be non-browsable and hidden. See all Properties here

  1. Select and Open MCCB.vb Code tab. Lets Import a reference, needed to specify some Properties Attributes, like Category, Description, Browsable or DesignerSerializationVisibility, and declare two Enum types and a couple of Variables - some for Property values (the _Name of the variable is the Name of the Property, preceded by an underscore), some for internal use:
Imports System.ComponentModel   ' For PropertyAttributes

Public Class MCCB

   Public Enum LFStyle         ' for Property ListFontStyle
        SameAsControl = 0
        AlwaysRegular = 1
  End Enum
    Public Enum LayOrder        ' for Property LayerOrder
        Normal = 0
        BringToFront = 1
        SendToBack = 2
   End Enum
    ' Property variables
    Private _MaxItemsDisp As Integer = 8
    Private _HeaderHeight As Byte = 19
    Private _HeaderBackColor As Color = Color.WhiteSmoke
    Private _HeaderForeColor As Color = Color.Black
    Private _HeaderBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _BoxHeight As Byte = 20
    Private _BoxBackColor As Color = Color.White
    Private _BoxForeColor As Color = Color.MediumBlue
    Private _BoxBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _ListBackColor As Color = Color.White
    Private _ListForeColor As Color = Color.Black
    Private _ListBorderStyle As BorderStyle = Windows.Forms.BorderStyle.FixedSingle
    Private _ListFontStyle As Byte = 0      ' 0-SameAsControl, 1-AlwaysRegular
    Private _LayerOrder As Byte = 0         ' 0-Normal, 1-BringToFront, 2-SendToBack
    Private _SelectedIndex As Integer = -1
    ' Internal variables
    Private _ColumnCount As Integer = 0
    Private _ColumnIndex As Integer = 0
    Private _TotalWidth As Integer = 99
    Private _LeftLocation As Integer = 0
    Private _ItemsCount As Integer = 0
    Private _Me_MinHeight As Integer = _HeaderHeight + _BoxHeight - 1
    Private _ListHeight As Integer = 0
    Private _ShowList As Boolean = False    ' used in Resize event
    Private _FNIlastIdx As Integer = -1     ' Last ColumnIndex searched
    Private _FNIlastStr As String = ""      ' Last String searched
    Private _FNI_iFrom As Integer = -1
    Private _Error As String = "Error"
  1. As said before, most Properties imply the Control to be redrawn. And redraw the Control implies the Resize Event to be raised. Resize Event will be also called whenever the List is dropped down or hidden, i.e., when the 'button' LblDrop is clicked. So, Resize Event must deal with two different situations: 1) The List is to be dropped down (if there are Items to show), with its correct Height. 2) The list is to be hidden. And for controlling its behavior, we created a variable _ShowList, whose initial value is False, thus ensuring the correct scaling in design mode. So, let's change the UserControl's Resize Event. Note that _Me_MinHeight = _HeaderHeight + _BoxHeight - 1, so _Me_MinHeight represents always the (minimum) height of the Control, without List, and the height of the List is the number of rows to display, each one 17 pixels [16 (row height) + 1 (separator line)] plus 1 (last line):
    Private Sub MCCB_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        'Me.Height = 38     ' Replaced by the code below:
        If _ShowList And _ItemsCount > 0 Then
            _ListHeight = Math.Min(_ItemsCount, _MaxItemsDisp) * 17 + 1
        Else
            _ListHeight = 0
        End If
        Me.Height = _Me_MinHeight + _ListHeight
        MyLV.Height = _ListHeight
    End Sub
  1. Resize Event is to be called (also) from several other situations, but the variable _ShowList must be set to the desired value. So, let's create a unique Sub that sets the value of the variable and invokes the Resize Event, which will be called in each of these situations:
    Private Sub ShowHideList(Show As Boolean)
        _ShowList = Show
        MCCB_Resize(Nothing, Nothing)
    End Sub
  1. Now, lets provide a mechanism to Drop-down or Hide the List, when the label LblDrop is Clicked. And more, when the Mouse passes over the label, it's BackColor becomes Orange and the Cursor changes to Hand. So, let's use the Events MouseHover, MouseLeave and Click:
    Private Sub LblDrop_MouseHover(sender As Object, e As EventArgs) Handles LblDrop.MouseHover
        Me.Cursor = Cursors.Hand
        sender.BackColor = Color.Orange
    End Sub
    Private Sub LblDrop_MouseLeave(sender As Object, e As EventArgs) Handles LblDrop.MouseLeave
        Me.Cursor = Cursors.Default
        sender.BackColor = Color.WhiteSmoke
    End Sub
    Private Sub LblDrop_Click(sender As Object, e As EventArgs) Handles LblDrop.Click
        ShowHideList(Show:=(Me.Height <= _Me_MinHeight))
    End Sub
  1. It's time to create our first Property. By default, Properties are readable (the Get part) and writable (the Set path). However, they can also be declared as ReadOnly (only Get part) or WriteOnly (only Set part). Also, some Attributes can be specified. In this case, Category (existing or new, where the Property is to be included) and Description (the explanation that appears at the bottom of the Properties Window). For reading a property, Return the value of the associated variable. For writing a property, set it's associated variable to the specified value, and, if the value is to take immediate effect, code the action. In this case, just a call to the Sub ShowHideList, with the argument True, to set the variable _ShowList and Drop-down the List:
<Category("_MCCB specifics")> _
<Description("Maximum Items Displyed by the List")> _
    Public Property MaxItemsDisp() As Byte
        Get
            ' Get - if the Property is readable (or ReadOnly)
            Return _MaxItemsDisp
        End Get
        Set(ByVal value As Byte)
            ' Set - if the Property is writable (or WriteOnly)
            _MaxItemsDisp = value
            ShowHideList(True) ' Action
        End Set
    End Property
  1. For now, let's create our first Method, which will allow adding Columns and set some (fixed) Properties. To be accessible from the outside, the Method must be Public. The first call to this Method will not create a new Column, but change the one already existing. The Method will accept three parameters:
  1. Width - Required. The Width of the Column, in pixels. A value of 0 will create a hidden Column.
  2. Text - Required. The title (Header) of the Column.
  3. Align - Optional. The Horizontal Alignment of the Column. Default (if not specified) is Left. HorizontalAlignment values (0=Left, 1=Right, 2=Center) are used to define the TextAlign property of ListView Columns (or, e.g., TextBoxes). However, Labels use other values (ContentAlignment), which besides the Horizontal position, also reflects the Vertical position (Top, Middle, Bottom). Our 'Headers' and 'Boxes' are to be 'middle' aligned, so HorizontalAlignment values (0, 1 and 2) must be transformed in 'Middle' ContentAlignment values (respectively 16, 64 and 32).
    Public Sub AddColumn(Width As Integer, Text As String, _
                         Optional Align As HorizontalAlignment = 0)
        Dim Lbl_Align As ContentAlignment = If(Align = 1, 64, _
                                               If(Align = 2, 32, 16))
        Dim Hdr As Label
        Dim Box As Label
        Me.SuspendLayout()
        _ColumnCount += 1
        _ColumnIndex = _ColumnCount - 1
        If _ColumnCount = 1 Then   ' First Column, already exists
            Hdr = Hdr0                          ' Existing Header
            Box = Box0                          ' Existing Box
            MyLV.Width = Width + 19             ' Existing List. Initial
            ' value, including the "Button" (LblDrop)
            lvCol0.Width = Width - 3            ' Existing ListView Column
            _TotalWidth = Width - 1             ' Initial value
            Me.BackColor = Color.Transparent
        Else                        ' Column is to be created
            Hdr = New Label                         ' New Header
            Hdr.Name = "Hdr" & _ColumnIndex.ToString
            Hdr.Left = _TotalWidth
            Box = New Label                         ' New Box
            Box.Name = "Box" & _ColumnIndex.ToString
            Box.Left = _TotalWidth
            MyLV.Columns.Add(New ColumnHeader)      ' New ListView Column
            MyLV.Columns(_ColumnIndex).Name = "lvCol" & _ColumnIndex.ToString
            MyLV.Columns(_ColumnIndex).Text = ""
            MyLV.Columns(_ColumnIndex).Width = Math.Max(Width - 1, 0)
            MyLV.Columns(_ColumnIndex).TextAlign = Align
            MyLV.Width += Width - 1     ' Accum.
            _TotalWidth += Width - 1    ' Accum.
        End If
        Hdr.Width = Width           ' Header
        Hdr.Text = Text
        Hdr.TextAlign = Lbl_Align
        Box.Width = Width           ' Box
        Box.Text = ""
        Box.TextAlign = Lbl_Align
        LblDrop.Left = _TotalWidth  ' DropDown "Button"
        LblDrop.Height = _BoxHeight
        If _ColumnCount > 1 Then    ' All but first
            Controls.AddRange({Box, Hdr})
        End If
        Me.ResumeLayout(True)
        RedrawControls()
    End Sub

Example of how to call this Method:

Mccb1.AddColumn(140, "Name")    ' Align not specified = HorizontalAlignment.Left
Mccb1.AddColumn(35, "Age", HorizontalAlignment.Center)
Mccb1.AddColumn(90, "Due date", 2)  ' 2 = HorizontalAlignment.Center
Mccb1.AddColumn(90, "Amount (US$)", HorizontalAlignment.Right)
  1. As said before, AddColumn Method creates new Columns, setting up their unalterable Properties. But to properly complete the Components setup, we have to initialize the Properties that can be changed at any time. Therefore, we'll create a specific (Private) Subroutine (RedrawControls), which is also called by the AddColumn Method (last instruction): Note that, because the Size of the Control is changed (Me.Height & Me.Width), the Control's Resize Event will be raised:
    Private Sub RedrawControls()
        Me.SuspendLayout()
        MyLV.Top = _HeaderHeight + _BoxHeight - 2
        MyLV.BackColor = _ListBackColor
        MyLV.ForeColor = _ListForeColor
        MyLV.BorderStyle = _ListBorderStyle
        Dim Name As String
        For iCtr As Integer = 0 To _ColumnIndex ' For each Column...
            Name = "Box" & iCtr.ToString
            Controls(Name).Top = _HeaderHeight - 1
            Controls(Name).Height = _BoxHeight
            Controls(Name).BackColor = _BoxBackColor
            Controls(Name).ForeColor = _BoxForeColor
            Dim Lbl As Label = CType(Controls(Name), Label)
            Lbl.BorderStyle = _BoxBorderStyle
            Name = "Hdr" & iCtr.ToString
            Controls(Name).Height = _HeaderHeight
            Controls(Name).BackColor = _HeaderBackColor
            Controls(Name).ForeColor = _HeaderForeColor
            Lbl = CType(Controls(Name), Label)
            Lbl.BorderStyle = _HeaderBorderStyle
        Next
        LblDrop.Top = _HeaderHeight - 1
        LblDrop.Height = _BoxHeight
        _Me_MinHeight = _HeaderHeight + _BoxHeight - 1
        Me.Height = _Me_MinHeight
        Me.Width = _TotalWidth + 20 ' 20=LblDrop.Width
        Me.ResumeLayout(True)
    End Sub
  1. Look at the provided Source Project. Code or copy all the other Properties which call RedrawControls. Special attention to two Properties whose action is different: The first, ListFontStyle, whose Type is defined by the Enum LFStyle, sets the variable _ListFontStyle and calls the Event (FontChanged) that occurs whenever the Font changes. The second, LayerOrder, whose Type is defined by the Enum LayOrder, sets the variable _LayerOrder, but its action is taken before, depending on its previous value. Why? Because, if a and b offer no doubts, setting the value to 0 (Normal) implies to change the last behavior. That action must also be taken when the Control is loaded, in the Control's Load Event. Let's see the Load and the FontChanged Events, and the two Properties:
    Private Sub MCCB_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        If _LayerOrder = 1 Then
            Me.BringToFront()
        ElseIf _LayerOrder = 2 Then
            Me.SendToBack()
        End If
    End Sub
    Private Sub MCCB_FontChanged(sender As Object, e As EventArgs) Handles Me.FontChanged
        MyLV.Font = New Font(Me.Font, If(_ListFontStyle = 0, _
                                        Me.Font.Style, FontStyle.Regular))
    End Sub
    <Category("_MCCB specifics")> _
    <Description("Action in List when Control's Font changes to Bold")> _
    Public Property ListFontStyle() As LFStyle
        Get
            Return _ListFontStyle
        End Get
        Set(ByVal value As LFStyle)
            _ListFontStyle = value
            MCCB_FontChanged(Nothing, Nothing)
        End Set
    End Property
    <Category("_MCCB specifics")> _
    <Description("Control's Layer Order Action")> _
    Public Property LayerOrder() As LayOrder
        Get
            Return _LayerOrder
        End Get
        Set(ByVal value As LayOrder)
            If value = 1 OrElse (value = 0 And _LayerOrder = 2) Then
                Me.BringToFront()
            ElseIf value = 2 OrElse (value = 0 And _LayerOrder = 1) Then
                Me.SendToBack()
            End If
            _LayerOrder = value
        End Set
    End Property
  1. Let's create a Method to feed the List. It requires an array of Strings, one for each SubItem, including any hidden Columns. It's simple, because such array is directly convertible in the required ListViewItem type. A Boolean is returned, indicating the Success (True) or Failure (False) of the operation:
    Public Function AddRow(SubItems As Array) As Boolean
        Dim OK As Boolean = False
        If SubItems.Length > _ColumnIndex + 1 Then
            MsgBox("SubItems (" & SubItems.Length.ToString & _
                   ") exceeds the number of Columns (" & _
                   (_ColumnIndex + 1).ToString & ")...", _
                   MsgBoxStyle.Critical, _Error)
        Else
            Try
                MyLV.Items.Add(New ListViewItem(SubItems))
                _ItemsCount += 1
                OK = True
            Catch ex As Exception
                MsgBox(ex.Message, MsgBoxStyle.Critical, _Error)
            End Try
        End If
        Return OK
    End Function

Example of how to call this Method:

Dim vAge As Byte = 35
Dim vDate As String = Today.ToShortDateString
Dim vValue As Single = 1234.56
If Mccb1.AddRow({"John Smith", vAge.ToString, vDate, Format(vValue, "#,###.00")}) = False Then
   ' Something went wrong...
End If
  1. When a List Row is selected, the SubItem(s) of the Row must be shown in the corresponding Box(es). To perform the task, let's create a Sub ShowSelectedItem, as well as the three situations where the task is to be performed: 1-Our Control's Property (Non-browsable and hidden) SelectedIndex, 2-our Control's Method FindNextItem and 3-the ListView Event ItemSelectionChanged., which fires our Control's Event (ItemSelectionChanged) (code fragment #1) that is to be raised whenever a new Row is selected and provides an array of Strings with the values of all the SubItems of the selected Row, also a simple Sub () (code fragment #2) used to display an error message when an invalid negative index is detected, and a Function (GetSubItems) that prepares the above mentioned array of Strings, also used in Method FindNextItem:
    Public Event ItemSelectionChanged(SubItems As Array)
    Private Sub MsgNoNeg(Prefx As String)
        MsgBox(Prefx & " cannot be negative...", _
               MsgBoxStyle.Critical, _Error)
    End Sub
    Private Sub ShowSelectedItem()
        Dim BoxName As String
        For iCtr As Integer = 0 To _ColumnIndex
            BoxName = "Box" & iCtr.ToString
            Controls(BoxName).Text = MyLV.Items(_SelectedIndex).SubItems(iCtr).Text
        Next
    End Sub
    Private Function GetSubItems() As Array
        Dim SubItems(_ColumnIndex) As String
        For iSI As Integer = 0 To _ColumnIndex
            SubItems(iSI) = MyLV.Items(_SelectedIndex).SubItems(iSI).Text
        Next
        Return SubItems
    End Function
     <Browsable(False)> _
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
    Public Property SelectedIndex() As Integer
        Get
            Return _SelectedIndex
        End Get
        Set(ByVal value As Integer)
            If value < 0 Then
                MsgNoNeg("Index")
            Else
                If value >= _ItemsCount Then
                    MsgBox("Last Index = " & (_ItemsCount - 1).ToString, _
                           MsgBoxStyle.Critical, _Error)
                Else
                    If _SelectedIndex >= 0 Then
                        MyLV.Items(_SelectedIndex).Selected = False
                    End If
                    _SelectedIndex = value
                    MyLV.Items(_SelectedIndex).Selected = True
                    ShowSelectedItem()
                    ShowHideList(Show:=False)    'Close List
                End If
            End If
        End Set
    End Property
    Public Function FindNextItem(ColumnIndex As Integer, SearchString As String) As Array
        Dim FoundItem(_ColumnIndex) As String
        If ColumnIndex >= 0 Then
            If ColumnIndex > _ColumnIndex Then
                MsgBox("Maximum ColumnIndex is " & _ColumnIndex.ToString & "...", _
                       MsgBoxStyle.Critical, _Error)
            Else
                If ColumnIndex = _FNIlastIdx And SearchString = _FNIlastStr Then
                    ' Same values, serch next
                    _FNI_iFrom += 1
                Else
                    ' New values, search first
                    _FNI_iFrom = 0
                    _FNIlastIdx = ColumnIndex
                    _FNIlastStr = SearchString
                End If
                If _FNI_iFrom >= 0 Then

                    Dim Found As Boolean = False
                    Dim iCtr As Integer
                    For iCtr = _FNI_iFrom To _ItemsCount - 1
                        If Strings.InStr(MyLV.Items(iCtr).SubItems(ColumnIndex).Text, _
                                         SearchString, CompareMethod.Text) > 0 Then
                            ' Item found...
                            Found = True
                            _SelectedIndex = iCtr
                            FoundItem = GetSubItems()
                            ' Select Item
                            If _SelectedIndex >= 0 Then
                                MyLV.Items(_SelectedIndex).Selected = False
                            End If
                            MyLV.Items(_SelectedIndex).Selected = True
                            ShowSelectedItem()
                            ShowHideList(True)
                            Exit For
                        End If
                    Next
                    _FNI_iFrom = iCtr
                    If Found = False And iCtr = _ItemsCount Then
                        MsgBox("No match...", MsgBoxStyle.Information, "Info")
                        FoundItem = Nothing
                    End If
                End If
            End If
        Else
            MsgNoNeg("ColumnIndex")
        End If
        Return FoundItem
    End Function
    Private Sub MyLV_ItemSelectionChanged(sender As Object, e As ListViewItemSelectionChangedEventArgs) _
                Handles MyLV.ItemSelectionChanged
        Dim FoundItem(_ColumnIndex) As String
        If e.IsSelected Then
            _SelectedIndex = e.ItemIndex
            ShowSelectedItem()
            ShowHideList(False)     ' Close List
            RaiseEvent ItemSelectionChanged(GetSubItems)
        End If
    End Sub
  1. Just two more simple Methods, Clear and DropDown:
    Public Sub Clear()
        MyLV.Items.Clear()
        _ItemsCount = 0
        For iCtr As Integer = 0 To _ColumnIndex
            Controls("Box" & iCtr.ToString).Text = ""
        Next
        ShowHideList(False)     ' Close the List
    End Sub
    Public Sub DropDown()
        ShowHideList(True)      ' Drop-down the List
    End Sub

That's all. I organized the Control's Code in Regions:

Imports
Enums & Variables
Public Properties
Public Methods
Public Event
Control Events
Private Subs/Functions
Initialization

#Region "Initialization" is the code generated by the Designer.

Finally, in your WinForms Application:

  1. Copy MCCB.dll to its StartupPath ( \bin\Debug or \bin\Release within the Project ) , and after to the Folder of your final .EXE
  2. In Visual Studio, Add a Reference: Menu PROJECT - Add Reference... - Browse... - MCCB.dll
  3. Put an Icon ( ) in the Toolbox: Menu TOOLS - Choose Toolbox Items... - .NET Framework Components - Browse... - MCCB.dll . The Icon will appear under All Windows Forms group.

Brief description of the Control's own Properties, Methods and Event

Properties

  • BoxBackColor - Back Color of the Box(es).
  • BoxBorderStyle - Border Style of the Box(es).
  • BoxForeColor - Fore Color of the Box(es).
  • BoxHeight - Height of the Box(es).
  • HeaderBackColor - Back Color of the Header(s).
  • HeaderBorderStyle - Border Style of the Header(s).
  • HeaderForeColor - Fore Color of the Header(s).
  • HeaderHeight - Height of the Header(s).
  • LayerOrder - Control's Layer Order Action.
  • ListBackColor - Back Color of the List.
  • ListBorderStyle - Border Style of the List.
  • ListFontStyle - Action in List when Control's Font changes to Bold.
  • ListForeColor - Fore Color of the List.
  • MaxItemsDisp - Maximum Items Displayed by the List.
  • SelectedIndex - Index of the Item that is (or is to be) Selected in the List.

Methods

  • AddColumn(Width As Integer, Text As String, [Align As HorizontalAlignment = HorizontalAlignment.Left]) - Adds a Column, given its Width (in pixels), its (header)Text and its Alignment (Left, Center, Right).
  • AddRow(SubItems As Array) As Boolean - Adds a Row to the List, given an array of Strings. Returns a Boolean indicating Success (True) or Failure (False) of the operation.
  • Clear - Clears the List and the Box(es) of the Control.
  • DropDown - Drops Down the List.
  • FindNextItem(ColumnIndex As Integer, SearchString As String) As Array - Finds the next occurrence of a given String within the values of a Column, given its Index. The Row where the match occurs is selected and an array of its SubItems is returned. When no more matches, the array is Nothing, but the last selected Row remains selected.

Event

  • ItemSelectionChanged(SubItems As Array) - Occurs whenever a new Row is selected from the List.

History

09.Mar.2016 - First post