Question Listview Item Color

Semola

Member
Joined
Aug 27, 2018
Messages
6
Programming Experience
3-5
Hi All,

I need your help: i need to change background color of listview items after created it. Specifically: when creating the listview I colored the lines alternately with two different colors (two different shades of gray).
Clicking on the column header reorder the rows according to a different criterion depending on the column. This rearrangement means that the alternation of colors is no longer guaranteed.I would like to recolor the items exactly as in the creation phase keeping the sort.

Can anyone help me?

Thanks
Semola
 
Last edited:

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,437
Location
Norway
Programming Experience
10+
After you sort loop through the items and set BackColor for each again, for example:
For Each item As ListViewItem In Me.ListView1.Items
    item.BackColor = If(item.Index Mod 2 = 0, Color.LightBlue, Color.LightGreen)
Next
 

Semola

Member
Joined
Aug 27, 2018
Messages
6
Programming Experience
3-5
Hi, testing your code, I noticed that I had a problem with the sortview of the listview. My listview contains a list of films grouped by initial title and there are 4 columns: title, rating, year and duration.Clicking on the columns should cause sorting in this way: Group (Descending) -> Title (Descending) -> Group (Ascending) -> Title (Ascending).
If I click on the "Title" column everything works correctly (including colors), if I click on another column the sorting works but the colors remain untidy.
I am convinced that I have mistaken the sorting algorithm (I ordered by group and, subsequently, by item but I do not know how to order at the same time for both types.) I quote the part of the code I wrote for completeness

Thanks
Semola


Private Sub lvFilmDetail_ColumnClick(sender As Object, e As ColumnClickEventArgs) Handles lvFilmDetail.ColumnClick


        swc_item_checked = False
        If ws_column_svd <> e.Column Then
            ws_group_sorting = SortOrder.Ascending
            If e.Column = 1 Then


                ws_item_sorting = SortOrder.Descending
            Else


                ws_item_sorting = SortOrder.Ascending
            End If


            ctr_sort = 0
            ws_column_svd = e.Column
        Else
            If lvFilmDetail.ShowGroups Then
                Dim ws_mod_sort = ctr_sort Mod 4


                Select Case ws_mod_sort
                    Case 0, 2
                        ws_item_sorting = ImpostaSorting(ws_item_sorting)
                    Case 1, 3
                        ws_group_sorting = ImpostaSorting(ws_group_sorting)
                        ws_item_sorting = ImpostaSorting(ws_item_sorting)


                End Select
            Else


                ws_item_sorting = ImpostaSorting(ws_item_sorting)
            End If


        End If


        If lvFilmDetail.ShowGroups Then
            Dim ws_mod_sort = ctr_sort Mod 4


            Select Case ws_mod_sort
                Case 0, 2
                    lvFilmDetail.ListViewItemSorter = New CompareByName(e.Column, ws_item_sorting)
                    lvFilmDetail.Sort()
                Case 1, 3
                    Dim listGroups As List(Of ListViewGroup) = lvFilmDetail.Groups.Cast(Of ListViewGroup).ToList
                    listGroups.Sort(New ListViewGroupHeaderSorter(ws_group_sorting))
                    lvFilmDetail.Groups.Clear()
                    lvFilmDetail.Groups.AddRange(listGroups.ToArray)
                    lvFilmDetail.ListViewItemSorter = New CompareByName(e.Column, ws_item_sorting)
                    lvFilmDetail.Sort()
            End Select
            ctr_sort += 1


        Else
            lvFilmDetail.ListViewItemSorter = New CompareByName(e.Column, ws_item_sorting)
            lvFilmDetail.Sort()
        End If


        For Each item As ListViewItem In lvFilmDetail.Items
            item.BackColor = If(item.Index Mod 2 = 0, Color.FromArgb(255, 240, 240, 240), Color.FromArgb(255, 210, 210, 210))
        Next


        swc_item_checked = True
    End Sub


    Private Function ImpostaSorting(ByVal InSorting As SortOrder) As SortOrder


        If InSorting = SortOrder.Ascending Then


            Return SortOrder.Descending
        Else


            Return SortOrder.Ascending
        End If


    End Function

'--------------------------------

Class CompareByName
    Implements IComparer
    Dim index As Integer
    Dim m_sortorder As SortOrder


    Sub New(ByVal subItemIndex As Integer, ByVal sortOrder As SortOrder)
        index = subItemIndex
        m_sortorder = sortOrder
    End Sub


    Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare


        Dim item1 As ListViewItem = CType(x, ListViewItem)
        Dim item2 As ListViewItem = CType(y, ListViewItem)


        If m_sortorder = SortOrder.Ascending Then


            Return String.Compare(item1.SubItems(index).Text, item2.SubItems(index).Text)


        Else
            Return String.Compare(item2.SubItems(index).Text, item1.SubItems(index).Text)


        End If
    End Function
End Class


