(1)Subclass Listview


マイクロソフトのリストビューは、パソコンの環境によって利用出来ないか、
配布が出来ない状態があります。
そこで、環境に依存しないサブクラスでリストビュー作成の紹介です。
環境OSはwindowsです。

-----------------------------------------------------------

これからの内容はmougモーグのExcelVBAに於いて
投稿者: たっく氏, 回答者: Abyss氏 のお二人のやり取りから
公開された内容を一部加筆したものです。
貴重な情報に感謝いたします。

mouglink1 mouglink2 mouglink3 mouglink4
------------------------------------------------------------

下記のソースコードはReport表示形式(Item+subItem)となります。
又ダウンロードファイルを2種類用意しましたので、
動作確認は、そちらのファイルで確認願います。
Userformは、モーダルのみ動作のみ対応となります。

公開に当たり利用者の知識向上の手助けとなれば幸いです。


※ダウンロードファイル   ListView(Report).zip  ListView(List).zip

Option Explicit

Public Const WS_CHILD = &H40000000
Public Const WS_VISIBLE = &H10000000
Public Const WS_CLIPSIBLINGS = &H4000000
Public Const WS_EX_CLIENTEDGE = &H200&
Public Const WM_SETFOCUS = 7&
Public Const WM_NOTIFY = &H4E&
Public Const WM_MOUSEACTIVATE = &H21&
Public Const WM_NCDESTROY = &H82&
Public Const WM_SETFONT = &H30
Public Const MA_NOACTIVATE = 3&
Public Const LVS_REPORT = 1&
Public Const LVS_SHOWSELALWAYS = 8&
Public Const LVS_EX_FULLROWSELECT = &H20&
Public Const LVS_EX_GRIDLINES = 1&
Public Const LVS_EX_CHECKBOXES = 4&
Public Const LVM_SETEXTENDEDLISTVIEWSTYLE = &H1036
Public Const LVM_SETITEM = &H1000& + 76
Public Const LVM_INSERTITEM = &H1000& + 77
Public Const LVM_INSERTCOLUMN = &H1000& + 97
Public Const LVM_GETITEMTEXT = &H1000& + 115
Public Const LVN_ITEMCHANGED = -100& - 1
Public Const LVCF_WIDTH = 2&
Public Const LVCF_TEXT = 4&
Public Const LVCF_SUBITEM = 8&
Public Const LVIF_TEXT = 1&
Public Const LVM_FIRST = &H1000
' テキストの背景色を設定
Public Const LVM_SETTEXTBKCOLOR = (LVM_FIRST + 38) 
' 背景色の設定
Public Const LVM_SETBKCOLOR = (LVM_FIRST + 1) 
' テキストの文字色を設定     
Public Const LVM_SETTEXTCOLOR = (LVM_FIRST + 36)   
Public Const NM_CLICK = -2&
Public Const NM_DBLCLK = -3&

Declare Function InitCommonControlsEx& Lib "comctl32" _
    (ByVal lpInitCtrls&)
Declare Function SetWindowSubclass& Lib "comctl32" _
    (ByVal hwnd&, _
     ByVal pfnSubclass&, _
     ByVal uIdSubclass&, _
     ByVal dwRefData&)
Declare Function DefSubclassProc& Lib "comctl32" _
    (ByVal hwnd&, _
     ByVal uMsg&, _
     ByVal wParam&, _
     ByVal lParam&)
Declare Function RemoveWindowSubclass& Lib "comctl32" _
    (ByVal hwnd&, _
     ByVal pfnSubclass&, _
     ByVal uIdSubclass&)
Declare Function CreateWindowExW& Lib "user32" _
    (ByVal dwExStyle&, _
     ByVal lpClassName&, _
     ByVal lpWindowName&, _
     ByVal dwStyle&, _
     ByVal X&, ByVal Y&, ByVal nWidth&, ByVal nHeight&, _
     ByVal HwndParent&, _
     ByVal HMENU&, _
     ByVal hInstance&, _
     ByVal lpParam&)
