リストビューの中にプログレスバーを表示するには?

解決


太一郎  2004-07-20 03:47:08  No: 84664  IP: [192.*.*.*]

ファイルダウンロード支援ツールなどにあるリストビューの中にプログレスバーがあって、ファイルのダウンロードの進行具合を把握できるやつありますよね。あれってどうやってるのでしょう?
カスタムドローとかでやらないとなのでしょうか

編集 削除
魔界の仮面弁士  2004-07-20 10:08:38  No: 84665  IP: [192.*.*.*]

そうですね、カスタムドローという事になるかと思います。

VB6であれば、下記の『グラフ機能付きリストビュー』とか。
http://www.mitene.or.jp/~sugisita/vb6_cmctl.html

編集 削除
太一郎  2004-07-20 16:34:56  No: 84666  IP: [192.*.*.*]

解答ありがとうございます。
まさにこういうことがやりたかったので、参考になりました。

今ダウンロードしていじっているんですが、プログレスバー的な使い方をするとちらつきがかなり気になります。

ソースの中のコメントにちらつきの解決策の例が書かれているのですが、当方3週間前に初めてVBに触ったもので書いてあることがよくわかりません。

カスタムドローの処理系をメモリデバイスコンテキストを描画してからBitBltで一括転送するようにしてみると良い・・・

と書かれているのですが、どこをどういじっていいのやら・・・誰かわかる方がいたらご教授願います。

編集 削除
ますお  2004-07-21 16:38:41  No: 84667  IP: [192.*.*.*]

> カスタムドローの処理系をメモリデバイスコンテキストを描画してから
> BitBltで一括転送するようにしてみると良い・・・

以前試した時、カスタムドローでは、ちらつきを抑えきれなかったと思う。
代わりに、WM_PAINTメッセージで、描画処理をした様な...
むろん、メモリDCに描いてBitBltが必須。
何処かに、サンプルがあったと思うが...思い出せない。

以上

編集 削除
太一郎  2004-07-21 18:56:08  No: 84668  IP: [192.*.*.*]

今、ちらつきの事に関するサイト巡りをしております。

>以前試した時、カスタムドローでは、ちらつきを抑えきれなかったと思う。
>代わりに、WM_PAINTメッセージで、描画処理をした様な...

そうなんですか?知識が浅いものでよくわからないのですが・・・
ちょっと長くなりますがソースを載せてみます。

'==============================================================================
'   サブクラス化を行う際のウィンドウプロシージャ
'==============================================================================
Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    
    Dim udtNMHdr    As NMHDR
    Dim udtNMLvw    As NMLISTVIEW
    Dim udtCdLvw    As NMLVCUSTOMDRAW
    Dim colBarItems As BarItems
    Dim clsBarItem  As BarItem
    Dim lngObj      As Long
    
    Select Case uMsg
        
    Case WM_NOTIFY
        CopyMemory udtNMHdr, ByVal lParam, LenB(udtNMHdr)
        If GetProp(udtNMHdr.hwndFrom, PROP_GRAPHLIST) Then
            CopyMemory udtNMLvw, ByVal lParam, LenB(udtNMLvw)
            If udtNMLvw.hdr.code = NM_CUSTOMDRAW Then
                CopyMemory udtCdLvw, ByVal lParam, LenB(udtCdLvw)
                Select Case udtCdLvw.nmcd.dwDrawStage
                Case CDDS_POSTERASE, CDDS_PREERASE
                    WindowProc = CDRF_DODEFAULT
                Case CDDS_PREPAINT
                    WindowProc = CDRF_NOTIFYITEMDRAW
                Case CDDS_ITEMPREPAINT  'アイテムの描画を行う直前のメッセージ
                    WindowProc = CDRF_NOTIFYPOSTPAINT
                Case CDDS_ITEMPOSTPAINT 'アイテムの描画を行った直後のメッセージ
                    '-- グラフの描画
                    Set colBarItems = objArray.Item(CStr(udtNMHdr.hwndFrom))
                    For Each clsBarItem In colBarItems
                        Call DrawGraph(udtNMLvw.hdr.hwndFrom, udtCdLvw.nmcd.hdc, _
                                       udtCdLvw.nmcd.dwItemSpec, clsBarItem.TargetColumn, _
                                       clsBarItem.ForeColor)
                    Next
                    WindowProc = CDRF_DODEFAULT
                End Select
            End If
        End If
    Case Else
    End Select
    If WindowProc = 0& Then
        WindowProc = CallWindowProc(lpProcOrg, hwnd, uMsg, wParam, lParam)
    End If
