Solved

How to swap or move items around in ListView ?

Posted on 2001-07-07
23
611 Views
Last Modified: 2007-11-27
You know how some apps have a list of items in a ListView, and you can click Up/Down buttons to move the item up or down the list?  I'd like to be able to do that.. but I don't see any easy way to swap items in a ListView.  How can I do this?

Thanks.
0
Comment
Question by:MDesigner
  • 8
  • 4
  • 3
  • +6
23 Comments
 

Expert Comment

by:awjackin35
ID: 6261954
Hey MDesigner

    Here is a good example with comments in the code and the text file is at the end.

Option Explicit
Private mintAlbumID As Integer    'Album ID number
Private mstrStyle As String       'Musical style

Private Sub cmdAdd_Click()
'Add the current album title to the list.
 
  If mstrStyle = "" Then
    MsgBox "Select a style before adding a title.", vbInformation, ""
    Exit Sub
  End If
 
  Call AddListItem(txtTitle.Text, mstrStyle)
End Sub

Private Sub AddListItem(strTitle As String, strStyle As String)
'Add a new row to the ListView control. Automatically increment
'the album ID. A ListView key cannot start with a digit,
'so we prepend a "K".

  Dim mItem As ListItem, strKey As String
  mintAlbumID = mintAlbumID + 1
  strKey = Format(mintAlbumID, "000")
 
  With lvwTitles.ListItems
    Set mItem = .Add(, "K" & strKey)    'index,key
    mItem.Text = strKey                 'album ID
    mItem.SubItems(1) = strTitle        'album title
    mItem.SubItems(2) = strStyle        'musical style
  End With
End Sub

Private Sub cmdLoad_Click()
'Load a list of titles and styles from a text file.

  Dim fileNum As Integer, strTitle As String, _
      strStyle As String
  On Error GoTo LoadFileErr
  fileNum = FreeFile
  Open App.Path + "\titles.txt" For Input As fileNum
  Do Until EOF(fileNum)
    Input #fileNum, strTitle, strStyle
    Call AddListItem(strTitle, strStyle)
  Loop
  Close fileNum
  Exit Sub
LoadFileErr:
  MsgBox Error$, vbCritical, ""
End Sub

Private Sub Form_Load()
 
'Set up the column headers with titles and default widths.
  With lvwTitles
    .ColumnHeaders.Add , , "CD #", 600   'index,key,text,width
    .ColumnHeaders.Add , , "Title", 3800
    .ColumnHeaders.Add , , "Style", (.Width - 800 - 3800)
    .View = lvwReport                 'show all column details
  End With
   
'Sort the ListView on the first column (the album ID),
'and allow the sort order to be changed at runtime.
  lvwTitles.Sorted = True
 
'Enable the following line to display check boxes on each line.
  'lvwTitles.Checkboxes = True
 
'Enable the following line to allow the user to interactively
'edit labels. If you do this, disable the message box in the
'ListView's Click event.
  'lvwTitles.LabelEdit = lvwAutomatic
         
'Set up the toolbar button styles.
  Dim btn As Button
  With tlbStyle
    .TextAlignment = tbrTextAlignRight
    .Style = tbrFlat
  End With
 
'Add group buttons to the toolbar, making each part of
'a button group.
  With tlbStyle.Buttons
    Set btn = .Add(, "Classical", "Classical")
    btn.Style = tbrButtonGroup
    Set btn = .Add(, "Jazz", "Jazz")
    btn.Style = tbrButtonGroup
    Set btn = .Add(, "Rock", "Rock")
    btn.Style = tbrButtonGroup
    Set btn = .Add(, "Swing", "Swing")
    btn.Style = tbrButtonGroup
    Set btn = .Add(, "Ethnic", "Ethnic")
    btn.Style = tbrButtonGroup
    Set btn = .Add(, "Movies", "Movies")
    btn.Style = tbrButtonGroup
  End With
End Sub

Private Sub lvwTitles_ColumnClick(ByVal ColumnHeader As _
   MSComctlLib.ColumnHeader)
'Sort the items using the selected column whose header was
'just clicked.
   lvwTitles.SortKey = ColumnHeader.Index - 1
End Sub