Declare Function SendMessageW& Lib "user32" _
    (ByVal hwnd&, _
     ByVal uMsg&, _
     ByVal wParam&, _
     ByVal lParam&)
Declare Function GetFocus& Lib "user32" ()
Declare Function SetFocus& Lib "user32" (ByVal hwnd&)
Declare Sub MoveMemory Lib "Kernel32" Alias "RtlMoveMemory" _
    (pDest As Any, _
     pSrc As Any, _
     ByVal cbLen&)
Declare Function SysAllocString& Lib "Oleaut32" (ByVal ptr&)
Declare Function GetSysColor Lib "user32" _ 
    (ByVal nIndex As Long) As Long
Declare Function CreateFont Lib "gdi32" Alias "CreateFontA" _
    (ByVal H As Long, _
     ByVal W As Long, _
     ByVal E As Long, _
     ByVal o As Long, _
     ByVal W As Long, _
     ByVal i As Long, _
     ByVal u As Long, _
     ByVal S As Long, _
     ByVal C As Long, _
     ByVal OP As Long, _
     ByVal CP As Long, _
     ByVal Q As Long, _
     ByVal PAF As Long, _
     ByVal F As String) As Long

Type LVCOLUMN
    mask        As Long
    fmt         As Long
    cx          As Long
    pszText     As Long
    cchTextMax  As Long
    iSubItem    As Long
    iImage      As Long
    buf(3)      As Long
End Type
Type LVITEM
    mask        As Long
    iItem       As Long
    iSubItem    As Long
    state       As Long
    stateMask   As Long
    pszText     As Long
    cchTextMax  As Long
    buf(7)      As Long
End Type
Type NMITEMACTIVATE
    hdr(2)      As Long
    iItem       As Long
    iSubItem    As Long
    buf(6)      As Long
End Type
Type NMLISTVIEW
    hrd(2)      As Long
    iItem       As Long
    iSubItem    As Long
    buf(4)      As Long
End Type
Type TT
    hParent As Long
    hChild As Long
    pfn As Long
End Type

Public TT As TT, acc As IAccessible
Public Function Redirect&(ByVal hwnd&, _ 
                          ByVal uMsg&, _ 
                          ByVal wParam&, _
                          ByVal lParam&, _ 
                          ByVal id&, _ 
                          ByVal lv As ListView)
    Redirect = lv.WndProc(hwnd, uMsg, wParam, lParam)
End Function
      
Dim fr As IControl

Event ItemSelected(ByVal iItem&, ByVal iSubItem&)
Event ItemClick(ByVal iItem&, ByVal iSubItem&)
'Event ItemDClick(ByVal iItem&, ByVal iSubItem&)

Public Sub Init(ByVal Ctl As IOptionFrame)
    Dim p&, i&, j&, mWS&
    Dim ii&(1)
    Dim wd&, ht&
    Dim flbkColor As Long
    Dim fnt As Long
    Dim buf As Long
    
    If TT.hParent Then Exit Sub
    Set fr = Ctl
    TT.hParent = fr.[_GethWnd]
    If TT.hParent = 0 Then
        Err.Raise 91
    End If
    
    Set acc = fr
    acc.accLocation 0, 0, wd, ht
    
    ii(0) = 8
    ii(1) = 1   'ICC_LISTVIEW_CLASSES
    InitCommonControlsEx VarPtr(ii(0))
    
    With TT
        .pfn = GetPtr(AddressOf Redirect)
        SetWindowSubclass .hParent, .pfn, .hParent, ObjPtr(Me)
        
        'コモンコントロールのウィンドウスタイル
        mWS = WS_CHILD Or WS_VISIBLE Or WS_CLIPSIBLINGS
        mWS = mWS Or &O1 Or LVS_SHOWSELALWAYS
        'LVS_REPORT→&01 LVS_LIST→&03
       
        .hChild = CreateWindowExW(WS_EX_CLIENTEDGE, _
                            StrPtr("SysListView32"), 0, mWS, _
                            0, 0, wd, ht, .hParent, 0, 0, 0)
        SetWindowSubclass .hChild, .pfn, .hChild, ObjPtr(Me)
                                    
        '拡張スタイル
        mWS = LVS_EX_GRIDLINES Or LVS_EX_CHECKBOXES Or _ 
              LVS_EX_FULLROWSELECT