End Function

'==============================================================================
'   グラフの描画メソッド
'==============================================================================
Private Sub DrawGraph(ByVal hwnd As Long, ByVal hdc As Long, _
                      ByVal iItemIndex As Long, ByVal iColumnIndex As Long, _
                      ByVal clrForeColor As Long)
    
    Dim strVal      As String
    Dim curVal      As Currency
    
    Dim udtRect As RECT
    Dim hFont   As Long
    Dim hOldBrush   As Long
    Dim hBrush      As Long
    Dim hOldPen     As Long
    Dim hPen        As Long
    Dim lngOldColor As Long
    
    ListView_GetSubItemRect hwnd, iItemIndex, iColumnIndex, LVIR_BOUNDS, udtRect
    
    '-- 見た目を良くするために各行間を1ピクセルづつあける
    udtRect.Top = udtRect.Top + 1
    udtRect.Bottom = udtRect.Bottom
    udtRect.Left = udtRect.Left + 1
    udtRect.Right = udtRect.Right - 1
    
    hFont = GetStockObject(DEFAULT_GUI_FONT)
    SelectObject hdc, hFont
    
    '-- いったん白で塗りつぶす
    hPen = GetStockObject(NULL_PEN)
    hBrush = CreateSolidBrush(vbWhite)
    hOldPen = SelectObject(hdc, hPen)
    hOldBrush = SelectObject(hdc, hBrush)
    Rectangle hdc, _
              udtRect.Left, _
              udtRect.Top, _
              udtRect.Right, _
              udtRect.Bottom
    DeleteObject SelectObject(hdc, hOldBrush)
    
    '-- テキストの描画
    lngOldColor = GetTextColor(hdc)
    ListView_GetItemText hwnd, iItemIndex, iColumnIndex, strVal
    If IsNumeric(strVal) Then
        curVal = CCur(strVal)
    Else
        curVal = 0
    End If
    SetTextColor hdc, clrForeColor
    DrawText hdc, curVal & "%", -1, udtRect, DT_CENTER Or DT_VCENTER Or DT_SINGLELINE
    If curVal > 100 Then
        curVal = 100
    End If
    
    '-- 反転モード
    SetROP2 hdc, R2_NOTXORPEN
    
    '-- 値に応じたバーの塗りつぶし
    hBrush = CreateSolidBrush(clrForeColor)
    hOldBrush = SelectObject(hdc, hBrush)
    Rectangle hdc, _
              udtRect.Left, _
              udtRect.Top, _
              udtRect.Left + Fix((udtRect.Right - udtRect.Left) * curVal / 100), _
              udtRect.Bottom
    DeleteObject SelectObject(hdc, hOldBrush)
    DeleteObject SelectObject(hdc, hOldPen)
    
    '-- 枠線の描画
    SetROP2 hdc, R2_COPYPEN
    hBrush = GetStockObject(NULL_BRUSH)
    hOldBrush = SelectObject(hdc, hBrush)
    hPen = GetStockObject(BLACK_PEN)
    hOldPen = SelectObject(hdc, hPen)
    Rectangle hdc, _
              udtRect.Left, _
              udtRect.Top, _
              udtRect.Right, _
              udtRect.Bottom
    Call SelectObject(hdc, hOldBrush)
    Call SelectObject(hdc, hOldPen)
    
    '-- 最後にテキストカラーを描画前の色に戻す
    SetTextColor hdc, lngOldColor

End Sub

DrawGraphの中でhdc(表画面?)に逐一書き込んでいるのがちらつきの原因なんでしょうかね?サイト巡りをしてみたところ、CreateCompatibleDCなんかを使って裏画面みたいなものをつくって、それを一気に表画面に転送すればちらつきを抑えられる見たいな事がよく書かれているのですが・・・実際にどういう風にやればいいかわかりません。

それともますおさんがおっしゃるみたいにカスタムドローではちらつきは抑えられないのでしょうか?

編集 削除
ますお  2004-07-21 22:07:51  No: 84669  IP: [192.*.*.*]