Private Sub lvwTitles_ItemCheck(ByVal Item As MSComctlLib.ListItem)
  If Item.Checked Then
     MsgBox "CD number " & Item.Text & " has been checked"
  Else
     MsgBox "CD number " & Item.Text & " has been unchecked"
  End If
End Sub

Private Sub lvwTitles_Click()
'The user clicked on an ID number.
  MsgBox "CD number " & lvwTitles.SelectedItem.Text & " has been selected"
End Sub

Private Sub tlbStyle_ButtonClick(ByVal Button As _
            MSComctlLib.Button)
'The button key is the musical style.
  mstrStyle = Button.Key
End Sub

Private Sub txtTitle_Change()
'Enable the Add button only when there is a title
'in the text box.
  cmdAdd.Enabled = Len(txtTitle.Text) > 0
End Sub

This is all of the code. Try this example in your compiler.

Here is the text file that comes with it.
Herbie Hancock Live,Jazz
Segovia - Andalusia,Classical
Hendrix's Purple Haze,Rock
The Glenn Miller Band,Swing
Fisk Plays Paganini,Classical
Javanese Gamelan Music,Ethnic
Billy Idol's Greatest Hits,Rock
Perlman - Brahms Concerto,Classical
Tibetan Chants,Ethnic
Titanic Soundtrack,Movies

Here is the problem:

Have the user create a program that lets the user input a music CD titles, select a style category, and add each title to the list. A listr of titles can also be loaded from a text file.  


Hope this helps!!!!!!!!!!!!!
0
 

Author Comment

by:MDesigner
ID: 6261965
awjackin:

Thanks..this is useful, but not quite what I was after.

Let's say you have a list in a ListView, in this order:

peas
carrots
potatoes
soup
ravioli

I want to be able to select "soup" and click a CommandButton called "Up" which moves "soup" up the list.  How can I pull this off?

Thanks..
0
 

Author Comment

by:MDesigner
ID: 6261977
Actually, I do have one side-question for you since you wrote the sorting program.  You know how in a lot of Microsoft apps, when you click a column to sort it, there's an arrow pointing down.. and if you click again, it sorts the other direction and the arrow points up.  Is that easy to do?
Thanks.
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6261996
MDesigner, I think you will have to remove the item and then add it again in the correct spot.  

As for sorting, the ListView allows sorting by column, here is the MSDN example.  (requires reference to DAO and path to Biblio.mdb)

Option Explicit
Private Sub Option1_Click(Index As Integer)
   ' These OptionButtons offer two choices: Ascending (Index 0),
   ' and Descending (Index 1). Clicking on one of these
   ' sets the SortOrder for the ListView control.
   ListView1.SortOrder = Index
   ListView1.Sorted = True ' Sort the List.
End Sub

Private Sub Form_Load()
   ' Create an object variable for the ColumnHeader object.
   Dim clmX As ColumnHeader
   ' Add ColumnHeaders. The width of the columns is the width
   ' of the control divided by the number of ColumnHeader objects.
   Set clmX = ListView1.ColumnHeaders. _
   Add(, , "Company", ListView1.Width / 3)
   Set clmX = ListView1.ColumnHeaders. _
   Add(, , "Address", ListView1.Width / 3)
   Set clmX = ListView1.ColumnHeaders. _
   Add(, , "Phone", ListView1.Width / 3)

   ListView1.BorderStyle = ccFixedSingle ' Set BorderStyle property.
   ListView1.View = lvwReport ' Set View property to Report.

   ' Label OptionButton controls with SortOrder options.
      Option1(0).Caption = "Ascending (A-Z)"
      Option1(1).Caption = "Descending (Z-A)"
      ListView1.SortOrder = lvwAscending ' Sort ascending.

   ' Create object variables for the Data Access objects.
   Dim myDb As Database, myRs As Recordset
   ' Set the Database to the BIBLIO.MDB database.
   Set myDb = DBEngine.Workspaces(0).OpenDatabase("C:\My Documents\Biblio.mdb")
   ' Set the recordset to the Publishers table.
   Set myRs = myDb.OpenRecordset("Publishers", dbOpenDynaset)
     
   ' Create a variable to add ListItem objects.
   Dim itmX As ListItem

   ' While the record is not the last record, add a ListItem object.
   ' Use the Name field for the ListItem object's text.
   ' Use the Address field for the ListItem object's subitem(1).
   ' Use the Phone field for the ListItem object's subitem(2).

   While Not myRs.EOF
      Set itmX = ListView1.ListItems.Add(, , CStr(myRs!Name))

      ' If the Address field is not Null, set subitem 1 to the field.
      If Not IsNull(myRs!Address) Then
         itmX.SubItems(1) = CStr(myRs!Address)  ' Address field.
      End If

      ' If the Phone field is not Null, set subitem 2 to the field.
      If Not IsNull(myRs!Telephone) Then
         itmX.SubItems(2) = myRs!Telephone  ' Phone field.
      End If

      myRs.MoveNext   ' Move to next record.
   Wend