'        LVS_EX_FULLROWSELECT→1行選択
'        LVS_EX_GRIDLINES→グリッドライン
'        LVS_EX_CHECKBOXES →ChekBox付き

        SendMessageW .hChild, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, mWS
       '------/text color/----------
'       flbkColor = GetSysColor(vbButtonFace And &HFF)
'       SendMessageW .hChild, LVM_SETTEXTBKCOLOR, 0, flbkColor
'       SendMessageW .hChild, LVM_SETBKCOLOR, 0, flbkColor
'       SendMessageW .hChild, LVM_SETTEXTCOLOR, 0, RGB(255, 0, 0)
       '------/text font/----------
       fnt = CreateFont(16, _
                         FW_NORMAL, _
                         0, _
                         0, _
                         0, _
                         False, _
                         False, _
                         False, _
                         DEFAULT_CHARSET, _
                         OUT_DEFAULT_PRECIS, _
                         CLIP_DEFAULT_PRECIS, _
                         DEFAULT_QUALITY, _
                         DEFAULT_PITCH, _
                         "MS UI Gothic")

'            int nHeight,               '// フォントの高さ
'            int nWidth,                '// 平均文字幅
'            int nEscapement,           '// 文字送り方向の角度
'            int nOrientation,          '// ベースラインの角度
'            int fnWeight,              '// フォントの太さ
'            DWORD fdwItalic,           '// 斜体にするかどうか
'            DWORD fdwUnderline,        '// 下線を付けるかどうか
'            DWORD fdwStrikeOut,        '// 取り消し線を付けるかどうか
'            DWORD fdwCharSet,          '// 文字セットの識別子
'            DWORD fdwOutputPrecision,  '// 出力精度
'            DWORD fdwClipPrecision,    '// クリッピング精度
'            DWORD fdwQuality,          '// 出力品質
'            DWORD fdwPitchAndFamily,   '// ピッチとファミリ
'            LPCTSTR lpszFace           '// フォント名
        SendMessageW .hChild, WM_SETFONT, fnt, ByVal 1&
    End With
End Sub

Public Function InsertColumn&(Title As String, ByVal iCol&, _ 
                              ByVal Width&)
    Dim lvc As LVCOLUMN
    With lvc
        .mask = LVCF_TEXT Or LVCF_WIDTH Or LVCF_SUBITEM
        .cx = Width
        .pszText = StrPtr(Title)
    End With
    With TT
        If .hChild = 0 Then Exit Function
        InsertColumn = SendMessageW(.hChild, LVM_INSERTCOLUMN, iCol, _ 
                       VarPtr(lvc))
    End With
End Function

Public Function InsertItem&(sItem As String, ByVal iItem&)
    Dim lvi As LVITEM
    With lvi
        .mask = LVIF_TEXT
        .iItem = iItem
        .pszText = StrPtr(sItem)
    End With
    With TT
        If .hChild = 0 Then Exit Function
        InsertItem = SendMessageW(.hChild, LVM_INSERTITEM, 0, _ 
                                  VarPtr(lvi))
    End With
End Function

Public Function SetItem(sItem As String, ByVal iItem&, ByVal iSubItem&)
    Dim lvi As LVITEM
    With lvi
        .mask = LVIF_TEXT
        .iItem = iItem
        .iSubItem = iSubItem
        .pszText = StrPtr(sItem)
    End With
    With TT
        If .hChild = 0 Then Exit Function
        SetItem = SendMessageW(.hChild, LVM_SETITEM, 0, VarPtr(lvi))
    End With
End Function

Public Function DeleteAllItems()
    DeleteAllItems = SendMessageW(TT.hChild, &H1009, 0, 0)
End Function

Public Function GetItemCount()
'    Dim Cnt As Integer
'    Cnt = SendMessageW(TT.hChild, &H1004, 0, 0)
    GetItemCount = SendMessageW(TT.hChild, &H1004, 0, 0)
End Function

