'Copyright (c) 2006 Patrick Sears
' No rights are conferred by this code, although you are allowed to use it freely.
' Just give credit where credit is due.
'
' Remember, no warranty and no guarantee for any express purpose are given
Imports System.Windows.Forms
Imports System.IO
Imports System.Text
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Drawing
Public Class IconComboBox
Inherits Windows.Forms.ComboBox
Private m_CurrentItem As IconComboItem
Private m_IconComboItemList As IconComboItemCollection
''' <summary>
''' Enumeration to indicate how the IconComboItemCollection gets changed
''' for various operations
''' </summary>
''' <remarks>Only used internally by IconComboBox</remarks>
Public Enum IconComboItemCollectionChangeType
ItemAdded = 0
ItemRemoved
ItemInserted
CollectionCleared
End Enum
''' <summary>
''' Get or set the tooltip text to be shown in the tooltip over the
''' combobox.
''' </summary>
''' <value>The new string to display in the tooltip.</value>
''' <returns>The string currently being displayed in the tooltip.</returns>
''' <remarks>This string defaults to the Data.ToString value when an item
''' in the ComboBox is selected.</remarks>
Public Property ToolTipText() As String
Get
Return ToolTip1.GetToolTip(Me)
End Get
Set(ByVal value As String)
ToolTip1.SetToolTip(Me, value)
End Set
End Property
''' <summary>
''' The collection of IconComboItems used to render the ComboBox.
''' </summary>
''' <returns>The collection of IconComboItems used to render the ComboBox.</returns>
''' <remarks>This property provides a similar interface to the .NET ComboBox,
''' needing to call ComboBox1.Items.Add("MyStr") to add an item.</remarks>
Public Overloads ReadOnly Property Items() As IconComboItemCollection
Get
Return m_IconComboItemList
End Get
End Property
''' <summary>
''' Gets the IconComboItem object for the currently selected item in the dropdown.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Overloads Property SelectedItem() As IconComboItem
Get
If Me.SelectedIndex >= 0 Then
Return m_IconComboItemList(Me.SelectedIndex)
Else
Return Nothing
End If
End Get
Set(ByVal Value As IconComboItem)
m_CurrentItem = Value
End Set
End Property
''' <summary>
''' Creates a new instance of the IconComboBox.
''' </summary>
''' <remarks></remarks>
Public Sub New()
MyBase.New()
InitializeComponent()
m_IconComboItemList = New IconComboItemCollection
AddHandler m_IconComboItemList.CollectionChanged, AddressOf m_IconComboItemList_CollectionItemsChanged
'Set to OwnerDraw
Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
'Must be set to DropDownList
Me.DropDownStyle = ComboBoxStyle.DropDownList
End Sub
'ComboBox must have its style set to DropDownList
Private Sub ExpCombo_DropDownStyleChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Me.DropDownStyle = ComboBoxStyle.DropDownList
End Sub
'Make sure font doesn't get set to more than 12 points
Protected Overrides Sub OnFontChanged(ByVal e As System.EventArgs)
If Me.Font.SizeInPoints > 12 Then
Me.Font = New Font(Me.Font.FontFamily, 12, Me.Font.Style)
End If
End Sub
Protected Overrides Sub OnSelectedIndexChanged(ByVal e As System.EventArgs)
If Me.SelectedIndex < 0 Then
Exit Sub
End If
If Me.SelectedIndex < m_IconComboItemList.Count Then
If m_IconComboItemList(Me.SelectedIndex).IsDivider Then
If Me.SelectedIndex > 0 Then
Me.SelectedIndex -= 1
m_CurrentItem = m_IconComboItemList(Me.SelectedIndex)
ElseIf Me.SelectedIndex < m_IconComboItemList.Count - 1 Then
Me.SelectedIndex += 1
m_CurrentItem = m_IconComboItemList(Me.SelectedIndex)
End If
Else
m_CurrentItem = m_IconComboItemList(Me.SelectedIndex)
MyBase.OnSelectedIndexChanged(e)
End If
' Set the default ToolTip string.
ToolTip1.SetToolTip(Me, m_CurrentItem.Data)
End If
End Sub
''' <summary>
''' Draws an IconComboBox item into the ComboBox in the area specified by the DrawItemEventArgs.
''' </summary>
''' <param name="e">Event argument specifying which item to draw and where to draw it.</param>
''' <remarks></remarks>
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
e.DrawBackground()
e.DrawFocusRectangle()
Dim bounds As Rectangle = e.Bounds
If e.Index > -1 AndAlso e.Index < Me.Items.Count AndAlso e.Index < m_IconComboItemList.Count Then
Dim currentItem As IconComboItem = m_IconComboItemList(e.Index)
If currentItem IsNot Nothing Then
If currentItem.IsDivider Then
Dim xStart As Single = bounds.X + 10
Dim yStart As Single = Convert.ToSingle(bounds.Y + bounds.Height / 2 + 1)
Dim xEnd As Single = bounds.Width - 10
Dim yEnd As Single = yStart
e.Graphics.DrawLine(Pens.Black, xStart, yStart, xEnd, yEnd)
yStart = Convert.ToSingle(bounds.Y + bounds.Height / 2 - 1)
yEnd = yStart
e.Graphics.DrawLine(Pens.Black, xStart, yStart, xEnd, yEnd)
Else
Dim imageSize As Size
Dim fileNameOnly As String = currentItem.DisplayText 'Items(e.Index).ToString()
Dim fullFileName As String = currentItem.Data
Dim fileIcon As Icon = currentItem.ItemImage
Dim imageRectangle As New Rectangle(bounds.Left + 1, bounds.Top + 1, ItemHeight, ItemHeight)
If fileIcon IsNot Nothing Then
e.Graphics.DrawIcon(fileIcon, imageRectangle)
End If
imageSize = imageRectangle.Size
Dim fileNameRec As New Rectangle(bounds.Left + imageSize.Width + 3, bounds.Top, bounds.Width - imageSize.Width - 3, bounds.Height)
Dim format As New StringFormat()
format.LineAlignment = StringAlignment.Center
e.Graphics.DrawString(fileNameOnly, e.Font, New SolidBrush(e.ForeColor), fileNameRec, format)
End If
Else
Console.WriteLine("Error, the selected item is nothing")
End If
End If
MyBase.OnDrawItem(e)
End Sub
''' <summary>
''' Add a divider line to the ComboBox. The line will be appended to the end of the list.
''' </summary>
''' <returns>The index of the added divider.</returns>
''' <remarks></remarks>
Public Function AddDivider() As Integer
Dim tempItem As New IconComboItem
'tempItem.ItemImage = Nothing
'tempItem.Data = Nothing
tempItem.DisplayText = ""
tempItem.IsDivider = True
Return Me.Items.Add(tempItem)
End Function
' I am forced to raise an event from the IconComboItemCollection to inform me when it changes -
' e.g. the user calls Items.Clear, Items.Add, or Items.Remove. I need to be notified of this so that
' I can remove the corresponding item from the base ComboBox object collection.
Private Sub m_IconComboItemList_CollectionItemsChanged(ByVal sender As Object, ByVal e As IconComboItemCollectionChangedEventArgs)
If e.ChangeType = IconComboItemCollectionChangeType.ItemAdded Then
MyBase.Items.Add(e.ChangedItem.DisplayText)
ElseIf e.ChangeType = IconComboItemCollectionChangeType.ItemInserted Then
MyBase.Items.Insert(e.ChangedIndex, e.ChangedItem.DisplayText)
ElseIf e.ChangeType = IconComboItemCollectionChangeType.ItemRemoved Then
MyBase.Items.Remove(e.ChangedItem.DisplayText)
ElseIf e.ChangeType = IconComboItemCollectionChangeType.CollectionCleared Then
MyBase.Items.Clear()
End If
End Sub
#Region "Class IconComboItem"
''' <summary>
''' Object representing an entry in the IconComboBox.
''' </summary>
''' <remarks>Implements IEquatable for searching and sorting the IconComboItemCollection.</remarks>
Public Class IconComboItem
Implements IEquatable(Of IconComboItem)
Private m_Icon As Icon
Private m_Data As String
Private m_DisplayText As String
Private m_isDivider As Boolean
''' <summary>
''' The image to be displayed next to the text for this combo box item.
''' </summary>
''' <value>The icon to be displayed</value>
''' <returns>The icon currently being displayed</returns>
''' <remarks></remarks>
Public Property ItemImage() As Icon
Get
Return m_Icon
End Get
Set(ByVal value As Icon)
m_Icon = value
End Set
End Property
''' <summary>
''' The string representing data you want associated with this IconComboItem.
''' </summary>
''' <value>The data to be set</value>
''' <returns>The data currently saved</returns>
''' <remarks></remarks>
Public Property Data() As String
Get
Return m_Data
End Get
Set(ByVal value As String)
m_Data = value
End Set
End Property
''' <summary>
''' What to display for this item.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property DisplayText() As String
Get
Return m_DisplayText
End Get
Set(ByVal value As String)
m_DisplayText = value
End Set
End Property
''' <summary>
''' Indicates whether this IconComboItem is a divider.
''' </summary>
''' <value>Boolean indicating if this item is a divider or not. This property can only be SET
''' by the IconComboBox.</value>
''' <returns>True if this is a divider; False if it is not.</returns>
''' <remarks></remarks>
Public Property IsDivider() As Boolean
Get
Return m_isDivider
End Get
' This property is Friend so that ONLY THE IconComboBox can set this property.
Friend Set(ByVal value As Boolean)
m_isDivider = value
End Set
End Property
Public Sub New()
End Sub
''' <summary>
''' Create a new IconComboItem with the specified values
''' </summary>
''' <param name="argText">The text to display in the combo box</param>
''' <param name="argData">The string representing this IconComboItem's data</param>
''' <remarks></remarks>
Public Sub New(ByVal argText As String, ByVal argData As String)
m_DisplayText = argText
m_Data = argData
End Sub
Public Shared Operator =(ByVal item1 As IconComboItem, ByVal item2 As IconComboItem) As Boolean
Return item1.Data = item2.Data
End Operator
Public Shared Operator <>(ByVal item1 As IconComboItem, ByVal item2 As IconComboItem) As Boolean
Return item1.Data <> item2.Data
End Operator
''' <summary>
''' Returns if this IconComboItem is equal to the specified one.
''' </summary>
''' <param name="other">The IconComboItem to compare</param>
''' <returns>True if this item's Data property equals the other item's Data property.</returns>
''' <remarks></remarks>
Public Overloads Function Equals(ByVal other As IconComboItem) As Boolean Implements System.IEquatable(Of IconComboItem).Equals
Return m_Data = other.Data
End Function
End Class
#End Region
#Region "Class IconComboItemCollection"
Public Class IconComboItemCollection
Private m_List As New Generic.List(Of IconComboBox.IconComboItem)
Friend Event CollectionChanged(ByVal sender As Object, ByVal e As IconComboItemCollectionChangedEventArgs)
''' <summary>
''' The number of items in the current collection
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property Count() As Integer
Get
Return m_List.Count
End Get
End Property
''' <summary>
''' Gets the IconComboItem at the specfied index
''' </summary>
''' <param name="index">The index of the IconComboItem to return</param>
''' <value></value>
''' <returns>The IconComboItem at the specified index, or nothing if the index is out of range.</returns>
''' <remarks></remarks>
Default Public ReadOnly Property Item(ByVal index As Integer) As IconComboItem
Get
If index < m_List.Count Then
Return m_List(index)
Else
Return Nothing
End If
End Get
End Property
''' <summary>
''' Add the specified IconComboItem to the end of the collection.
''' </summary>
''' <param name="argItem">The IconComboItem to add. Can be Null.</param>
''' <returns>The zero-based index where the IconComboItem was added.</returns>
''' <remarks></remarks>
Public Function Add(ByVal argItem As IconComboItem) As Integer
m_List.Add(argItem)
RaiseEvent CollectionChanged(Me, New IconComboItemCollectionChangedEventArgs(m_List.Count - 1, IconComboBox.IconComboItemCollectionChangeType.ItemAdded, argItem))
Return m_List.Count - 1
End Function
''' <summary>
''' Insert the specified IconComboItem at the specified index.
''' </summary>
''' <param name="idx"></param>
''' <param name="argItem"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function Insert(ByVal idx As Integer, ByVal argItem As IconComboItem) As Boolean
m_List.Insert(idx, argItem)
If m_List(idx) = argItem Then
RaiseEvent CollectionChanged(Me, New IconComboItemCollectionChangedEventArgs(idx, IconComboBox.IconComboItemCollectionChangeType.ItemInserted, argItem))
Return True
End If
Return False
End Function
Public Function Remove(ByVal argItem As IconComboItem) As Boolean
If m_List.Remove(argItem) Then
RaiseEvent CollectionChanged(Me, New IconComboItemCollectionChangedEventArgs(0, IconComboBox.IconComboItemCollectionChangeType.ItemRemoved, argItem))
Return True
End If
Return False
End Function
''' <summary>
''' Clear all the items from this collection and from the combo box.
''' </summary>
''' <remarks></remarks>
Public Sub Clear()
m_List.Clear()
RaiseEvent CollectionChanged(Me, New IconComboItemCollectionChangedEventArgs(-1, IconComboItemCollectionChangeType.CollectionCleared, Nothing))
End Sub
''' <summary>
''' Determine if the collection contains the specified IconComboItem, using the
''' <see>IconComboItem.Equals</see> method for comparison.
''' </summary>
''' <param name="item"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function Contains(ByVal item As IconComboItem) As Boolean
'For Each tempitem As IconComboItem In List
' If tempitem.Equals(item) Then
' Return True
' End If
'Next
'Return False
'Return List.Contains(item)
Return m_List.Contains(item)
End Function
''' <summary>
''' Searches for the specified IconComboItem and returns the zero-based index of the first
''' occurrence within the entire IconComboItemCollection.
''' </summary>
''' <param name="item">The object to locate in the IconComboItemCollection.
''' The value can be null for reference types.</param>
''' <returns>The zero-based index of the first occurrence of item within
''' the entire IconComboItemCollection, if found; otherwise, �1.</returns>
''' <remarks></remarks>
Public Function IndexOf(ByVal item As IconComboItem) As Integer
Return m_List.IndexOf(item)
End Function
End Class
#End Region
#Region "Friend Class IconComboItemCollectionChangedEventArgs"
''' <summary>
''' These args are used in events indicating that the IconComboItemCollection has been
''' changed. This class can only be used by the IconComboBox.
''' </summary>
''' <remarks></remarks>
Friend Class IconComboItemCollectionChangedEventArgs
Inherits EventArgs
Private m_ChangeType As IconComboBox.IconComboItemCollectionChangeType
Private m_ChangedItem As IconComboBox.IconComboItem
Private m_ChangedIndex As Integer
''' <summary>
''' Indicates how the IconComboItemCollection was changed - add, delete, remove, or clear.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ChangeType() As IconComboBox.IconComboItemCollectionChangeType
Get
Return m_ChangeType
End Get
End Property
''' <summary>
''' The item that was changed resulting in this event.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ChangedItem() As IconComboBox.IconComboItem
Get
Return m_ChangedItem
End Get
End Property
''' <summary>
''' The index of the changed item in the IconComboItemCollection.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property ChangedIndex() As Integer
Get
Return m_ChangedIndex
End Get
End Property
''' <summary>
''' Create a new instance of these event args with the specified arguments
''' </summary>
''' <param name="argidx">Index of the changed item</param>
''' <param name="argType">How the item was changed. <seealso>IconComboBox.IconComboItemCollectionChangeType</seealso></param>
''' <param name="argItem">The <see>IconComboItem</see> that was changed</param>
''' <remarks></remarks>
Public Sub New(ByVal argidx As Integer, ByVal argType As IconComboBox.IconComboItemCollectionChangeType, ByVal argItem As IconComboBox.IconComboItem)
m_ChangeType = argType
m_ChangedItem = argItem
m_ChangedIndex = argidx
End Sub
End Class
#End Region
End Class