﻿'********************************************************************************
'*                  Created by Predrag Gruevski (obi1kenobi)                    *
'*                      Originally published on VBForums                        *
'*                                02.01.2010                                    *
'*            Feel free to use this code for any use you see fit,               *
'*       just please do not alter this info box and credit me for the code.     *
' mod _katze_ 22/12/2011                                                        *
'********************************************************************************
Imports System.Drawing
Imports System.Windows.Forms
Imports System.ComponentModel

Public Class ListViewTransparente
    Inherits ListView
#Region " Constantes "
    Const CLR_NONE As Integer = -1
    Const LVM_FIRST As Integer = &H1000
    Const LVM_GETBKCOLOR As Integer = LVM_FIRST + 0
    Const LVM_SETBKCOLOR As Integer = LVM_FIRST + 1
    Const WM_HSCROLL As Integer = &H114
    Const WM_VSCROLL As Integer = &H115
    Const SBM_SETSCROLLINFO As Integer = &HE9

#End Region

#Region " APIs "
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
#End Region

#Region " Variables "
    Private WithEvents tmr As New Timer
    Private stp As New Stopwatch
    Private _redrawOnMouseMove As Boolean
    Private _interval As Integer = 15
    Private _startcolor As Color = Color.FromArgb(230, 0, 163, 211)
    Private _endcolor As Color = Color.FromArgb(140, 200, 202, 0)
    Private itemHeight As Integer
    Private _font As Font = New Font("Comic Sans MS", 9, FontStyle.Regular, GraphicsUnit.Point)
#End Region

#Region " Constructors "


    Public Sub New()
        Me.OwnerDraw = True
        Me.BorderStyle = Windows.Forms.BorderStyle.None
        Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or _
        ControlStyles.ResizeRedraw Or ControlStyles.AllPaintingInWmPaint, True)
        Me.Font = _font
        tmr.Interval = _interval

    End Sub

#End Region

#Region " Properties "
    'modify the Font property to use at will
    Public Overrides Property Font As System.Drawing.Font
        Get
            Return _font
        End Get
        Set(ByVal value As System.Drawing.Font)
            _font = value
        End Set
    End Property

    'Public Property ErrorTextColor() As Color
    '    Get
    '        Return _errorColor
    '    End Get
    '    Set(ByVal value As Color)
    '        _errorColor = value
    '    End Set
    'End Property

    ' ''' <summary>
    ' ''' The color to use when highlighting an item.
    ' ''' </summary>
    'Public Property HighlightColor() As Color
    '    Get
    '        Return _highlightColor
    '    End Get
    '    Set(ByVal value As Color)
    '        _highlightColor = value
    '    End Set
    'End Property

    <Description(""), Category("LISTCUSTOM")>
    Public Property EndColor As Color
        Get
            Return _endcolor
        End Get
        Set(ByVal value As Color)
            _endcolor = value
        End Set
    End Property
    <Description(""), Category("LISTCUSTOM")>
    Public Property StartColor As Color
        Get
            Return _startcolor
        End Get
        Set(ByVal value As Color)
            _startcolor = value
        End Set
    End Property

    <Description(""), Category("LISTCUSTOM")>
    Public Property RedrawInterval() As Integer
        Get
            Return _interval
        End Get
        Set(ByVal value As Integer)
            If value <= 0 Then
                _interval = 15 '15ms should result in appx. 60 refreshes per second (60Hz) - only when required
                tmr.Interval = 15
            Else
                _interval = value
                tmr.Interval = value
            End If
        End Set
    End Property

    <Description(""), Category("LISTCUSTOM"), DefaultValue(True), Browsable(True)>
    Public Property RedrawOnMouseMove() As Boolean
        Get
            Return _redrawOnMouseMove
        End Get
        Set(ByVal value As Boolean)
            '_redrawOnMouseMove = value
        End Set
    End Property

#End Region

#Region " Event  "

    Private Sub tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmr.Tick
        Me.Invalidate()
        tmr.Enabled = False
    End Sub

#End Region