End Sub

Private Sub ListView1_ColumnClick(ByVal ColumnHeader As ColumnHeader)
   ' When a ColumnHeader object is clicked, the ListView control is
   ' sorted by the subitems of that column.
   ' Set the SortKey to the Index of the ColumnHeader - 1
   ListView1.SortKey = ColumnHeader.Index - 1
   ' Set Sorted to True to sort the list.
   ListView1.Sorted = True
End Sub



0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6262000
>>
when you click a column to sort it, there's an arrow pointing down.. and if
you click again, it sorts the other direction and the arrow points up.  Is that easy to do?
Thanks.
<<

In this case you just toggle the ListView1.SortOrder property from 0 to 1.
0
 

Expert Comment

by:vbfan
ID: 6262041
You're wrong all!
See the question carefully,he want to swap two items in the listview,not sort!
Maybe you can delete the item then add the item you want to swap...
0
 
LVL 5

Expert Comment

by:KDivad
ID: 6262049
vbfan, you missed a comment:

Comment
From: MDesigner  Date: 07/07/2001 09:29PM PST  
Actually, I do have one side-question for you since you wrote the sorting program.  You know how in a lot of Microsoft apps, when you click a column to sort it, there's an arrow pointing down.. and if you click again, it sorts the other direction and the arrow points up.  Is that easy to do?
Thanks.  
0
 

Expert Comment

by:awjackin35
ID: 6262457
This program example will do the arrows if you keep hitting the add button and adding files to the box.

0
 
LVL 12

Expert Comment

by:jgv
ID: 6262758
Can't help with the arrow on the column header (don't think that's possible) but this will give you an idea of how to move a node in a Listview up or down.

Start a new project, add a listview and two command buttons. The command buttons must be part of a control array named "cmdMoveItem". Index 0 is "Up" and index 1 is "Down". Paste this code:

Private Sub Form_Load()
    Dim lvwItem As ListItem
    Dim i As Integer
   
    With ListView1
        .View = lvwReport
        .HideSelection = False
        .ColumnHeaders.Add , , "Column 1"
        .ColumnHeaders.Add , , "Column 2"
        .ColumnHeaders.Add , , "Column 3"
    End With
   
    For i = 1 To 20
        Set lvwItem = ListView1.ListItems.Add
        With lvwItem
            .Text = "Text " & i
            .SubItems(1) = "Item " & i & "  SubItem 1"
            .SubItems(2) = "Item " & i & "  SubItem 2"
            .Tag = "Tag " & i
            .Key = "Key " & i
        End With
    Next
           
End Sub

Private Sub cmdMoveItem_Click(Index As Integer)
    Dim lvwNode As ListItem
   
    If Not ListView1.SelectedItem Is Nothing Then
       
        Set lvwNode = ListView1.SelectedItem
       
        Select Case Index
            Case 0 'Move Up
                If lvwNode.Index > 1 Then
                    SwapItems lvwNode.Index, lvwNode.Index - 1
                End If
           
            Case 1 'Move Down
                If lvwNode.Index < ListView1.ListItems.Count Then
                    SwapItems lvwNode.Index, lvwNode.Index + 1
                End If
        End Select
       
    End If
End Sub

