locked
How do I override a ListViews selected item color RRS feed

  • Question

  • As you know,  when you load items into a ListView that has it's .view property set to Details, and it's FullRowSelect property set to True,  the selected row is highlighted when you click on it.

    By default the highlight color is a dark blue.

    I want to change it to whatever color I choose.

    If I wanted to update each item as I arrow keyed my way through the rows I'd have to write a complicated routine to handle the key presses and update the position of the hightlight color.

    Even then I think the new color would be overridden by the default or system color.

    My question is How do I change the default highlight color on a ListView control that has it's .View set to Details, and it's FullRowSelect set to True?


    Saturday, February 28, 2009 2:11 AM

Answers

  • With owner draw you have to do all the drawing that includes the text . You would do that in the DrawSubitem event handler .

     Private Sub listView1_DrawSubItem(ByVal sender As Object, _     
            ByVal e As DrawListViewSubItemEventArgs) _     
            Handles ListView1.DrawSubItem     
        
        
            Dim sf As New StringFormat()     
            Try    
        
                ' Store the column text alignment, letting it default        
                ' to Left if it has not been set to Center or Right.        
                Select Case e.Header.TextAlign     
                    Case HorizontalAlignment.Center     
                        sf.Alignment = StringAlignment.Center    
                    Case HorizontalAlignment.Right     
                        sf.Alignment = StringAlignment.Far   
                End Select    
        
                e.Graphics.DrawString(e.SubItem.Text, _     
                                   Me.ListView1.Font, Brushes.Black, e.Bounds, sf)     
        
        
            Finally    
                sf.Dispose()     
            End Try    
        End Sub    
     


    Coding for fun

    Sunday, March 1, 2009 1:20 AM