Public Function LabelText(ByVal iItem&, ByVal iSubItem&) As String
    Dim lv As LVITEM
    Dim buf(255) As Integer
    
    With lv
        .cchTextMax = 256
        .iSubItem = iSubItem
        .pszText = VarPtr(buf(0))
    End With
    SendMessageW TT.hChild, LVM_GETITEMTEXT, iItem, VarPtr(lv)
    With lv
        If .pszText = 0 Then Exit Function
        MoveMemory ByVal VarPtr(LabelText), SysAllocString(.pszText), 4
    End With
End Function

Public Function GetCheckState(ByVal ii As Long) As Boolean
    ' チェック状態を取得(ii = インデックス)...
    ' LVM_GETITEMSTATE...
    ii = SendMessageW(TT.hChild, &H102C, ii, &H1000&)
    GetCheckState = ii  '= 0
End Function

Public Function SetCheckState(ByVal ii As Long, ByVal F As Boolean)
    ' チェック状態をセット(ii = インデックス)
    ' iiが「-1」の時は全ての項目が対象
    Dim buf As LVITEM
    With buf
        .stateMask = &HF000&    'LVIS_STATEIMAGEMASK
        .state = &H1000&
        If F Then .state = &H2000&
    End With
    ' LVM_SETITEMSTATE
    SendMessageW TT.hChild, &H102B, ii, VarPtr(buf)
End Function

Friend Function WndProc&(ByVal hwnd&, _ 
                         ByVal uMsg&, _ 
                         ByVal wParam&, _ 
                         ByVal lParam&)
    Dim iCode As Long
    Dim lvi As LVITEM
    Dim nlv As NMLISTVIEW
    Dim nia As NMITEMACTIVATE  ' ←追加

    If hwnd = TT.hParent Then
        Select Case uMsg
        
        Case WM_SETFOCUS
            SetFocus TT.hChild
            Exit Function

        Case WM_NOTIFY
            MoveMemory iCode, ByVal lParam + 8, 4
            Select Case iCode
                    
            Case LVN_ITEMCHANGED 'ItemSelected
                MoveMemory nlv, ByVal lParam, LenB(nlv)
                RaiseEvent ItemSelected(nlv.iItem, nlv.iSubItem)
                Exit Function
                
            Case NM_CLICK 'ItemClick
                MoveMemory nia, ByVal lParam, LenB(nia)
                RaiseEvent ItemClick(nia.iItem, nia.iSubItem)
                Exit Function
'
'            Case NM_DBLCLK
'                MoveMemory nia, ByVal lParam, LenB(nia)
'                RaiseEvent ItemClick(nia.iItem, nia.iSubItem)
'                Exit Function
            
            'Case いろいろ...
            '      :
            '      :
            End Select
        End Select
    Else
        Select Case uMsg

        Case WM_MOUSEACTIVATE
            If GetFocus() <> hwnd Then
                acc.accSelect 1&
                WndProc = MA_NOACTIVATE
                Exit Function
            End If

        Case WM_NCDESTROY
            RemoveSubClass
            Exit Function
       '----------------------------
        Case WM_KEYDOWN
            Select Case wParam
                Case VK_RETURN
                   Exit Function
                Case VK_UP, VK_DOWN
                   WndProc = DefSubclassProc(hwnd, uMsg, wParam, lParam)
                   Exit Function
            End Select
            Exit Function
       '-----------------------------
        End Select
    End If
    WndProc = DefSubclassProc(hwnd, uMsg, wParam, lParam)
End Function

Private Function GetPtr&(ByVal ptr&)
    GetPtr = ptr
End Function

Private Sub RemoveSubClass()
    With TT
        If .hChild Then
            RemoveWindowSubclass .hChild, .pfn, .hChild
            .hChild = 0
        End If
        
        If .hParent Then
            RemoveWindowSubclass .hParent, .pfn, .hParent
            .hParent = 0
        End If
    End With
End Sub

Private Sub Class_Terminate()
    RemoveSubClass
End Sub
      
Option Explicit
Private WithEvents LView As ListView