Private Sub SwapItems(iSelIndex As Integer, iNewIndex As Integer)
   
    Dim lvwSelNode As ListItem 'Selected list item
    Dim lvwNewNode As ListItem 'List item to swap with
    Dim sText As String
    Dim sSubItem As String
    Dim sKey As String
    Dim sTag As String
    Dim sKeyNew As String
    Dim sKeySel As String
    Dim i As Integer
   
    'Set a reference to each node
    Set lvwSelNode = ListView1.ListItems(iSelIndex)
    Set lvwNewNode = ListView1.ListItems(iNewIndex)
   
    'Swap the Text of each node
    sText = lvwNewNode.Text
    lvwNewNode.Text = lvwSelNode.Text
    lvwSelNode.Text = sText
   
    'Swap the Keys of each node; these values must be unique
    'so we need two variables to store the values
    sKeyNew = lvwNewNode.Key
    sKeySel = lvwSelNode.Key
    lvwNewNode.Key = ""
    lvwSelNode.Key = sKeyNew
    lvwNewNode.Key = sKeySel
   
    'Swap the Tag of each node
    sTag = lvwNewNode.Tag
    lvwNewNode.Tag = lvwSelNode.Tag
    lvwSelNode.Tag = sTag
   
    'Swap all SubItems (if any) of each node
    For i = 1 To ListView1.ColumnHeaders.Count - 1
        sSubItem = lvwNewNode.SubItems(i)
        lvwNewNode.SubItems(i) = lvwSelNode.SubItems(i)
        lvwSelNode.SubItems(i) = sSubItem
    Next
   
    'Keep the selection on the proper node
    ListView1.ListItems(iNewIndex).Selected = True
End Sub


0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6263041
This is how I code up and down

Private Sub cmdDown_Click()
    Dim lngIndex As Long
    Dim i As Integer
    Dim itmMove As ListItem
    Dim itmNew As ListItem
    Dim subMove As ListSubItem
   
    Set itmMove = ListView1.SelectedItem
    lngIndex = itmMove.Index
    If lngIndex = ListView1.ListItems.Count Then Exit Sub   'cannot move last one down
   
    ListView1.Sorted = False
   
    Set itmNew = ListView1.ListItems.Add(lngIndex + 2, , itmMove.Text)
   
    For i = 1 To ListView1.ColumnHeaders.Count - 1
        itmNew.SubItems(i) = itmMove.SubItems(i)
    Next
    ListView1.ListItems.Remove (lngIndex)

End Sub

Private Sub cmdUp_Click()
    Dim lngIndex As Long
    Dim i As Integer
    Dim itmMove As ListItem
    Dim itmNew As ListItem
    Dim subMove As ListSubItem
   
    Set itmMove = ListView1.SelectedItem
    lngIndex = itmMove.Index
    If lngIndex = 1 Then Exit Sub  'cannot move first one up
   
    ListView1.Sorted = False
'    ListView1.ColumnHeaders.Count
   
   
   
    Set itmNew = ListView1.ListItems.Add(lngIndex - 1, , itmMove.Text)
   
    For i = 1 To ListView1.ColumnHeaders.Count - 1
        itmNew.SubItems(i) = itmMove.SubItems(i)
    Next
    ListView1.ListItems.Remove (lngIndex + 1) 'we have added one before this.

End Sub
0
 

Author Comment

by:MDesigner
ID: 6263319
It's tough to choose when two people post good answers...

PaulHews's is short and to the point.  jgv's is a bit longer but more modular and also takes care of swapping keys/tags.

I'll come back later after I try these bits of code in my app...

Thanks, guys
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 38

Expert Comment

by:PaulHews
ID: 6263340
>also takes care of
swapping keys/tags.<

Change this:

Set itmNew = ListView1.ListItems.Add(lngIndex - 1, , itmMove.Text)


To:

Set itmNew = ListView1.ListItems.Add(lngIndex + 2, itmMove.Key, itmMove.Text)
itmNew.Tag = itmMove.Tag

You also have the advantage with this that you can move it anywhere in the list without needing an item to swap it with.
0
 
LVL 38

Accepted Solution

by:
PaulHews earned 25 total points
ID: 6263357
Actually make it compatible with control arrays for conciseness:

