CodeKicks.com
Focus on Microsoft Technologies - Tutorials, Articles, Code Samples.

Monday, September 25, 2006

Movable ItemsListBox in .NET

Description: A simple extension of the Windows Forms ListBox control that provides a method allowing easy movement of a selected item up or down in the list.

Imports System.Windows.Forms
'
'Author: Andrew Ransom (andrewransom@gmail.com)
'
'License: Do whatever you like with this code!
'
''' <summary>
''' A simple extension of the Windows Forms ListBox
''' control that provides a
''' method allowing easy movement of a selected
''' item up or down in the list.
''' </summary>
''' <remarks>
''' Use the MoveSelectedItem() method to move the item up or down
''' in the list. This
''' will only work if a single item is selected, the 'Sorted'
''' property is set to False,
''' and the listbox is NOT bound to a datasource.
''' </remarks>
Public Class ARMovableItemsListBox
Inherits ListBox


Private _IsDoingMove As Boolean = False
Public Enum ListItemMoveDirection As Integer
Up = 1
Down = 2
End Enum
Protected Overrides Sub OnPaint(ByVal e As
System.Windows.Forms.PaintEventArgs)
'Don't repaint if we're in the middle of a move.
'Note: No quantitative
‘testing has
'been done to see if this actually makes much of a difference. :p
If (_IsDoingMove = True) Then Return
MyBase.OnPaint(e)
End Sub
''' <summary>
''' Moves the selected item in the ListBox up or down
''' </summary>
''' <param name="moveDirection">The direction to move the selected item,
‘’’ either up or down.</param>
''' <remarks>
''' If the selected item is already at the top and the
''' move direction is up,
‘’’ nothing happens.
''' Likewise, if the selected item is already at the bottom and the move
‘’’ direction is down.
'''
''' This method throws two exceptions:
''' 1. If the ListBox's 'Sorted' property is set to True,
''' this method won't work
''' <br />
''' 2. If the ListBox is bound to a datasource, the 'Items'
''' collection cannot
‘’’ be modified, so
''' a check for this might throw an exception.
''' </remarks>
Public Sub MoveSelectedItem(ByVal moveDirection As ListItemMoveDirection)

Dim selectedItem As Object
Dim selectedIndex As Integer
Dim copiedItemList As ArrayList
Dim itemCount, i As Integer
Dim topIndex As Integer
'Some initial checking:
If (MyBase.Sorted = True) Then
Throw New Exception("Items can only be moved in a Listbox
that is not sorted. Set the 'Sorted' Property to False")
Return
End If
If (Not MyBase.DataSource Is Nothing) Then
Throw New Exception("Items can only be moved in a Listbox that is not bound
to a data source")
Return
End If
itemCount = MyBase.Items.Count
topIndex = MyBase.TopIndex
'With none, or only one item in the listbox, there's nowhere to move it to
If (itemCount <= 1) Then
Return
End If
selectedIndex = MyBase.SelectedIndex
'If nothing is selected, then just cancel out
If (selectedIndex = -1) Then
Return
End If
'If the selected item is already at the top, then it can't go any further up
If (moveDirection = ListItemMoveDirection.Up) AndAlso (selectedIndex = 0) Then
Return
End If
'Likewise, if the selected item is already at the top, then it can't go any further
If (moveDirection = ListItemMoveDirection.Down) AndAlso
(selectedIndex = (MyBase.Items.Count - 1)) Then
Return
End If
selectedItem = MyBase.SelectedItem
Try
_IsDoingMove = True
copiedItemList = New ArrayList(itemCount)
'This can probably be done much more efficiently, but I'm not an experit
'on list optimization! One no-so-difficult possibility would be to use
'AddRange() for the unmoved items instead. Performance for me is acceptable
'up to about 2000-3000 items, and I would very rarely, if ever have that many.
If (moveDirection = ListItemMoveDirection.Up) Then
'Replace the previous item with the selected item
For i = 0 To (itemCount - 1)
If (i = selectedIndex - 1) Then
'add the selected item
copiedItemList.Add(selectedItem)
'then also add the next item
copiedItemList.Add(MyBase.Items(i))
i += 1 'to skip to the NEXT record after the 2 affected ones
Else
copiedItemList.Add(MyBase.Items(i))
End If
Next
Else
'Replace the following item with the next one
For i = 0 To (itemCount - 1)
If (i = selectedIndex) Then
'Copy the lower object to the temp array first
copiedItemList.Add(MyBase.Items(i + 1))
'then the selected on
copiedItemList.Add(selectedItem)
i += 1 'skip on to the next record
Else
copiedItemList.Add(MyBase.Items(i))
End If
Next
End If
'Clear out the listbox
MyBase.Items.Clear()
'and add back the contents of the copied arraylist
MyBase.Items.AddRange(copiedItemList.ToArray)
'go back to where we started
MyBase.TopIndex = topIndex
MyBase.SelectedItem = selectedItem
_IsDoingMove = False
MyBase.Invalidate()
Finally
_IsDoingMove = False
End Try
End Sub
End Class

Post a Comment