2009-03-03

Display the drop down list of Combo box Explicitly

When you set the DropDownStyle property of Combo box, it allows you input the text like text box control. When you type something and press down arrow key then the item from the list which starts from the text entered is selected. Imagine, how helpful it would be if we can see the drop down list while typing. Below is the code which does exactly the same.

In this code I am displaying the drop down list on GotFocus event of ComboBox.

Imports System.Runtime.InteropServices

Public Class Form1

_
Private Shared Function SendMessage( _
ByVal hWnd As IntPtr, _
ByVal Msg As UInteger, _
ByVal wParam As IntPtr, _
ByVal lParam As IntPtr) As IntPtr
End Function

Public Const CB_SHOWDROPDOWN As Long = &H14F

Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load

Dim comboItems() As String
comboItems = New String() {"the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog"}

With ComboBox1
.Items.Clear()
.Items.AddRange(comboItems)
End With
End Sub

Private Sub ComboBox1_GotFocus( _
ByVal sender As Object, _
ByVal e As System.EventArgs _
) Handles ComboBox1.GotFocus

Dim iret As IntPtr
iret = SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, New IntPtr(CInt(True)), IntPtr.Zero)
End Sub

End Class
2009-02-17

Tabless Tab Control

Did you ever came across situations where you wanted to develop interfaces like in below images.





In some of my projects I wanted to develop UI like the images displayed below, in some projects I needed a wizard interface where in I just wanted to hide tab headings and display the appropriate tabs when needed.

When I thought about such interface, the first control that came in mind was obviously Tab Control that comes with .NET by default. But there no property to hide tab headings in Tab Control. After some digging I managed to remove the tab headings but the result was not up to my expectations. So I started to find out alternatives and came across this website.

Author Mick Doherty has posted a custom control which helped me to design the interface that I imagined. He call the control as PanelManager.



It works somewhat like Tab Control but doesn't has tab headings. When you drop PanelManager on the form in design mode then it contains two panels by default. Once the PanelManager is dropped on the form you can design the UI just like we do in Tab control. You can then change the current panel through SelectedPanel property from property browser.

Code of the PanelManager control is given below. Please note that you'll need to add a reference to System.Design.dll. Once you compile the control it becomes available in Toolbox.

PanelManager Code:
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Globalization
Imports System.Security.Permissions
Imports System.Runtime.InteropServices
Imports System.ComponentModel.Design

Namespace Controls

_
Public Class PanelManager
Inherits System.Windows.Forms.Control

#Region " Windows Form Designer generated code "

Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'UserControl1 overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Private Sub InitializeComponent()
components = New System.ComponentModel.Container
End Sub

#End Region

Private m_SelectedPanel As Controls.ManagedPanel

Public Event SelectedIndexChanged As EventHandler

'ManagedPanels
_
Public ReadOnly Property ManagedPanels() As ControlCollection
Get
Return MyBase.Controls
End Get
End Property

'SelectedPanel
_
Public Property SelectedPanel() As Controls.ManagedPanel
Get
Return m_SelectedPanel
End Get
Set(ByVal Value As Controls.ManagedPanel)
If m_SelectedPanel Is Value Then Return
m_SelectedPanel = Value
OnSelectedPanelChanged(EventArgs.Empty)
End Set
End Property

'SelectedIndex
_
Public Property SelectedIndex() As Int32
Get
Return Me.ManagedPanels.IndexOf(CType(Me.SelectedPanel, Controls.ManagedPanel))
End Get
Set(ByVal Value As Int32)
If Value = -1 Then
Me.SelectedPanel = Nothing
Else
Me.SelectedPanel = DirectCast(Me.ManagedPanels(Value), ManagedPanel)
End If
End Set
End Property

'DefaultSize
Protected Overrides ReadOnly Property DefaultSize() As System.Drawing.Size
Get
Return New Size(200, 100)
End Get
End Property

Protected Overridable Sub OnSelectedPanelChanged(ByVal e As EventArgs)
Static oldSelection As ManagedPanel = Nothing
If Not (oldSelection Is Nothing) Then
oldSelection.Visible = False
End If
If Not (m_SelectedPanel Is Nothing) Then
CType(m_SelectedPanel, Controls.ManagedPanel).Visible = True
End If
Dim tabChanged As Boolean
If m_SelectedPanel Is Nothing Then
tabChanged = Not (oldSelection Is Nothing)
Else
tabChanged = Not (m_SelectedPanel.Equals(oldSelection))
End If
If tabChanged And Me.Created Then
RaiseEvent SelectedIndexChanged(Me, EventArgs.Empty)
End If
oldSelection = CType(m_SelectedPanel, Controls.ManagedPanel)
End Sub