Private Sub cmdUpDown_Click(Index As Integer)
    Dim lngIndex As Long
    Dim intOffset As Integer
    Dim i As Integer
    Dim itmMove As ListItem
    Dim itmNew As ListItem
    Dim subMove As ListSubItem
    intOffset = Index + 1
    Set itmMove = ListView1.SelectedItem
    lngIndex = itmMove.Index
   
    If Index Then
        If lngIndex = ListView1.ListItems.Count Then Exit Sub  'cannot move last one down
    Else
        If lngIndex = 1 Then Exit Sub 'cannot move first one up
    End If
   
   
    ListView1.Sorted = False
    If Index Then  ''-1 up +2 down
        Set itmNew = ListView1.ListItems.Add(lngIndex + 2, itmMove.Key, itmMove.Text) 'up
    Else
        Set itmNew = ListView1.ListItems.Add(lngIndex - 1, itmMove.Key, itmMove.Text) 'down
    End If
   
    itmNew.Tag = itmMove.Tag
   
    For i = 1 To ListView1.ColumnHeaders.Count - 1
        itmNew.SubItems(i) = itmMove.SubItems(i)
    Next
    If Index Then
        ListView1.ListItems.Remove lngIndex
    Else
        ListView1.ListItems.Remove lngIndex + 1  'we have added one before this.
    End If
End Sub
0
 
LVL 12

Expert Comment

by:jgv
ID: 6263597
Not as smooth as the first one I put up but here is a condensed version :)

Private Sub cmdMoveItem_Click(Index As Integer)
   
    Dim iSelIndex As Integer
    Dim iNewIndex As Integer
    Dim i As Integer
    Dim itmOld As ListItem
    Dim itmNew As ListItem
    Dim OldSubItems As ListSubItems
   
    If ListView1.SelectedItem Is Nothing Then Exit Sub
   
    iSelIndex = ListView1.SelectedItem.Index
    iNewIndex = IIf(Index, iSelIndex + 1, iSelIndex - 1)
   
    If iNewIndex = 0 Or iNewIndex > ListView1.ListItems.Count Then Exit Sub
   
    Set itmOld = ListView1.ListItems(iNewIndex)
    Set OldSubItems = itmOld.ListSubItems
   
    ListView1.ListItems.Remove (iNewIndex)
   
    Set itmNew = ListView1.ListItems.Add(iSelIndex, itmOld.Key, itmOld.Text)
    itmNew.Tag = itmOld.Tag
   
    For i = 1 To ListView1.ColumnHeaders.Count - 1
       itmNew.SubItems(i) = OldSubItems(i)
    Next

End Sub
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6263654
>Dim OldSubItems As ListSubItems

Cool!  I would have done that if I knew it was possible.
0
 
LVL 12

Expert Comment

by:jgv
ID: 6264869
Got the idea from you Paul

>> Dim subMove As ListSubItem

...is from your code; you just didn't use it after declaring it. I played around and found that you could actually retain all the subitem values in the variable after deleting the listitem. A collaborative effort :)
0
 

Author Comment

by:MDesigner
ID: 6265951
Who the heck am I supposed to give the points to now?! :)
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6266042
I'm game for a split if jgv is.  Like he says, it was a collaborative effort.  To split points, just post a zero pointer at community support topic area, giving them the link to this Q and explaining how you want to split points.  There are lots of moderators now, so it won't take long.
0
 
LVL 38

Expert Comment

by:PaulHews
ID: 6266048
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 6266724
I have processed this point split award for you.

jgv, please comment here for the other half.

http://www.experts-exchange.com/jsp/qManageQuestion.jsp?qid=20148045

Thank you,

Moondancer
Community Support Moderator @ Experts Exchange
0
 
LVL 3

Expert Comment

by:leojl
ID: 6267926
this is what makes EE great. real people helping real people.   leo
0
 
LVL 1

Expert Comment

by:Moondancer
ID: 6273166
You'r so right, Leo.  This is a most excellent team, and it's a pleasure to be part of it.
Moondancer
Community Support Moderator @ Experts Exchange
0
 

Expert Comment

by:ADawn
ID: 7858906
agreed!!!
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Introduction I needed to skip over some file processing within a For...Next loop in some old production code and wished that VB (classic) had a statement that would drop down to the end of the current iteration, bypassing the statements that were c…
Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now