Click here to Skip to main content
15,896,154 members
Articles / Desktop Programming / Windows Forms

An All VB.NET Explorer Tree Control with ImageList Management

Rate me:
Please Sign up or sign in to vote.
4.87/5 (138 votes)
17 May 2012CPOL30 min read 2.2M   29.6K   269  
Explorer TreeView control with Shell Folder access class and Icon management.
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.Threading
Imports ExpTreeLib.ShellDll

Public Class SystemImageListManager

#Region "       ImageList Related Constants"
    ' For ImageList manipulation
    Private Const LVM_FIRST = &H1000
    Private Const LVM_SETIMAGELIST = (LVM_FIRST + 3)

    Private Const LVSIL_NORMAL = 0
    Private Const LVSIL_SMALL = 1
    Private Const LVSIL_STATE = 2

    Private Const TV_FIRST = &H1100
    Private Const TVM_SETIMAGELIST = (TV_FIRST + 9)

    Private Const TVSIL_NORMAL = 0
    Private Const TVSIL_STATE = 2
#End Region

#Region "   Private Fields"
    Private Shared m_Initialized As Boolean = False
    Private Shared m_smImgList As IntPtr = IntPtr.Zero 'Handle to System Small ImageList
    Private Shared m_lgImgList As IntPtr = IntPtr.Zero 'Handle to System Large ImageList
    Private Shared m_Table As New Hashtable(128)

    Private Shared m_Mutex As New Mutex()
#End Region

#Region "   New"
    Private Shared Sub Initializer()
        If m_Initialized Then
            Exit Sub
        End If

        Dim dwFlag As Integer = SHGFI.USEFILEATTRIBUTES Or _
                        SHGFI.SYSICONINDEX Or _
                        SHGFI.SMALLICON
        Dim shfi As New SHFILEINFO()
        m_smImgList = SHGetFileInfo(".txt", _
                           FILE_ATTRIBUTE_NORMAL, _
                           shfi, _
                           cbFileInfo, _
                           dwFlag)
        Debug.Assert((Not m_smImgList.Equals(IntPtr.Zero)), "Failed to create Image Small ImageList")
        If m_smImgList.Equals(IntPtr.Zero) Then
            Throw New Exception("Failed to create Small ImageList")
        End If

        dwFlag = SHGFI.USEFILEATTRIBUTES Or _
                        SHGFI.SYSICONINDEX Or _
                        SHGFI.LARGEICON
        m_lgImgList = SHGetFileInfo(".txt", _
                           FILE_ATTRIBUTE_NORMAL, _
                           shfi, _
                           cbFileInfo, _
                           dwFlag)
        Debug.Assert((Not m_lgImgList.Equals(IntPtr.Zero)), "Failed to create Image Small ImageList")
        If m_lgImgList.Equals(IntPtr.Zero) Then
            Throw New Exception("Failed to create Large ImageList")
        End If

        m_Initialized = True
    End Sub
#End Region

#Region "   Public Properties"
    Public Shared ReadOnly Property hSmallImageList() As IntPtr
        Get
            Return m_smImgList
        End Get
    End Property
    Public Shared ReadOnly Property hLargeImageList() As IntPtr
        Get
            Return m_lgImgList
        End Get
    End Property
#End Region

#Region "   Public Methods"
#Region "       GetIconIndex"
    Private Shared mCnt As Integer
    Private Shared bCnt As Integer

    Public Shared Function GetIconIndex(ByRef item As CShItem, Optional ByVal GetOpenIcon As Boolean = False) As Integer

        Initializer()
        Dim HasOverlay As Boolean = False  'true if it's an overlay
        Dim rVal As Integer     'The returned Index

        Dim dwflag As Integer = SHGFI.SYSICONINDEX Or _
                        SHGFI.PIDL Or SHGFI.ICON
        Dim dwAttr As Integer = 0
        'build Key into HashTable for this Item
        Dim Key As Integer = IIf(Not GetOpenIcon, item.IconIndexNormal * 256, _
                                                  item.IconIndexOpen * 256)
        With item
            If .IsLink Then
                Key = Key Or 1
                dwflag = dwflag Or SHGFI.LINKOVERLAY
                HasOverlay = True
            End If
            If .IsShared Then
                Key = Key Or 2
                dwflag = dwflag Or SHGFI.ADDOVERLAYS
                HasOverlay = True
            End If
            If m_Table.ContainsKey(Key) Then
                rVal = (m_Table(Key))
                mCnt += 1
            ElseIf Not HasOverlay Then  'for non-overlay icons, we already have
                rVal = Key \ 256        '  the right index -- put in table
                m_Table(Key) = rVal
                bCnt += 1
            Else        'don't have iconindex for an overlay, get it. 
                'This is the tricky part -- add overlaid Icon to systemimagelist
                Dim shfi As New SHFILEINFO()
                Dim HR As IntPtr
                If .IsFileSystem And Not .IsDisk And Not .IsFolder Then
                    dwflag = dwflag Or SHGFI.USEFILEATTRIBUTES
                    dwAttr = FILE_ATTRIBUTE_NORMAL
                End If
                HR = SHGetFileInfo(.PIDL, dwAttr, shfi, cbFileInfo, dwflag)
                m_Mutex.WaitOne()
                rVal = ImageList_ReplaceIcon(m_smImgList, -1, shfi.hIcon)
                Debug.Assert(rVal > -1, "Failed to add overlaid small icon")
                Dim rVal2 As Integer
                rVal2 = ImageList_ReplaceIcon(m_lgImgList, -1, shfi.hIcon)
                Debug.Assert(rVal2 > -1, "Failed to add overlaid large icon")
                Debug.Assert(rVal = rVal2, "Small & Large IconIndices are Different")
                m_Mutex.ReleaseMutex()
                'Debug.WriteLine(.DisplayName & ": iInx = " & rVal)
                '.DebugDump()
                DestroyIcon(shfi.hIcon)
                If rVal < 0 OrElse rVal <> rVal2 Then
                    Throw New ApplicationException("Failed to add Icon for " & item.DisplayName)
                End If
                m_Table(Key) = rVal
            End If
        End With
        'Debug.WriteLine("mCnt = " & mCnt & " bCnt = " & bCnt & " TableCount = " & m_Table.Count)
        Return rVal
    End Function