All replies

  • Thanks for the link, it's interesting.

    I can't get that approach to work with a ListView though.

    I'm not sure how to set up the class,  because I don't know if the backcolor is readonly,  or something else.

    Even if I did I don't think I could write the class or procedure correctly,  because it's beyond the scope of my Visual Basic knowledge.

    It seems that it would be as simply as ListView1.FocusedItem.BackColor = Color.Coral

    Unfortunately it's not at all that simple, and there is no place on the planet that I know of where I can find the information I need to form a strategy for tackling this task.






    Saturday, February 28, 2009 3:46 AM
  • Does anyone have a clue as to how to set this property?

    Anyone?
    Saturday, February 28, 2009 4:12 AM
  • Sorry that was the wrong link I don't know what I was thinking :-) this is the right link

    http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvb/thread/dc4a374f-e2c7-4dbd-8e09-835f36e3e803
    Coding for fun
    Saturday, February 28, 2009 5:00 AM
  • bdbodger said:

    Sorry that was the wrong link I don't know what I was thinking :-) this is the right link

    http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvb/thread/dc4a374f-e2c7-4dbd-8e09-835f36e3e803


    Coding for fun


    I tried that,  it didn't do anything.

    Well, it threw a few errors,  then I removed the part that was looking for 0 currency values and my program ran, but there was no noticeable affect.

    I don't understand why something as simple as changing the background color of a selected row in a detail view would require more code than there is in my entire program.
    Saturday, February 28, 2009 5:53 AM
  • Did you set the Listview owner draw property to true ? I know it works you just have to set that property , use detail view and handle the DrawItem event the DrawSubItem event and DrawColumnHeader events . This code changes the selected item background color to maroon . If the item is not selected then the background color will be a gradient .

     
    Private Sub listView1_DrawItem(ByVal sender As Object, _     
            ByVal e As DrawListViewItemEventArgs) _     
            Handles listView1.DrawItem     
        
            If Not (e.State And ListViewItemStates.Selected) = 0 Then    
        
                ' Draw the background for a selected item.     
                e.Graphics.FillRectangle(Brushes.Maroon, e.Bounds)     
                e.DrawFocusRectangle()     
        
            Else    
        
                ' Draw the background for an unselected item.     
                Dim brush As New LinearGradientBrush(e.Bounds, Color.Orange, _     
                    Color.Maroon, LinearGradientMode.Horizontal)     
                Try    
                    e.Graphics.FillRectangle(brush, e.Bounds)     
                Finally    
                    brush.Dispose()     
                End Try    
        
            End If    
        
            ' Draw the item text for views other than the Details view.     
            If Not Me.listView1.View = View.Details Then    
                e.DrawText()     
            End If    
        
        End Sub    
     

    Coding for fun
    Saturday, February 28, 2009 6:16 AM
  • Use this at the top of your form so the gradiantbrush does not give you an error 

    Imports System.Drawing.Drawing2D


    and don't forget that the built in formater in the forum does not show the underscore so the code is like this in reality

    Private Sub listView1_DrawItem(ByVal sender As Object, _
    ByVal e As DrawListViewItemEventArgs) _
    Handles ListView1.DrawItem

    If Not (e.State And ListViewItemStates.Selected) = 0 Then

    ' Draw the background for a selected item.

    e.Graphics.FillRectangle(Brushes.Maroon, e.Bounds)

    e.DrawFocusRectangle()

    Else

    ' Draw the background for an unselected item.

    Dim brush As New LinearGradientBrush(e.Bounds, Color.Orange, _
    Color.Maroon, LinearGradientMode.Horizontal)

    Try

    e.Graphics.FillRectangle(brush, e.Bounds)

    Finally

    brush.Dispose()

    End Try

    End If

    ' Draw the item text for views other than the Details view.

    If Not Me.ListView1.View = View.Details Then

    e.DrawText()

    End If

    End Sub


    Coding for fun
    • Edited by bdbodger Saturday, February 28, 2009 6:33 AM
    Saturday, February 28, 2009 6:29 AM
  • bdbodger said:

    Did you set the Listview owner draw property to true ? I know it works you just have to set that property , use detail view and handle the DrawItem event the DrawSubItem event and DrawColumnHeader events . This code changes the selected item background color to maroon . If the item is not selected then the background color will be a gradient .

     
    Private Sub listView1_DrawItem(ByVal sender As Object, _     
            ByVal e As DrawListViewItemEventArgs) _     
            Handles listView1.DrawItem     
        
            If Not (e.State And ListViewItemStates.Selected) = 0 Then    
        
                ' Draw the background for a selected item.     
                e.Graphics.FillRectangle(Brushes.Maroon, e.Bounds)     
                e.DrawFocusRectangle()     
        
            Else    
        
                ' Draw the background for an unselected item.     
                Dim brush As New LinearGradientBrush(e.Bounds, Color.Orange, _     
                    Color.Maroon, LinearGradientMode.Horizontal)     
                Try    
                    e.Graphics.FillRectangle(brush, e.Bounds)     
                Finally    
                    brush.Dispose()     
                End Try    
        
            End If    
        
            ' Draw the item text for views other than the Details view.     
            If Not Me.listView1.View = View.Details Then    
                e.DrawText()     
            End If    
        
        End Sub    
     

    Coding for fun

    Warning    1    WithEvents variable 'Location' conflicts with property 'Location' in the base class 'Form' and should be declared 'Shadows'.

    Error    2    Type 'LinearGradientBrush' is not defined. 




    Saturday, February 28, 2009 6:42 AM
  • bdbodger said:

    Use this at the top of your form so the gradiantbrush does not give you an error 

    Imports System.Drawing.Drawing2D


    and don't forget that the built in formater in the forum does not show the underscore so the code is like this in reality

    Private Sub listView1_DrawItem(ByVal sender As Object, _
    ByVal e As DrawListViewItemEventArgs) _
    Handles ListView1.DrawItem

    If Not (e.State And ListViewItemStates.Selected) = 0 Then

    ' Draw the background for a selected item.

    e.Graphics.FillRectangle(Brushes.Maroon, e.Bounds)

    e.DrawFocusRectangle()

    Else

    ' Draw the background for an unselected item.

    Dim brush As New LinearGradientBrush(e.Bounds, Color.Orange, _
    Color.Maroon, LinearGradientMode.Horizontal)

    Try

    e.Graphics.FillRectangle(brush, e.Bounds)

    Finally

    brush.Dispose()

    End Try

    End If

    ' Draw the item text for views other than the Details view.

    If Not Me.ListView1.View = View.Details Then

    e.DrawText()

    End If

    End Sub


    Coding for fun


    Holy $#|@!

    It looks like Tetris!

    There's not data in it,  and when I first move the mouse over it the grid disappears.

     


    Saturday, February 28, 2009 6:49 AM
  • Now I have text in the first row, but it's in Chinese.  :-{

    Lol

    That works to change the BackColor of the selected item.  Thanks

    Is it possible to do that in the GotFocus event?

    Also, is it possible to Change the BackColor in the GotFocus event,  and retain the text I've loaded into the ListView?


     
     
    Saturday, February 28, 2009 7:05 AM
  • What the heck are you doing ? Your not doing a wpf project are you ? This is a very simple thing .
    Coding for fun
    Saturday, February 28, 2009 3:19 PM
  • bdbodger said:

    What the heck are you doing ? Your not doing a wpf project are you ? This is a very simple thing .


    Coding for fun


    No I'm doing a WFA.

    It is simple to change the background color,  it only takes a line of code.

    I can't get it to display the data that I'm loading from file now.

    Mainly due to the fact that now I don't know which events to use.

    It's like this brush thing is causing VB to behave differently.   Or maybe I'm doing something the wrong way.

    I don't know.

    Am I supposed to drop the ListView onto my Form and then assign it's properties in code,  or should I create the entire ListView from code?


    Saturday, February 28, 2009 4:06 PM
  • Now I'm confused.

    What seemed like a simple task is look more like a two week detour.

    Although I have found a way to change the default hightlight color of a selected item in the ListView(Thanks bdbodger).    Now I can't load my file into the ListView.

    I'm at a decision point,  of whether to continue to pursue this,  or to drop it and continue on with the rest of the project.

    I guess aesthetics aren't that important.  Not as important as being able to display data anyways!


    It seems like such a simple task,  if only it were as it seems.


    Saturday, February 28, 2009 11:29 PM
  • With owner draw you have to do all the drawing that includes the text . You would do that in the DrawSubitem event handler .

     Private Sub listView1_DrawSubItem(ByVal sender As Object, _     
            ByVal e As DrawListViewSubItemEventArgs) _     
            Handles ListView1.DrawSubItem     
        
        
            Dim sf As New StringFormat()     
            Try    
        
                ' Store the column text alignment, letting it default        
                ' to Left if it has not been set to Center or Right.        
                Select Case e.Header.TextAlign     
                    Case HorizontalAlignment.Center     
                        sf.Alignment = StringAlignment.Center    
                    Case HorizontalAlignment.Right     
                        sf.Alignment = StringAlignment.Far   
                End Select    
        
                e.Graphics.DrawString(e.SubItem.Text, _     
                                   Me.ListView1.Font, Brushes.Black, e.Bounds, sf)     
        
        
            Finally    
                sf.Dispose()     
            End Try    
        End Sub    
     


    Coding for fun

    Sunday, March 1, 2009 1:20 AM
  • First off,  that's just completely and utterly awesome!   Because it works!!!

    The DrawSubItems event,  that's starting to make some sense.

    And second,

    This is a new term for me   StringFormat()  

    What is it,  and why do we use it?

    And third......Awesome!!!

    Well except for the fact that I can't see my header text.  Probably because I haven't created any except for the ones I set in the Columns Collection in Properties grid.

    Anyway,

    Why can't I find any of this stuff in my books or anywhere else?

    I guess it's more intuitive if you think about it in Event Driven terms.
    Sunday, March 1, 2009 1:50 AM
  • Add this too

    ' Forces each row to repaint itself the first time the mouse moves over      
        ' it, compensating for an extra DrawItem event sent by the wrapped      
        ' Win32 control.     
        Private Sub listView1_MouseMove(ByVal sender As Object, _     
            ByVal e As MouseEventArgs) _     
            Handles listView1.MouseMove     
        
            Dim item As ListViewItem = listView1.GetItemAt(e.X, e.Y)     
            If Not item Is Nothing AndAlso item.Tag Is Nothing Then    
                listView1.Invalidate(item.Bounds)     
                item.Tag = "tagged"    
            End If    
     

    Coding for fun
    Sunday, March 1, 2009 1:56 AM
  • bdbodger said:

    Add this too

    ' Forces each row to repaint itself the first time the mouse moves over      
        ' it, compensating for an extra DrawItem event sent by the wrapped      
        ' Win32 control.     
        Private Sub listView1_MouseMove(ByVal sender As Object, _     
            ByVal e As MouseEventArgs) _     
            Handles listView1.MouseMove     
        
            Dim item As ListViewItem = listView1.GetItemAt(e.X, e.Y)     
            If Not item Is Nothing AndAlso item.Tag Is Nothing Then    
                listView1.Invalidate(item.Bounds)     
                item.Tag = "tagged"    
            End If    
     

    Coding for fun

    What do this do?

    I know it's commented that it forces each row to repaint it's self,  but I don't understand why I would want to do that.



    Sunday, March 1, 2009 1:59 AM
  • StringFormat is for drawing mainly or formating type this

    Dim sf As New StringFormat()

    then type sf. and let intellesence show you the options or type

    sf.FormatFlags =

    And see what options popup that will give you an idea of what it does .


    Coding for fun
    Sunday, March 1, 2009 2:01 AM
  • ThisDisplayNameHasGotToWorkBecauseIBluuBlaaaHa said:

    bdbodger said:

    Add this too

    ' Forces each row to repaint itself the first time the mouse moves over      
        ' it, compensating for an extra DrawItem event sent by the wrapped      
        ' Win32 control.     
        Private Sub listView1_MouseMove(ByVal sender As Object, _     
            ByVal e As MouseEventArgs) _     
            Handles listView1.MouseMove     
        
            Dim item As ListViewItem = listView1.GetItemAt(e.X, e.Y)     
            If Not item Is Nothing AndAlso item.Tag Is Nothing Then    
                listView1.Invalidate(item.Bounds)     
                item.Tag = "tagged"    
            End If    
     

    Coding for fun

    What do this do?

    I know it's commented that it forces each row to repaint it's self,  but I don't understand why I would want to do that.





    Without it when you mouse over a line the first time the line goes blank for some reason I don't know much more than that .
    Coding for fun
    Sunday, March 1, 2009 2:02 AM
  • Remember too that since your doing all the drawing then you can do what you want drawline drawstring drawelipse drawimage drawrectangle those are some of the drawing commands that can be used with e.graphics if you have done any drawing on a form then you already know how to use those .
    Coding for fun
    Sunday, March 1, 2009 2:08 AM
  • bdbodger said:

    ThisDisplayNameHasGotToWorkBecauseIBluuBlaaaHa said:

    bdbodger said:

    Add this too

    ' Forces each row to repaint itself the first time the mouse moves over      
        ' it, compensating for an extra DrawItem event sent by the wrapped      
        ' Win32 control.     
        Private Sub listView1_MouseMove(ByVal sender As Object, _     
            ByVal e As MouseEventArgs) _     
            Handles listView1.MouseMove     
        
            Dim item As ListViewItem = listView1.GetItemAt(e.X, e.Y)     
            If Not item Is Nothing AndAlso item.Tag Is Nothing Then    
                listView1.Invalidate(item.Bounds)     
                item.Tag = "tagged"    
            End If    
     

    Coding for fun

    What do this do?

    I know it's commented that it forces each row to repaint it's self,  but I don't understand why I would want to do that.





    Without it when you mouse over a line the first time the line goes blank for some reason I don't know much more than that .
    Coding for fun

    Mine aren't going blank,  but I did notice that in the first column the text gets a little darker when I mouseover it,  almost bold compared to the way it was when it loaded.

    I'll see it that last piece of code stops that.


    Sunday, March 1, 2009 2:08 AM
  • bdbodger said:

    Remember too that since your doing all the drawing then you can do what you want drawline drawstring drawelipse drawimage drawrectangle those are some of the drawing commands that can be used with e.graphics if you have done any drawing on a form then you already know how to use those .


    Coding for fun


    O.K.  lol,  now I know why I couldn't find any of this in my books. 

    I skipped the section on Graphics, because I thought it wasn't as important just now as getting the file and data aspects handled.

    Anyway,

    I added that last bit of code,  and it mostly stopped the text darkening.  There is maybe a very slight flicker in the text it's self,  almost not even noticeable.

    I actually didn't even notice it darkening before, until you mentioned the lines blanking out,  and I started looking closer.
     


    Sunday, March 1, 2009 2:17 AM
  • There is a shortcut if your not doing anything special with the text

     

    Private Sub listView1_DrawSubItem(ByVal sender As Object, _

    ByVal e As DrawListViewSubItemEventArgs) _

    Handles ListView1.DrawSubItem

    e.DrawText()

    End Sub


    Coding for fun

    • Edited by bdbodger Sunday, March 1, 2009 2:27 AM
    Sunday, March 1, 2009 2:26 AM
  • I got the headers back,  by adding this code.   It's actually an example from the documentation.

    I only had to change the font to my liking.

     Private Sub ListView1_DrawColumnHeader(ByVal sender As ObjectByVal e As System.Windows.Forms.DrawListViewColumnHeaderEventArgs) Handles ListView1.DrawColumnHeader 
     
            Dim sf As New StringFormat() 
            Try 
                ' Store the column text alignment, letting it default 
                ' to Left if it has not been set to Center or Right. 
                Select Case e.Header.TextAlign 
                    Case HorizontalAlignment.Center 
                        sf.Alignment = StringAlignment.Center 
                    Case HorizontalAlignment.Right 
                        sf.Alignment = StringAlignment.Far 
                End Select 
     
                ' Draw the standard header background. 
                e.DrawBackground() 
     
                ' Draw the header text. 
                Dim headerFont As New Font("Verdana", 10, FontStyle.Regular) 
                Try 
                    e.Graphics.DrawString(e.Header.Text, headerFont, _ 
                        Brushes.Black, e.Bounds, sf) 
                Finally 
                    headerFont.Dispose() 
                End Try 
     
            Finally 
                sf.Dispose() 
            End Try 
        End Sub 



           
    Sunday, March 1, 2009 3:44 AM
  • Here's something that got me thinking.

    It's the description for  OwnerDraw.

    "Gets or sets a value indicating whether the ListView control is drawn by the operating system or by code that you provide".

    What I'm doing is,  I added the ListView from the ToolBox.

    Then I added Columns to the Columns Collection in the Properties grid.

    Then I added in the Form Load event   ListView1.OwnerDraw = True,   and added the Graphics code to set the BackColor,  and DrawItem,  DrawSubItem,  and DrawColumnHeader events.

    So I'm not actually drawing the entire thing.

    I'm just adding some code to change the BackColor of the selected item, which causes the ColumnHeaders, and the Column text to not display.

    Then I'm adding more code to make what's already in the ColumnHeaders, and the Columns visible again.

    I know this, because I didn't add any columns in my code,  or make any changes to the way the data is loaded,  what's showing in the headers is set in the properties grid,  and the data is loaded in a MenuItem's click event by means of an OpenFileDialog.





    Sunday, March 1, 2009 3:54 AM
  • Yes you can set the OwnerDraw property in the property grid as well but from code is ok . With OwnerDraw you draw the items but the layout is set you just changeing the colors and fonts etc . Your code can be more complex with OwnerDraw like the colors of the text . Given the right code each letter of the text can be a different color . You can draw lines pictures arcs circles etc . That is what they mean by OwnerDraw your not actually laying out the layout just drawing the background and text and what ever else you draw in the "bounds" of the item or subitem . Glad you figured out the header code . Those 3 events are all you need to use . The headers have a text property your just drawing it as do the items and subitems . OwnerDraw does not change the properties of them it just lets you draw them . You can do with it what you want . Want a nice red border around your header item then use drawrectangle in your code . Want the second header to have a different border color then do that too . Have fun with it . Doing it this way allows you to use gradients , that you can't do without OwnerDraw .
    Coding for fun
    • Edited by bdbodger Sunday, March 1, 2009 4:37 AM
    Sunday, March 1, 2009 4:34 AM