Private Sub UserForm_Initialize()
    Dim i As Long
    Dim Item1 As String
    Dim Item2 As String
    Dim Item3 As String
    '-------------------
    Frame1.Height = 170
    Frame1.Width = 225
    '-------------------

    Set LView = New ListView
    LView.Init Frame1
    SendMessageW TT.hChild, LVM_SETTEXTCOLOR, 0, RGB(0, 0, 0)    '[黒]
'    SendMessageW TT.hChild, LVM_SETTEXTCOLOR, 0, RGB(0, 0, 255)  '[青]

    With LView
        'Hheader
        .InsertColumn "Item", 0, 70
        .InsertColumn "subItem1", 1, 110
        .InsertColumn "subItem2", 2, 110
        'Item
        For i = 0 To 10
            Item1 = "Item" & i
            Item2 = "subItem1-" & i
            Item3 = "subItem2-" & i
            
            .InsertItem Item1, i
            .SetItem Item2, i, 1
            .SetItem Item3, i, 2
            'アイテムにチェックを入れる
           .SetCheckState i, 1
        Next
    End With
End Sub

Private Sub LView_ItemClick(ByVal iItem&, ByVal iSubItem&)  
    TextBox1.Text = iItem
    TextBox2.Text = LView.LabelText(iItem, 2) 'iSubItem or 1 ~2

    If iItem <> -1 Then
        If LView.GetCheckState(iItem) Then
            LView.SetCheckState iItem, 1
        Else
            LView.SetCheckState iItem, 0
        End If
    End If
End Sub

Private Sub LView_ItemSelected(ByVal iItem&, ByVal iSubItem&)
    TextBox1.Text = iItem
    TextBox2.Text = LView.LabelText(iItem, 2) 'iSubItem or 1 ~2
End Sub

Private Sub CommandButton1_Click()
    Dim i As Long
    Dim Item1 As String
    Dim Item2 As String
    Dim Item3 As String
    
    With LView
        'Item
        For i = 0 To 10
            Item1 = "Item" & i
            Item2 = "subItem1-" & i
            Item3 = "subItem2-" & i
            
            .InsertItem Item1, i
            .SetItem Item2, i, 1
            .SetItem Item3, i, 2
            'アイテムにチェックを入れる
            .SetCheckState i, 1
        Next
    End With
End Sub

Private Sub CommandButton2_Click()
    LView.DeleteAllItems
End Sub

Private Sub CommandButton3_Click()
    MsgBox LView.GetItemCount
End Sub

Private Sub CommandButton4_Click()
    UserForm1.Hide
    Unload Me
End Sub

Private Sub CommandButton5_Click()
    Dim i As Long
    Dim cnt As Long
    cnt = LView.GetItemCount
    With LView
        For i = 0 To cnt
           If LView.GetCheckState(i) = False Then
               Debug.Print LView.LabelText(i, 0)
           End If
        Next
    End With
End Sub
      

(2)Subclass Treeview


ツリービューのコードは、私の知識では不可能なので、ネット検索したものを紹介します。
動作確認をしましたが、動作的には宜しいかと思います。

ただフォームに多くのチェックボタンが有り、実用に組み込むには、
そのプロパティ設定をそぎ落とし、ソースコードに盛り込んでの利用がベターと思います。
どなたか、加工してみては如何でしょうか。
又その結果をお知らせ頂ければ、この場で公開したいと思います。


JKP An MSForms (all VBA) treeview

※ダウンロードファイル:Download the treeview sample Excel workbook (including documentation)





写真

その後の情報ですが、
ツリービューのコードを自分なりに加工してみました。
元々表示する事が目的でしたが、元コードには
貴重なツールが有り十分に活用する事が出来ます。
これも作成者JKP氏に深く感謝致します。

この加工編集ブックが著作権に侵害しないとの認識の上で、
ブックを公開致します。(Form画像参照)
利用者のサポートツールになれば幸いです。


※ダウンロードファイル   TreeView.zip TreeView(64bit).zip


私事ですが、JKP氏のコードで発見したこと
・条件付きコンパイル
・エラー発生時の行番号を表示




PAGE TOP