Protected Overrides Sub OnControlAdded(ByVal e As System.Windows.Forms.ControlEventArgs)
If Not (TypeOf e.Control Is Controls.ManagedPanel) Then
Throw New ArgumentException("Only Mangel.Controls.ManagedPanels can be added to a Mangel.Controls.PanelManger.")
End If
If Not (Me.SelectedPanel Is Nothing) Then
CType(Me.SelectedPanel, Controls.ManagedPanel).Visible = False
End If
Me.SelectedPanel = DirectCast(e.Control, Controls.ManagedPanel)
e.Control.Visible = True
MyBase.OnControlAdded(e)
End Sub

Protected Overrides Sub OnControlRemoved(ByVal e As System.Windows.Forms.ControlEventArgs)
If Not (TypeOf e.Control Is Controls.ManagedPanel) Then Return
If Me.ManagedPanels.Count > 0 Then
Me.SelectedIndex = 0
Else
Me.SelectedPanel = Nothing
End If
MyBase.OnControlRemoved(e)
End Sub

End Class

_
Public Class ManagedPanel
Inherits System.Windows.Forms.ScrollableControl

Public Sub New()
MyBase.Dock = DockStyle.Fill
setstyle(ControlStyles.ResizeRedraw, True)
End Sub

_
Public Overrides Property Dock() As System.Windows.Forms.DockStyle
Get
Return MyBase.Dock
End Get
Set(ByVal value As System.Windows.Forms.DockStyle)
MyBase.Dock = DockStyle.Fill
End Set
End Property

_
Public Overrides Property Anchor() As AnchorStyles
Get
Return AnchorStyles.None
End Get
Set(ByVal value As AnchorStyles)
MyBase.Anchor = AnchorStyles.None
End Set
End Property

Protected Overrides Sub OnLocationChanged(ByVal e As System.EventArgs)
MyBase.OnLocationChanged(e)
MyBase.Location = Point.Empty
End Sub

Protected Overrides Sub OnSizeChanged(ByVal e As System.EventArgs)
MyBase.OnSizeChanged(e)
If Me.Parent Is Nothing Then
Me.Size = Size.Empty
Else
Me.Size = Me.Parent.ClientSize
End If
End Sub

Protected Overrides Sub OnParentChanged(ByVal e As System.EventArgs)
If Not (TypeOf Me.Parent Is Controls.PanelManager) AndAlso Not (Me.Parent Is Nothing) Then
Throw New ArgumentException("Managed Panels may only be added to a Panel Manager.")
End If
MyBase.OnParentChanged(e)
End Sub

End Class

End Namespace

Namespace Design

Public Class PanelManagerDesigner
Inherits System.Windows.Forms.Design.ParentControlDesigner

Private m_verbs As DesignerVerbCollection = New DesignerVerbCollection
Private m_DesignerHost As IDesignerHost
Private m_SelectionService As ISelectionService

Private ReadOnly Property HostControl() As Controls.PanelManager
Get
Return DirectCast(Me.Control, Controls.PanelManager)
End Get
End Property

Public Sub New()
MyBase.New()

Dim verb1 As New DesignerVerb("Add MangedPanel", AddressOf OnAddPanel)
Dim verb2 As New DesignerVerb("Remove ManagedPanel", AddressOf OnRemovePanel)
m_verbs.AddRange(New DesignerVerb() {verb1, verb2})

End Sub

Protected Overrides Sub OnPaintAdornments(ByVal pe As System.Windows.Forms.PaintEventArgs)
'Don't want DrawGrid Dots.
End Sub

Public Overrides ReadOnly Property Verbs() As System.ComponentModel.Design.DesignerVerbCollection
Get
If m_verbs.Count = 2 Then
If HostControl.ManagedPanels.Count > 0 Then
m_verbs(1).Enabled = True
Else
m_verbs(1).Enabled = False
End If
End If
Return m_verbs
End Get
End Property

Public ReadOnly Property DesignerHost() As IDesignerHost
Get
If m_DesignerHost Is Nothing Then
m_DesignerHost = DirectCast(GetService(GetType(IDesignerHost)), IDesignerHost)
End If
Return m_DesignerHost
End Get
End Property

Public ReadOnly Property SelectionService() As ISelectionService
Get
If m_SelectionService Is Nothing Then
m_SelectionService = DirectCast(getservice(GetType(ISelectionService)), ISelectionService)
End If
Return m_SelectionService
End Get
End Property

Private Sub OnAddPanel(ByVal sender As Object, ByVal e As EventArgs)

Dim oldManagedPanels As Control.ControlCollection = HostControl.Controls

RaiseComponentChanging(TypeDescriptor.GetProperties(HostControl)("ManagedPanels"))

Dim P As Controls.ManagedPanel = DirectCast(DesignerHost.CreateComponent(GetType(Controls.ManagedPanel)), Controls.ManagedPanel)
P.Text = P.Name
HostControl.ManagedPanels.Add(P)

RaiseComponentChanged(TypeDescriptor.GetProperties(HostControl)("ManagedPanels"), oldManagedPanels, HostControl.ManagedPanels)
HostControl.SelectedPanel = P

SetVerbs()

End Sub

