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
- 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
- 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.
- 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.
- 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
- 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
- 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...
- 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
- 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
- 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.
- 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
- 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
Public Class MCCB
Public Enum LFStyle
SameAsControl = 0
AlwaysRegular = 1
End Enum
Public Enum LayOrder
Normal = 0
BringToFront = 1
SendToBack = 2
End Enum
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
Private _LayerOrder As Byte = 0
Private _SelectedIndex As Integer = -1
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
Private _FNIlastIdx As Integer = -1
Private _FNIlastStr As String = ""
Private _FNI_iFrom As Integer = -1
Private _Error As String = "Error"
- 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
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
- 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
- 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
- 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
Return _MaxItemsDisp
End Get
Set(ByVal value As Byte)
_MaxItemsDisp = value
ShowHideList(True)
End Set
End Property
- 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:
- Width - Required. The Width of the Column, in pixels. A value of 0 will create a hidden Column.
- Text - Required. The title (Header) of the Column.
- 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
Hdr = Hdr0
Box = Box0
MyLV.Width = Width + 19
lvCol0.Width = Width - 3
_TotalWidth = Width - 1
Me.BackColor = Color.Transparent
Else
Hdr = New Label
Hdr.Name = "Hdr" & _ColumnIndex.ToString
Hdr.Left = _TotalWidth
Box = New Label
Box.Name = "Box" & _ColumnIndex.ToString
Box.Left = _TotalWidth
MyLV.Columns.Add(New ColumnHeader)
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
_TotalWidth += Width - 1
End If
Hdr.Width = Width
Hdr.Text = Text
Hdr.TextAlign = Lbl_Align
Box.Width = Width
Box.Text = ""
Box.TextAlign = Lbl_Align
LblDrop.Left = _TotalWidth
LblDrop.Height = _BoxHeight
If _ColumnCount > 1 Then
Controls.AddRange({Box, Hdr})
End If
Me.ResumeLayout(True)
RedrawControls()
End Sub
Example of how to call this Method:
Mccb1.AddColumn(140, "Name")
Mccb1.AddColumn(35, "Age", HorizontalAlignment.Center)
Mccb1.AddColumn(90, "Due date", 2)
Mccb1.AddColumn(90, "Amount (US$)", HorizontalAlignment.Right)
- 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
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
Me.ResumeLayout(True)
End Sub
- 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
- 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
End If
- 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)
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
_FNI_iFrom += 1
Else
_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
Found = True
_SelectedIndex = iCtr
FoundItem = GetSubItems()
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)
RaiseEvent ItemSelectionChanged(GetSubItems)
End If
End Sub
- 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)
End Sub
Public Sub DropDown()
ShowHideList(True)
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:
- Copy MCCB.dll to its StartupPath ( \bin\Debug or \bin\Release within the Project ) , and after to the Folder of your final .EXE
- In Visual Studio, Add a Reference: Menu PROJECT - Add Reference... - Browse... - MCCB.dll
- 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
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