Class CompareByDate
    Implements IComparer
    Dim index As Integer
    Dim m_sortorder As SortOrder


    Sub New(ByVal subItemIndex As Integer, ByVal sortOrder As SortOrder)
        index = subItemIndex
        m_sortorder = sortOrder
    End Sub


    Public Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
        Dim item1 As ListViewItem = CType(x, ListViewItem)
        Dim item2 As ListViewItem = CType(y, ListViewItem)


        If m_sortorder = SortOrder.Ascending Then


            Return Date.Compare(Date.Parse(item1.SubItems(index).Text), Date.Parse(item2.SubItems(index).Text))


        Else
            Return Date.Compare(Date.Parse(item2.SubItems(index).Text), Date.Parse(item1.SubItems(index).Text))


        End If


    End Function


End Class


Public Class ListViewGroupHeaderSorter
    Implements IComparer(Of ListViewGroup)


    Dim RequiredSortOrder As SortOrder


    Public Sub New(ByRef RequiredOrder As SortOrder)
        RequiredSortOrder = RequiredOrder
    End Sub


    Public Function Compare(ByVal FirstHeader As ListViewGroup, ByVal SecondHeader As ListViewGroup) As Integer Implements IComparer(Of ListViewGroup).Compare
        If RequiredSortOrder = SortOrder.Ascending Then
            Return String.Compare(FirstHeader.Header, SecondHeader.Header)
        Else
            Return String.Compare(SecondHeader.Header, FirstHeader.Header)
        End If
    End Function
End Class



 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,335
Location
Sydney, Australia
Programming Experience
10+
The order of the Items collection does not necessarily match what you see if the items are grouped. Without having tested it, I think that something like this should work:
Dim itemBackColor = Color.LightBlue

For Each group As ListViewGroup In ListView1.Groups
    For Each item As ListViewItem In group.Items
        item.BackColor = itemBackColor

        'Toggle the colour for the next item.
        itemBackColor = If(itemBackColor = Color.LightBlue,
                           Color.LightGreen,
                           Color.LightBlue)
    Next
Next
 

Semola

Member
Joined
Aug 27, 2018
Messages
6
Programming Experience
3-5
I have the same problem.
I have solved with this code, but I read all listview items (excluding all those who have a different group).

        Dim itemBackColor = Color.FromArgb(255, 240, 240, 240)
        For Each group As ListViewGroup In lvFilmDetail.Groups
            For Each item As ListViewItem In lvFilmDetail.Items
                If item.Group.Header = group.Header Then
                    item.BackColor = itemBackColor
                    itemBackColor = If(itemBackColor = Color.FromArgb(255, 240, 240, 240),
                               Color.FromArgb(255, 210, 210, 210), Color.FromArgb(255, 240, 240, 240))
                End If
            Next
        Next


Is there a way to avoid this logic?

Thanks
Semola
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,335
Location
Sydney, Australia
Programming Experience
10+
but I read all listview items (excluding all those who have a different group).
I don't know what that means.
Is there a way to avoid this logic?
Avoid what logic? That code does what it does. If you want to do something else then use other code. I don't know what you want to do exactly so I don't know what code would do it.

One thing I would say is don't ever do what you're doing there and keep repeating the same thing over and over like that. There's no good reason to call Color.FromArgb repeatedly when you know that the results will be the same every time. Do it once, assign the result to a variable and then use that over and over.
Dim firstColor = Color.FromArgb(240, 240, 240)
Dim secondColor = Color.FromArgb(210, 210, 210)
Dim itemBackColor = firstColor

For Each group As ListViewGroup In lvFilmDetail.Groups
    For Each item As ListViewItem In lvFilmDetail.Items
        If item.Group.Header = group.Header Then
            item.BackColor = itemBackColor
            itemBackColor = If(itemBackColor = firstColor,
                               secondColor,
                               firstColor )
        End If
    Next
Next

Note also that there's no need to specify the alpha component unless you want transparency because it defaults to 255.
 

Semola

Member
Joined
Aug 27, 2018
Messages
6
Programming Experience
3-5
Right, I had not thought about it. I'm not used to writing a lot of code (as in this case) and I would have optimized it all at a later time

Thank You.
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,437
Location
Norway
Programming Experience
10+
Just a tip for code you posted, you can compare the group objects rather than their header strings:
If item.Group Is group Then

As for another logic, the group items are not returned in sort order, but their Index order is valid, so you can do this:
For Each group As ListViewGroup In Me.ListView1.Groups
    Dim sorted = (From item As ListViewItem In group.Items Order By item.Index).ToArray

    For i = 0 To sorted.Count - 1
        sorted(i).BackColor = If(i Mod 2 = 0, Color.LightBlue, Color.LightGreen)
    Next
Next

Thanks to jmcilhinney for pointing this out in another forum not long ago :)
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,335
Location
Sydney, Australia
Programming Experience
10+
Thanks to jmcilhinney for pointing this out in another forum not long ago :)
I recall the thread you're talking about but not it's content. I do recall I spent a fair bit of time testing on that occasion because things did not work at all as expected.
 
Top Bottom