Private Sub OnRemovePanel(ByVal sender As Object, ByVal e As EventArgs)

Dim oldManagedPanels As Control.ControlCollection = HostControl.Controls

If HostControl.SelectedIndex < enabled =" False" enabled =" True" panelmanager =" DirectCast(Me.Control," text =" pm.ManagedPanels(0).Name" text =" pm.ManagedPanels(1).Name" selectedindex =" 0" designerverbcollection =" New" m_selectionservice =" DirectCast(getservice(GetType(ISelectionService)),">= 0.5 Then
penColor = ControlPaint.Dark(Me.Control.BackColor)
Else
penColor = Color.White
End If
Dim dashedPen As New Pen(penColor)
Dim borderRectangle As Rectangle = Me.Control.ClientRectangle
borderRectangle.Width -= 1
borderRectangle.Height -= 1
dashedPen.DashStyle = Drawing2D.DashStyle.Dash
pe.Graphics.DrawRectangle(dashedPen, borderRectangle)
dashedPen.Dispose()
End Sub

Public Overrides ReadOnly Property Verbs() As System.ComponentModel.Design.DesignerVerbCollection
Get
Return m_verbs
End Get
End Property

Protected Overrides Sub PostFilterProperties(ByVal properties As System.Collections.IDictionary)
properties.Remove("Anchor")
properties.Remove("TabStop")
properties.Remove("TabIndex")
MyBase.PostFilterProperties(properties)
End Sub

Public Overrides Sub OnSetComponentDefaults()
MyBase.OnSetComponentDefaults()
Me.Control.Visible = True
End Sub

End Class

End Namespace

Namespace Editors

Public Class ManagedPanelCollectionEditor
Inherits System.ComponentModel.Design.CollectionEditor

Public Sub New(ByVal type As Type)
MyBase.New(type)
End Sub

Protected Overrides Function CreateCollectionItemType() As System.Type
Return GetType(Controls.ManagedPanel)
End Function

End Class

End Namespace

Namespace TypeConverters

Public Class SelectedPanelConverter
Inherits ReferenceConverter

Public Sub New()
MyBase.New(GetType(Controls.ManagedPanel))
End Sub

Protected Overrides Function IsValueAllowed(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal value As Object) As Boolean
If Not (context Is Nothing) Then
Dim pm As Controls.PanelManager = DirectCast(context.Instance, Controls.PanelManager)
Return pm.ManagedPanels.Contains(CType(value, Controls.ManagedPanel))
End If
Return False
End Function

End Class

End Namespace
2009-01-29

Proper Case Or Title Case

In VB6 there was a function called StrConv which we were using to convert a string to Proper Case or Title Case, but there is no direct equivalent in .NET. Yes, we can still use this function in vb.net but it's not a .NET way. I mean, StrConv cannot be used in C# directly. To use it in C# we need to add the reference of Microsoft.VisualBasic.Compatibility.dll. And people will not like to do so just to use a single function of that library.

Some people write their own function to achieve the functionality. They first convert all the letters of the string to lowercase and then loop through all the words and capitalize the first letter of each word of the string. I am not in favor of this approach. I don't like to write long code just to achieve a small functionality. I always look for shortcuts.

Though there is not direct equivalent for StrConv in .NET, .NET provides us System.Globalization.TextInfo class to overcome the problem. We can use ToTitleCase function of TextInfo class to convert the string in proper case.

I have written a small function to do the work.

Public Function ToProperCase( _
ByVal source As String _
) As String

source = source.ToLower
Return Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(source)
End Function
Usage:
Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

MessageBox.Show(ToProperCase("HELLO WORLD!"))
End Sub
If you want to use ToProperCase() function like this...

Dim str As String = "HELLO WORLD!"
str = str.ToProperCase()
...then you need to use a concept called Extension Methods. Here it goes:

Module ExtensionMethods

_
Public Function ToProperCase( _
ByVal source As String _
) As String

source = source.ToLower
Return Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(source)
End Function

End Module'
2009-01-21

How To Trap TAB Key

Have you ever faced any situation wherein you wanted to trap the TAB key and do some stuff. Lets say for example, you want to trap the TAB key on a Textbox control and want to process some logic. TAB key cannot be trapped in the KeyrPress event. TAB key can be trapped with ProcessCmdKey and ProcessTabKey methods. But in this case I prefer ProcessCmdKey. In ProcessCmdKey method, we can cancel the processing of TAB key but that is not the case with ProcessTabKey method. It just traps the TAB key, it doesn't gives you any control over TAB key.

In one of my Interop project, I was using True DBGrid control of ComponentOne. In that project, the grid was throwing an exception in some scenarios when TAB key is pressed. The exception was thrown by the control itself, so I didn't had any control on it. That's why I chose ProcessCmdKey to trap the TAB key. I trapped the key and manually set the focus to the next control. This is how ProcessCmdKey came to my rescue. I am posting the code below that I used in my project.