#Region " metodos "
#Region " Overrides Metodos"
    Protected Overrides Sub OnMouseWheel(ByVal e As System.Windows.Forms.MouseEventArgs)
        MyBase.OnMouseWheel(e)
        OnListViewScrolled(EventArgs.Empty)
    End Sub
    Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
        If Me.Items.Count > 0 Then
            Dim clickedItem As ListViewItem = Me.GetItemAt(5, e.Y)
            If (clickedItem IsNot Nothing) Then
                clickedItem.Selected = True
                clickedItem.Focused = True
                'Else
                'Dim bnd As Integer = Me.Items.Count - 1
                'For i As Integer = 0 To bnd
                ' clickedItem = Me.Items(bnd)
                ' If clickedItem.Bounds.Contains(5, e.Y) Then
                ' clickedItem.Selected = True
                ' clickedItem.Focused = True
                ' Exit For
                'End If
                '    Next
            End If
        End If
        MyBase.OnMouseUp(e)
    End Sub
    Protected Overrides Sub OnResize(ByVal e As System.EventArgs)
        Me.Refresh()
        MyBase.OnResize(e)
    End Sub
    Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
        If _redrawOnMouseMove Then
            Dim item As ListViewItem = Me.GetItemAt(e.X, e.Y)
            If item IsNot Nothing Then 'AndAlso item.Tag Is Nothing Then
                Me.Invalidate(item.Bounds)
                'item.Tag = "tagged"
            End If
        End If
        MyBase.OnMouseMove(e)
    End Sub

    Protected Overrides Sub OnInvalidated(ByVal e As System.Windows.Forms.InvalidateEventArgs)
        'Related to the use of Tag and RedrawOnMouseMove properties.

        'For Each item As ListViewItem In Me.Items
        'If item Is Nothing Then Return
        'item.Tag = Nothing
        'Next
        MyBase.OnInvalidated(e)
    End Sub

    Protected Overrides Sub OnColumnWidthChanged(ByVal e As System.Windows.Forms.ColumnWidthChangedEventArgs)
        Me.Invalidate()
        MyBase.OnColumnWidthChanged(e)
    End Sub

    Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
        MyBase.OnHandleCreated(e)
        SendMessage(Me.Handle.ToInt32, LVM_SETBKCOLOR, 0, CLR_NONE)

    End Sub

    '<DebuggerStepThrough()> _
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        Select Case m.Msg
            Case WM_VSCROLL
                OnListViewScrolled(EventArgs.Empty)
            Case WM_HSCROLL
                OnListViewScrolled(EventArgs.Empty)
            Case SBM_SETSCROLLINFO
                OnListViewScrolled(EventArgs.Empty)
        End Select
        MyBase.WndProc(m)
    End Sub
    '    '<DebuggerStepThrough()> _
    Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawListViewItemEventArgs)

        If Not (e.State And ListViewItemStates.Selected) = 0 OrElse e.Item.Selected Then
            Using br As New Drawing2D.LinearGradientBrush(e.Bounds, _startcolor, _endcolor, Drawing2D.LinearGradientMode.Vertical)
                ' Draw the background for a selected item, the gradient Brush.
                e.Graphics.FillRectangle(br, e.Bounds)
            End Using
        End If

        Dim sf As New StringFormat()
        Dim index As Integer
        index = e.ItemIndex
        If SmallImageList IsNot Nothing Then 'smallimagelist found that the drawing is not empty icons in the control
            e.Graphics.DrawImage(SmallImageList.Images.Item(index), New Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, 16, 16))
            e.Graphics.DrawString(e.Item.Text, Me.Font, Brushes.Black, New Rectangle(e.Bounds.X + SmallImageList.ImageSize.Width + 4, e.Bounds.Y, e.Bounds.Width - SmallImageList.ImageSize.Width - 4, e.Bounds.Height), sf)
        Else 'but draw the text only
            e.Graphics.DrawString(e.Item.Text, Me.Font, Brushes.Black, e.Bounds)
        End If

        MyBase.OnDrawItem(e)
    End Sub

    '<DebuggerStepThrough()> _
    Protected Overrides Sub OnDrawSubItem(ByVal e As System.Windows.Forms.DrawListViewSubItemEventArgs)
        'Dim flags As TextFormatFlags = TextFormatFlags.Left

        Dim sf As New StringFormat()

        Dim index As Integer

        'Try

        sf.LineAlignment = StringAlignment.Far
        ' 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.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias

        'If e.Item.ForeColor <> _errorColor Then
        '    clr = Me.ForeColor
        'Else
        '    clr = _errorColor
        'End If

        'Dim lv As ListViewItem = e.Item
        'Dim tmp As String

        'For Each t As ListViewItem.ListViewSubItem In e.Item.SubItems
        '    tmp = t.Text
        'Next t


        If e.Item.Text = e.SubItem.Text Then

            If SmallImageList IsNot Nothing Then
                index = e.ItemIndex

                If index = -1 Then
                    'e.Graphics.DrawImage(SmallImageList.Images.Item(index), New Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, 16, 16))
                    'e.Graphics.DrawString(tmp, Me.Font, Brushes.AliceBlue, New Rectangle(e.Bounds.X + SmallImageList.ImageSize.Width + 4, e.Bounds.Y, e.Bounds.Width - SmallImageList.ImageSize.Width - 4, e.Bounds.Height), sf)


                Else

                    'e.Graphics.DrawString(tmp, Me.Font, Brushes.AliceBlue, New Rectangle(e.Bounds.X + 4, e.Bounds.Y, e.Bounds.Width - 4, e.Bounds.Height), sf)

                End If

            Else

                'e.Graphics.DrawString(tmp, Me.Font, Brushes.AliceBlue, New Rectangle(e.Bounds.X + 4, e.Bounds.Y, e.Bounds.Width - 4, e.Bounds.Height), sf)

            End If

        Else

            'e.DrawText()
            e.Graphics.DrawString(e.SubItem.Text, Me.Font, Brushes.Black, e.Bounds, sf)

        End If


        'e.DrawText(flags)

        'Finally
        '    sf.Dispose()
        'End Try

        MyBase.OnDrawSubItem(e)
    End Sub

    '<DebuggerStepThrough()> _
    Protected Overrides Sub OnDrawColumnHeader(ByVal e As System.Windows.Forms.DrawListViewColumnHeaderEventArgs)
        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.
            sf.LineAlignment = StringAlignment.Center
            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.

            Try
                e.Graphics.DrawString(e.Header.Text, Me.Font, _
                    Brushes.Black, e.Bounds, sf)
            Finally


            End Try

        Finally
            sf.Dispose()
        End Try

        MyBase.OnDrawColumnHeader(e)
    End Sub
#End Region

#Region " Custom Metodos "

    Protected Overridable Sub OnListViewScrolled(ByVal e As EventArgs)
        If stp.IsRunning Then
            If stp.ElapsedMilliseconds > _interval Then
                stp = Stopwatch.StartNew
                Me.Invalidate()
            End If
        Else
            stp.Start()
        End If
        tmr.Stop()
        tmr.Start()

    End Sub

#End Region
#End Region
End Class