#End Region

#Region "       GetDeskTopIconIndex"
    ' No longer used. Retained for information purposes
    ' <summary>
    ' public int GetDeskTopIconIndex()
    ' Returns the Icon Index for the Desktop. This is not
    '		available using the normal methods and the image
    '		itself is not placed into the ImageList unless this
    '		call is made.
    ' </summary>
    ' <returns>Returns the Icon Index for the Desktop
    ' </returns>
    'Public Shared Function GetDeskTopIconIndex() As Integer
    '    Dim ppidl As IntPtr
    '    Dim hDum As Integer = 0
    '    Dim rVal As Integer

    '    rVal = SHGetSpecialFolderLocation(hDum, CSIDL.DESKTOP, ppidl)
    '    If rVal = 0 Then
    '        Dim dwFlags As Integer = SHGFI.SYSICONINDEX _
    '                                 Or SHGFI.PIDL
    '        Dim dwAttr As Integer = 0

    '        Dim shfi As SHFILEINFO = New SHFILEINFO()
    '        Dim resp As IntPtr
    '        resp = SHGetFileInfo(ppidl, _
    '                                dwAttr, _
    '                                shfi, _
    '                                cbFileInfo, _
    '                                dwFlags)
    '        Marshal.FreeCoTaskMem(ppidl)  ' free the pointer
    '        If resp.Equals(IntPtr.Zero) Then
    '            Debug.Assert(Not resp.Equals(IntPtr.Zero), "Failed to get icon index")
    '            rVal = -1  'Failed to get IconIndex
    '        Else
    '            rVal = shfi.iIcon  'got it, return it
    '        End If
    '    Else        'failed to get DesktopLocation
    '        rVal = -1
    '    End If
    '    If rVal > -1 Then
    '        Return rVal
    '    Else
    '        Throw New ApplicationException("Failed to get Desktop Icon Index")
    '        Return -1
    '    End If
    'End Function
#End Region

#Region "       SetListViewImageList"
    '    <summary>
    '    Associates a SysImageList with a ListView control
    '    </summary>
    '    <param name="listView">ListView control to associate ImageList with</param>
    '    <param name="forLargeIcons">True=Set Large Icon List
    '                   False=Set Small Icon List</param>
    '    <param name="forStateImages">Whether to add ImageList as StateImageList</param>
    Public Shared Sub SetListViewImageList( _
            ByVal listView As ListView, _
            ByVal forLargeIcons As Boolean, _
            ByVal forStateImages As Boolean)

        Initializer()
        Dim wParam As Integer = LVSIL_NORMAL
        Dim HImageList As IntPtr = m_lgImgList
        If Not forLargeIcons Then
            wParam = LVSIL_SMALL
            HImageList = m_smImgList
        End If
        If forStateImages Then
            wParam = LVSIL_STATE
        End If
        SendMessage(listView.Handle, _
                    LVM_SETIMAGELIST, _
                    wParam, _
                    HImageList)
    End Sub
#End Region

#Region "       SetTreeViewImageList"
    '/// <summary>
    '/// Associates a SysImageList with a TreeView control
    '/// </summary>
    '/// <param name="treeView">TreeView control to associated ImageList with</param>
    '/// <param name="forStateImages">Whether to add ImageList as StateImageList</param>
    Public Shared Sub SetTreeViewImageList( _
        ByVal treeView As TreeView, _
        ByVal forStateImages As Boolean)

        Initializer()
        Dim wParam As Integer = LVSIL_NORMAL
        If forStateImages Then
            wParam = LVSIL_STATE
        End If
        Dim HR As Integer
        HR = SendMessage(treeView.Handle, _
                    TVM_SETIMAGELIST, _
                    wParam, _
                    m_smImgList)
    End Sub

#End Region

#End Region
End Class

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
After 30+ years working in the IT field, mostly managing SysAdmins, I have retired. One of my hobbies returns me to programming, basically just to keep my hand in.

Comments and Discussions