''' 
''' Process the windows messages manually.
''' Trap the TAB key pressed on grid and suppress it.
''' Pressing the TAB key in FilterBar of C1 True DBGrid causes exception.
'''

'''
'''
'''
'''
Protected Overrides Function ProcessCmdKey( _
ByRef msg As Message, ByVal keyData As System.Windows.Forms.Keys _
) As Boolean

' Declare a variable of type Keys enumeration named keyPressed.
' Cast the msg's WParam as a KeyEnum value and assign it to the
' keyPressed variable.
Dim keyPressed As Keys = CType(msg.WParam.ToInt32(), Keys)
Dim ctrlType As String = "C1.Win.C1TrueDBGrid.GridEditor"

' Process keyPressed.
Select Case keyPressed
Case Keys.Tab
If Me.ActiveControl.ToString.StartsWith(ctrlType) Then
If grdSamples.EditActive = False Then
txtInstrument.Focus()
End If
' Cancel the TAB key message
Return True

' You can also trap the TAB key for button or TextBox
ElseIf Me.ActiveControl.Name = "btnClose" Then
grdSamples.Focus()
' Cancel the TAB key message
Return True
End If
Case Else
' Return the key message so it can be processed by this control.
Return MyBase.ProcessCmdKey(msg, keyData)
End Select
End Function
2008-10-14

Simulate key strokes like CAPS LOCK, NUM LOCK, SCROLL LOCK, etc

There are situation when we need to turn on/off or check the status of CAPS LOCK, NUM LOCK and SCROLL LOCK keys. The simplest way to accomplish this is to use SendKeys, but it has it's own disadvantages. So I am using keybd_event API instead. If offers two advantages over SendKeys. First, it doesn't cause the NUM LOCK light to flicker unless you specifically press the NUM LOCK key. Secondly, it's possible to press and hold a key. So it does like this:
Imports System.Runtime.InteropServices

Public Class Form2

Private Declare Sub keybd_event Lib "user32" ( _
ByVal bVk As Byte, _
ByVal bScan As Byte, _
ByVal dwFlags As Integer, _
ByVal dwExtraInfo As Integer _
)

Private Const VK_CAPITAL As Integer = 20
Private Const VK_NUMLOCK As Integer = 144
Private Const VK_SCROLL As Integer = 145
Private Const KEYEVENTF_EXTENDEDKEY As Integer = &H1
Private Const KEYEVENTF_KEYUP As Integer = &H2

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

' Toggle CapsLock

' Simulate the Key Press
keybd_event(VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or 0, 0)

' Simulate the Key Release
keybd_event(VK_CAPITAL, &H45, KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP, 0)
End Sub

End Class
To toggle NUM LOCK and SCROLL LOCK keys, you just need to replace VK_CAPITAL with appropriate constant variables of NUM LOCK and SCROLL LOCK keys.

Actually speaking, above code can be used to simulate any key. We just need to replace VK_ constants with the appropriate variables of desired keys.

Here I am listing down the virtual keys standard set for your reference.

VK_LBUTTON = 1
VK_RBUTTON = 2
VK_CANCEL = 3
VK_MBUTTON = 4
VK_BACK = 8
VK_TAB = 9
VK_CLEAR = 12
VK_RETURN = 13
VK_SHIFT = &H10
VK_CONTROL = 17
VK_MENU = 18
VK_PAUSE = 19
VK_CAPITAL = 20
VK_ESCAPE = 27
VK_SPACE = &H20
VK_PRIOR = 33
VK_NEXT = 34
VK_END = 35
VK_HOME = 36
VK_LEFT = 37
VK_UP = 38
VK_RIGHT = 39
VK_DOWN = 40
VK_SELECT = 41
VK_PRINT = 42
VK_EXECUTE = 43
VK_SNAPSHOT = 44
VK_INSERT = 45
VK_DELETE = 46
VK_HELP = 47
VK_LWIN = 91
VK_RWIN = 92
VK_APPS = 93
VK_NUMPAD0 = 96
VK_NUMPAD1 = 97
VK_NUMPAD2 = 98
VK_NUMPAD3 = 99
VK_NUMPAD4 = 100
VK_NUMPAD5 = 101
VK_NUMPAD6 = 102
VK_NUMPAD7 = 103
VK_NUMPAD8 = 104
VK_NUMPAD9 = 105
VK_MULTIPLY = 106
VK_ADD = 107
VK_SEPARATOR = 108
VK_SUBTRACT = 109
VK_DECIMAL = 110
VK_DIVIDE = 111
VK_F1 = 112
VK_F2 = 113
VK_F3 = 114
VK_F4 = 115
VK_F5 = 116
VK_F6 = 117
VK_F7 = 118
VK_F8 = 119
VK_F9 = 120
VK_F10 = 121
VK_F11 = 122
VK_F12 = 123
VK_F13 = 124
VK_F14 = 125
VK_F15 = 126
VK_F16 = 127
VK_F17 = 128
VK_F18 = 129
VK_F19 = 130
VK_F20 = 131
VK_F21 = 132
VK_F22 = 133
VK_F23 = 134
VK_F24 = 135
VK_NUMLOCK = 144
VK_SCROLL = 145
VK_LSHIFT = 160
VK_RSHIFT = 161
VK_LCONTROL = 162
VK_RCONTROL = 163
VK_LMENU = 164
VK_RMENU = 165
VK_PROCESSKEY = 229
VK_ATTN = 246
VK_CRSEL = 247
VK_EXSEL = 248
VK_EREOF = 249
VK_PLAY = 250
VK_ZOOM = 251
VK_NONAME = 252
VK_PA1 = 253
VK_OEM_CLEAR = 254

VK_0 thru VK_9 are the same as ASCII '0' thru '9' (&H30 - &H39)
VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (&H41 - &H5A)
2008-10-13

Get drive information of Remote Server using WMI

To use this code you need to add reference to System.Management namespace.

Imports System.Management

Public Class Form1

Dim strFreespace As String
Dim D_Freespace As Double
Dim strTotalspace As String
Dim D_Totalspace As Double

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

CalculateFreeUsed("wabdkp59")
End Sub

Private Sub CalculateFreeUsed(ByVal srvname As String)
Dim msg As String

Try
' Connection credentials to the remote computer -
' not needed if the logged in account has access
Dim oConn As New ConnectionOptions()

oConn.Username = "mum-users\dsakpal"
oConn.Password = "*****"
Dim strNameSpace As String = "\\"

If srvname <> "" Then
strNameSpace += srvname
Else
strNameSpace += "."
End If

strNameSpace += "\root\cimv2"

Dim oMs As New System.Management.ManagementScope(strNameSpace, oConn)

'get Fixed disk stats
Dim oQuery As New System.Management.ObjectQuery("select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3")

'Execute the query
Dim oSearcher As New ManagementObjectSearcher(oMs, oQuery)

'Get the results
Dim oReturnCollection As ManagementObjectCollection = oSearcher.[Get]()

'loop through found drives and write out info
For Each oReturn As ManagementObject In oReturnCollection
' Free Space in bytes
D_Freespace = System.Convert.ToDouble(oReturn("FreeSpace"))
' Free Space in GB
strFreespace = (((D_Freespace / 1024) / 1024) / 1024).ToString("0.00")
' Size in bytes
D_Totalspace = System.Convert.ToDouble(oReturn("Size"))
' Size in GB
strTotalspace = (((D_Totalspace / 1024) / 1024) / 1024).ToString("0.00")

msg = "Drive: {0}" & ControlChars.NewLine
msg = msg & "Total space: {1} GB" & ControlChars.NewLine
msg = msg & "Free Space: {2} GB" & ControlChars.NewLine
msg = String.Format(msg, oReturn("Name").ToString(), strTotalspace, strFreespace)
MessageBox.Show(msg)
Next
Catch
msg = "Failed to obtain Server Information."
MessageBox.Show(msg, "Server Error", MessageBoxButtons.OK, MessageBoxIcon.[Error])
End Try
End Sub

End Class
2008-10-06

Call DrawItem from another sub

Hi,

In this post I am going to tell you how to call DrawItem from another function, sub or procedure.

What is DrawItem
DrawItem is a event which occurs when a visual aspect of an owner-drawn control changes. The DrawItem event is fired only if the DrawMode property is set to OwnerDrawFixed or OwnerDrawVariable. In this article I am going to use this event to change the the ForeColor of Listbox items.

Simple Example

VB.NET Version:
Public Class Form1

Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load

ListBox1.DrawMode = DrawMode.OwnerDrawFixed
ListBox1.Items.Add("Red")
ListBox1.Items.Add("Green")
ListBox1.Items.Add("Blue")
End Sub

Private Sub ListBox1_DrawItem( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.DrawItemEventArgs _
) Handles ListBox1.DrawItem

Dim myFont As Font
If e.Index > -1 Then
e.DrawBackground()
myFont = New Font(ListBox1.Font.Name, ListBox1.Font.Size, ListBox1.Font.Style)
If ListBox1.Items(e.Index).ToString = "Red" Then
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, New SolidBrush(Color.Red), e.Bounds)
ElseIf ListBox1.Items(e.Index).ToString = "Green" Then
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, New SolidBrush(Color.Green), e.Bounds)
ElseIf ListBox1.Items(e.Index).ToString = "Blue" Then
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, New SolidBrush(Color.Blue), e.Bounds)
End If
End If
End Sub

End Class
C# Version:
public class Form1 {

private void Form1_Load(object sender, System.EventArgs e) {
ListBox1.DrawMode = DrawMode.OwnerDrawFixed;
ListBox1.Items.Add("Red");
ListBox1.Items.Add("Green");
ListBox1.Items.Add("Blue");
}

private void ListBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) {
Font myFont;

if (e.Index > -1) {

e.DrawBackground();
myFont = new Font(ListBox1.Font.Name, ListBox1.Font.Size, ListBox1.Font.Style);

if (ListBox1.Items(e.Index).ToString == "Red") {
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, new SolidBrush(Color.Red), e.Bounds);
}
else if (ListBox1.Items(e.Index).ToString == "Green") {
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, new SolidBrush(Color.Green), e.Bounds);
}
else if (ListBox1.Items(e.Index).ToString == "Blue") {
e.Graphics.DrawString(ListBox1.Items(e.Index).ToString, myFont, new SolidBrush(Color.Blue), e.Bounds);
}

}
}

}
Example is quite straight forward. In the Load event of the Form I am adding three items to the Listbox control "Red", "Green" and "Blue" respectively. In the DrawItem event I am checking the text of the listbox item being added and doing the coloring stuff.

Real World Scenario
The example given above is very simple. In real world applications the scenarios may not be as simple as this one. I am checking for Red, Green and Blue colors in DrawItem event itself, but what of you want to create a procedure that will add an item to the listbox as well as will specifying the color of that listbox item.

Real World Example
Lets consider a scenario wherein I want to add items to a ListBox control. While adding items to ListBox, I want to specify the ForeColor of the list item. This cannot be done in DrawItem event. Just go through the code snippet given below. I will explain it shortly.

VB.NET Version:
Public Class Form1

Class ListItem
Friend Text As String
Friend ForeColor As Color

Public Sub New(ByVal text As String, ByVal textColor As Color)
Me.Text = text
Me.ForeColor = textColor
End Sub

Public Overrides Function ToString() As String
Return Text
End Function
End Class

Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load

ListBox1.DrawMode = DrawMode.OwnerDrawFixed
End Sub

Private Sub AddItem(ByVal text As String, ByVal foreColor As Color)
ListBox1.Items.Add(New ListItem(text, foreColor))
End Sub

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

AddItem("One", Color.Red)
AddItem("Two", Color.Green)
AddItem("Three", Color.Blue)
End Sub

Private Sub ListBox1_DrawItem( _
ByVal sender As Object, _
ByVal e As System.Windows.Forms.DrawItemEventArgs _
) Handles ListBox1.DrawItem

Dim myFont As Font
Dim cListItem As ListItem

If e.Index > -1 Then
e.DrawBackground()
myFont = New Font(ListBox1.Font.Name, ListBox1.Font.Size, ListBox1.Font.Style)
cListItem = DirectCast(ListBox1.Items(e.Index), ListItem)
e.Graphics.DrawString(cListItem.ToString, myFont, New SolidBrush(cListItem.ForeColor), e.Bounds)
End If
End Sub

End Class
C# Version:
public class Form1 {

class ListItem {
internal string Text;
internal Color ForeColor;

public ListItem(string text, Color textColor) {
this.Text = text;
this.ForeColor = textColor;
}

public override string ToString() {
return Text;
}
}

private void Form1_Load(object sender, System.EventArgs e) {
ListBox1.DrawMode = DrawMode.OwnerDrawFixed;
}

private void AddItem(string text, Color foreColor) {
ListBox1.Items.Add(new ListItem(text, foreColor));
}

private void Button1_Click(object sender, System.EventArgs e) {
AddItem("One", Color.Red);
AddItem("Two", Color.Green);
AddItem("Three", Color.Blue);
}

private void ListBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) {
Font myFont;
ListItem cListItem;

if (e.Index > -1) {
e.DrawBackground();
myFont = new Font(ListBox1.Font.Name, ListBox1.Font.Size, ListBox1.Font.Style);
cListItem = (ListItem)ListBox1.Items(e.Index);
e.Graphics.DrawString(cListItem.ToString, myFont, new SolidBrush(cListItem.ForeColor), e.Bounds);
}
}

}

In the above example I have created a custom class called 'ListItem' to represent the item to be added in the Listbox control. This class contains two members which are quite explanatory. They represent Text and ForeColor of list box item. Next, I have created a constructor having two arguments whose values are assigned to the class members. In the next code segment I am overriding the ToString function of the class. ToString function is the default function of any class. When items are added to the listbox, this function gets called. I am using this function to return the text to be displayed in the listbox control.

In the form load event, I am setting the drawMode property of Listbox. DrawMode specifies how the elements of a control are drawn i. e. it gets or sets a value indicating whether user code or the operating system will handle drawing of elements in the list. Its default value is set to 'Normal'. When the DrawMode property is set to Normal, all the elements in a control are drawn by the operating system. As in this example we are going to handle to drawing stuff ourselves in DrawItem event, I set the value of DrawMode to OwnerDrawFixed.

Next, I have created a procedure 'AddItem' which accepts two arguments; text and foreColor. Using this two arguments I am creating a new object of ListItem class and adding it to the Listbox. In the button click event, I am calling the AddItem procedure and passing values to it.

The next code segment is the most important part of this article i.e. DrawItem event. Every time an item is added to the ListBox, DrawItem event is fired.
This event is also fired even when there are no items in the ListBox. So I have added an IF condition to prevent our code from failure. The next statement is e.DrawBackground(). This method basically draws the item selection background. In the next statement I am creating a font object from Listbox control that will be used while drawing the actual string in the ListBox. In the next statement I am getting the object ofListItem class that we added from AddItem method. The next statement is the heart of the DrawItem event which actually draws the ListBox item text.

That's All.
2008-09-11

Narrowing & Widening Conversion Gotcha

What is Narrowing & Widening Conversion
Basically there are two type of conversions, Narrowing conversion and Widening conversion. Both these conversions comes into picture whenever type conversion occurs. An important consideration with a type conversion is whether the result of the conversion is within the range of the destination data type. A widening conversion changes a value to a data type that can accommodate any possible value of the original data. Converting from an Integer to a Long is a widening conversion. A narrowing conversion changes a value to a data type that might not be able to hold some of the possible values. Converting from a Long to a Integer is a narrowing conversion.

When does it occurs
Narrowing conversions are done by default i.e. implicitly by the .NET compiler. The compiler doesn't do widening conversions implicitly for you. When you assign a Long variable's value to a Integer variable narrowing conversion is done by default. The compiler automatically treats the number as Integer. But when assign a Integer value to a Long variable, compiler doesn't treat it as a long data type value. Having said that, you need to explicitly tell the compiler that your number is a Long.

Gotcha
Sometimes things can go wrong without your knowledge while these conversions are involved. Lets have a look at following code segment:

Lets consider that you have a form with a ComboBox control dropped on it.

Public Class Form1

Public Class cListItem
Private m_id As Integer
Private m_text As String

Public Sub New( _
ByVal id As Integer, _
ByVal text As String _
)
Me.m_id = id
Me.m_text = text
End Sub

Public Property ID() As Long
Get
Return m_id
End Get
Set(ByVal value As Long)
m_id = value
End Set
End Property

Public Property Text() As String
Get
Return m_text
End Get
Set(ByVal value As String)
m_text = value
End Set
End Property
End Class

Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles MyBase.Load

Dim items As cListItem() = New cListItem(3) {}

items(0) = New cListItem(1, "Test 1")
items(1) = New cListItem(2, "Test 2")
items(2) = New cListItem(3, "Test 3")
items(3) = New cListItem(4, "Test 4")

ComboBox1.Items.AddRange(items)
ComboBox1.DataSource = items
ComboBox1.ValueMember = "ID"
ComboBox1.DisplayMember = "Text"
End Sub

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

ComboBox1.SelectedValue = 2
End Sub

End Class
Above code populates the ComboBox with some values. If you click the button it should select the second item in the ComBox, but it doesn't. Why?

When you assign a numeric value '2' to the 'SelectedValue' property of the ComboBox at line "ComboBox1.SelectedValue = 2", the compiler automatically treats the number 2 as an Integer. Since all the combo box items are Long objects, it doesn't find a match. So nothing is selected and you end up with a blank combo box.

Solution
The compiler doesn't do widening conversions implicitly for you, so you have to do the conversion explicitly.
' Explicitly convert the Integer to Long
ComboBox1.SelectedValue = 2L
So the moral of the story is to make a practice of converting values to proper types while doing type conversions.
2008-09-05

Load form from its String name

In some situations we need to load form from its string name. Lets consider for example, you have stored the list of forms in your application in a database and now you want to load those forms at run-time. The simplest solution for this is that you will write if conditions like this:

' Dummy code
Dim formName As String

formName = "form1" ' Get the form name from database.

If formName = "form1" Then
Dim frm1 As New Form1
frm1.Show()
ElseIf formName = "form2" Then
Dim frm2 As New Form2
frm2.Show()
ElseIf formName = "form3" Then
Dim frm3 As New Form3
frm3.Show()
ElseIf formName = "form4" Then
Dim frm4 As New Form4
frm4.Show()
End If
But what if you have 40 more forms in your application. So basically writing that much if conditions is not a good solution. To accomplish the goal we need to use Reflection. Here is the code:

Imports System.Reflection

Public Class Form2

Private Sub Button1_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs _
) Handles Button1.Click

Dim frm As New Form
Dim formName As String = "Form1"

formName = Me.GetType.Assembly.GetName.Name & "." & formName
frm = DirectCast(Me.GetType.Assembly.CreateInstance(formName), Form)
frm.Show()
End Sub

End Class
2008-09-02

.NET Tips and Tricks

Vertical Block Selection:

Let’s assume a situation where we have 20 lines of code and now we need to delete first 10 characters of every line. What we do normally do here is go to first line and select the first 10 characters and delete it. Again go to second line, select the first 10 characters and delete it. We follow these steps for all remaining lines. Quite a boring work, isn’t it? There is a nice feature in Visual Studio .NET IDE called ‘Vertical Block Selection’ present since from .NET 2005 edition. To delete 10 characters of every line, press and hold the ALT key and then select the text of every line in vertical direction with mouse. That’s it. It’s simple but very handy.

Convert a piece of VB6 code to VB.NET:

If we want to convert VB6 project to VB.NET the easiest solution for this is we open the VB6 project straight forward in .NET IDE. This starts the ‘Visual Basic Upgrade Wizard’ and converts the entire project. Now what if want to convert a small piece of VB6 code to VB.NET. There is no provision for this in Visual Basic Upgrade Wizard. The solution is, create a sample VB.NET Windows application and go to Tools menu and select ‘Upgrade Visual Basic 6 code’. Paste the VB6 code here and click the Upgrade button. The upgraded code will inserted in currently open document at the current cursor position. If the VB6 code is referring to any COM component then you can add reference to it from the Reference tab of Upgrade Visual Basic 6 code window.

Writing Comments in a better way:

You can add better procedure level comments by typing three single quotes, just type ‘’’ just above procedure name, function name, property name, etc. Visual Studio will automatically insert a documentation template whenever the three single quotes are typed within a VB.NET source code file. Inserting comments this way basically enhance the information presented about a class, constructor or other member. Although these comments can be added to the source code at any point, it is usual to ensure that documentation is inserted immediately before the definition of classes, functions, subroutines and properties, thereby allowing these members to be documented.

Setting Tab Order easily:

How do you set the tab order of controls in your application? Setting tab order for controls in a bit boring task for me since from VB6 days, but now in .NET things are changed. To set the tab order, you simply select all controls, and then select Tab Order from the View menu. Then just click the controls in the order you want the tabs to sit. As you click each control, the tab order will be displayed on the control to keep you up to date. Press ESC when you have finished.

Code Snippets:

Code snippets are one of the best productivity features introduced in Visual Studio 2005. It allows you to quickly insert fragments of code to avoid tedious typing (such as typing a for loop) or to give you a template of how to accomplish a certain task (such as sending data over the network).

There are two ways to insert a snippet. You can type the snippet's alias in the code editor and press TAB twice to insert the snippet immediately. After the code snippet has been inserted, you can press TAB and SHIFT+TAB to jump to different fields within the snippet. This allows you to quickly change the parts of code that need to be modified.

If you don't remember your code snippet's alias, you can also insert it by pressing "Ctrl+K, Ctrl+X" within the code editor or do a mouse right-click with the mouse and select Insert Snippet.... This shows the code snippet picker, which enables you to browse all the snippets that are applicable to your current programming language and to choose the one you want to insert.

The most exciting part of the code snippet feature is that you can create your own snippets. You can do it from ‘Code Snippets Manager’ window found in Tools menu.

Use Regions:

A very nice feature of the Visual Studio .NET code editor is the concept of Regions. Regions are great way to organize your code. You can create named regions directly in our source code. We can then expand and collapse regions in the editor to hide or show code based. For example, you could create a region called “Public Properties” and put all your property code there like I have done below:

There are five basic regions into which we can group our code: Private Fields, Constructors, Public Methods, Public Properties, and Private Methods. Off course other region can be created as per demand. This is up to us how we organise the code.

Line Numbering:

Did you know that you can add line numbers to your code files in VS.NET? Line numbers are especially helpful if discussing a block of code with someone else, as you can refer to a specific line numbers. It is configured through the Options dialog of Tools menu.

Tools|Options|Text Editor|All Languages|General|Line numbers.

If you want to set this option only for any specific language, then choose the appropriate language instead of ‘All Languages’.



Store Commonly Used Text/Code in Toolbox:

One of the nice VS.NET productivity tricks is to store text/code/re-usable things as toolbox items. To add text to the Toolbox, highlight it in the code editor, drag it over to your toolbox, and drop it when the mouse pointer changes to a rectangle. Thereafter, you can simply drag and drop the toolbox items to your code editor for reuse.

Don't Cut and Paste controls:

Don't cut and paste controls that have event code in them. It removes their handlers which can cause massive headaches.

For example, If you add a button to a form, put some code in its click event, then cut and paste it back to the form, the code in the click event will no longer work.

What happens is the code is still there, but it removes the "Handles button1.Click" from the end of the sub since the IDE is using the background compiler.

You can only imagine what would happen if you had 100 controls on your form and you cut and paste them for some reason (like putting them in a frame or panel)

Drag the controls instead, they will retain the links to the handlers.

Advanced Members:

Some members of classes in the framework are invisible to VB.NET by default. While many may truly be advanced members that you will not likely use, but some are common ones . You can turn on these advanced members from:

Tools|Options|Text Editor|Basic

Keyboard IDE Launch:

I like this one a lot because I do it myself for many programs that don't need fully qualified paths to run.

If you want to open the VS.NET IDE you can simply hit Start|Run and type devenv and hit ENTER key.

Some parts of this article taken from vbforums.