> ちょっと長くなりますがソースを載せてみます。
無断転載なら、マズイんとちゃいまっか?
リンク貼ってもらってるんで必要なかったかと。

> カスタムドローではちらつきは抑えられないのでしょうか?
カスタムドローでその方法を見つけ出せなかっただけどす。

代替策として、WM_PAINTメッセージ処理で、グラフ機能を付けて、
ちらつきを抑えたVB6サンプルが残っていたので、載せておきます。
未完成なので、適当に手を入れて下さい。
「グラフ機能付きリストビュー」サンプルに対して以下を変更。

Private Sub Form_Load()
    ・・・・・・・・
    '-- サブクラス化
    SubClass ListView1.hwnd  '// ウィンドウハンドル変更
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    '-- サブクラス化終了
    UnSubClass ListView1.hwnd  '// ウィンドウハンドル変更

    ・・・・・・・・
End Sub

'// 中身をすべて差し換える。
Public Function WindowProc(ByVal hwnd As Long, _
                           ByVal uMsg As Long, _
                           ByVal wParam As Long, _
                           ByVal lParam As Long) As Long
    Select Case uMsg
    Case WM_PAINT
        Static fNotPaint  As Boolean
        Dim rcLV          As RECT
        Dim ps            As PAINTSTRUCT
        Dim hdc           As Long
        Dim hdcMem        As Long
        Dim hBmpMem       As Long
        Dim hOldBmpMem    As Long
        Dim hdcMem0       As Long
        Dim hBmpMem0      As Long
        Dim hOldBmpMem0   As Long

        Dim iItem         As Long
        Dim iColumn       As Long
        Dim clrForeColor  As Long
        Dim lngOldColor   As Long
        Dim rcItem        As RECT
        Dim rc            As RECT
        Dim hFont         As Long
        Dim hOldFont      As Long
        Dim hPen          As Long
        Dim hOldPen       As Long
        Dim hBrush        As Long
        Dim hOldBrush     As Long
        Dim strVal        As String
        Dim curVal        As Currency

        If (fNotPaint) Then
            WindowProc = CallWindowProc(lpProcOrg, hwnd, uMsg, wParam, lParam)
            Exit Function
        End If
        fNotPaint = True        
        hdc = BeginPaint(hwnd, ps)

        hdcMem = CreateCompatibleDC(hdc)
        Call GetClientRect(hwnd, rcLV)
        hBmpMem = CreateCompatibleBitmap(hdc, rcLV.Right - rcLV.Left, _
                                         rcLV.Bottom - rcLV.Top)
        hOldBmpMem = SelectObject(hdcMem, hBmpMem)
        Call Rectangle(hdcMem, -1, -1, rcLV.Right - rcLV.Left + 1, _
                       rcLV.Bottom - rcLV.Top + 1)
        Call SendMessage(hwnd, WM_PAINT, hdcMem, ByVal 0&)

        clrForeColor = vbBlack
        For iItem = 0 To 2
            For iColumn = 3 To 4
                Call ListView_GetSubItemRect(hwnd, iItem, iColumn, _
                                             LVIR_BOUNDS, rcItem)
                hdcMem0 = CreateCompatibleDC(hdc)
                hBmpMem0 = CreateCompatibleBitmap(hdc, _
                                rcItem.Right - rcItem.Left, _
                                rcItem.Bottom - rcItem.Top)
                hOldBmpMem0 = SelectObject(hdcMem0, hBmpMem0)
                
                '-- いったん白で塗りつぶす
                hBrush = CreateSolidBrush(vbWhite)
                hOldBrush = SelectObject(hdcMem0, hBrush)
                Call Rectangle(hdcMem0, -1, -1, _
                               rcItem.Right - rcItem.Left + 1, _
                               rcItem.Bottom - rcItem.Top + 1)
                Call SelectObject(hdcMem0, hOldBrush)
                Call DeleteObject(hBrush)

                '-- 見た目を良くするために各行間を1ピクセルづつあける
                Call SetRect(rc, 1, 1, rcItem.Right - rcItem.Left - 1, _
                             rcItem.Bottom - rcItem.Top - 1)

                '-- テキストの描画
                hFont = GetStockObject(DEFAULT_GUI_FONT)
                hOldFont = SelectObject(hdcMem0, hFont)
                lngOldColor = SetTextColor(hdcMem0, clrForeColor)
                Call ListView_GetItemText(hwnd, iItem, iColumn, strVal)
                If (IsNumeric(strVal)) Then
                    curVal = CCur(strVal)
                Else
                    curVal = 0
                End If
                Call DrawText(hdcMem0, curVal & "%", -1, rc, _
                        DT_CENTER Or DT_VCENTER Or DT_SINGLELINE)
                If (curVal > 100) Then curVal = 100
                Call SelectObject(hdcMem0, hOldFont)
                Call SetTextColor(hdcMem0, lngOldColor)

                '-- 反転モード
                Call SetROP2(hdcMem0, R2_NOTXORPEN)    
                '-- 値に応じたバーの塗りつぶし
                hPen = GetStockObject(NULL_PEN)
                hOldPen = SelectObject(hdcMem0, hPen)
                hBrush = CreateSolidBrush(clrForeColor)
                hOldBrush = SelectObject(hdcMem0, hBrush)
                Call Rectangle(hdcMem0, rc.Left, rc.Top, rc.Left + _
                        Fix((rc.Right - rc.Left) * curVal / 100), _
                        rc.Bottom)
                Call SelectObject(hdcMem0, hOldBrush)
                Call DeleteObject(hBrush)
                Call SelectObject(hdcMem0, hOldPen)

                '-- 枠線の描画
                Call SetROP2(hdcMem0, R2_COPYPEN)
                hPen = GetStockObject(BLACK_PEN)
                hOldPen = SelectObject(hdcMem0, hPen)
                hBrush = GetStockObject(NULL_BRUSH)
                hOldBrush = SelectObject(hdcMem0, hBrush)
                Call Rectangle(hdcMem0, rc.Left, rc.Top, _
                               rc.Right, rc.Bottom)

                Call BitBlt(hdcMem, rcItem.Left, rcItem.Top, _
                            rcItem.Right - rcItem.Left, _
                            rcItem.Bottom - rcItem.Top, _
                            hdcMem0, 0, 0, vbSrcCopy)

                Call SelectObject(hdcMem0, hOldBrush)
                Call SelectObject(hdcMem0, hOldPen)
                
                Call SelectObject(hdcMem0, hOldBmpMem0)
                Call DeleteObject(hBmpMem0)
                Call DeleteDC(hdcMem0)
            Next
        Next

        Call BitBlt(hdc, rcLV.Left, 16, rcLV.Right - rcLV.Left, _
                    rcLV.Bottom - rcLV.Top, hdcMem, 0, 16, vbSrcCopy)
        
        Call SelectObject(hdcMem, hOldBmpMem)
        Call DeleteObject(hBmpMem)
        Call DeleteDC(hdcMem)

        Call EndPaint(hwnd, ps)
        fNotPaint = False
        WindowProc = 0
        Exit Function
    Case WM_ERASEBKGND
        WindowProc = 1
        Exit Function
    End Select
    WindowProc = CallWindowProc(lpProcOrg, hwnd, uMsg, wParam, lParam)
End Function

'// 標準モジュールの宣言部に追加する。
Private Const WM_PAINT = &HF
Private Const WM_ERASEBKGND = &H14
       
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function SetRect Lib "user32" (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function InvalidateRect Lib "user32" (ByVal hwnd As Long, ByVal lpRect As Long, ByVal bErase As Long) As Long

Private Type PAINTSTRUCT
    hdc        As Long
    fErase     As Long
    rcPaint    As RECT
    fRestore   As Long
    fIncUpdate As Long
    rgbReserved As Byte
End Type

Private Declare Function BeginPaint Lib "user32" (ByVal hwnd As Long, lpPaint As PAINTSTRUCT) As Long
Private Declare Function EndPaint Lib "user32" (ByVal hwnd As Long, lpPaint As PAINTSTRUCT) As Long

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long

編集 削除
太一郎  2004-07-21 23:07:25  No: 84670  IP: [192.*.*.*]

>無断転載なら、マズイんとちゃいまっか?
>リンク貼ってもらってるんで必要なかったかと。

確かに(;--)  配慮に欠けてました。
事後報告になってしまうけどサイトさんのほうには謝罪メールを入れておきます。

ますおさん  ヒントをありがとうございました。いろいろサイト巡りをして得た知識とあわせて、描画のコツがわかった気がします。これから試行錯誤して完成させたいと思います。

編集 削除