2016-12-08 19 views
2

CreateWindowExを使用してListViewを作成し、クラス名としてWC_LISTVIEWを使用しました。WinAPI WC_LISTVIEWペイントの問題

スムーズなスクロールを作成しようとしています。リストが正しく描かれていないことを除いて、すべてが完全に機能します。リストのスクリーンショットの下を参照してください: List View Paint Problem

リストビューは、CreateWindowEx関数で次のスタイルを持っている:私は、ウィンドウをスクロールするためにタイマーを使用してい

WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER | 
     WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED 

。それは以下を行います:

ScrollWindowEx(
    listHandle, 
    0, 
    step * linesDelta, 
    NULL, 
    NULL, 
    0, 0, 0 
    ); 
UpdateWindow(listHandle); 

スクロールは塗り以外完全に動作します。

Iが試み:

  1. UpdateWindow() - screenshootが接続されているすべての可能なオプションと

  2. RedrawWindow - ウィンドウは一度しか描かれ

  3. InvalidateRect + UpdateWindow = 2

  4. と同じ
  5. InvalidateRect + SendMessage(hwnd、WM_PAINT、0、0) - 2と同じ

リストについては、項目をペイントコードは以下の通りです:

LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) { 

    Item *itemData = (Item *)drawStruct->itemData; 
    HDC hdc = drawStruct->hDC; 

    COLORREF backgroundColor; 
    COLORREF oldColor; 

    if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) { 
     backgroundColor = GetSysColor(COLOR_HIGHLIGHT); 
     oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 
    } else { 
     backgroundColor = RGB(255, 255, 255); 
     oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT)); 
    } 

    HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor); 

    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush); 
    FillRect(hdc, &drawStruct->rcItem, backgroundBrush); 

    drawStruct->rcItem.left += 5; 
    drawStruct->rcItem.right -= 5; 

    drawStruct->rcItem.left += 30; 
    DrawText(hdc, itemData->path, -1, &drawStruct->rcItem, 
     DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); 
    drawStruct->rcItem.left -= 30; 

    if (itemData->searchData && itemData->searchData->bitmap) { 
     HBITMAP bitmap = itemData->searchData->bitmap; 
     HDC hdcMem = CreateCompatibleDC(hdc); 
     HGDIOBJ oldBitmap = SelectObject(hdcMem, bitmap); 

     BITMAPINFO bi = { 0 }; 
     bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 

     // Get the bitmap info header. 
     if (0 != GetDIBits(
      hdcMem, // hdc 
      bitmap, // hbmp 
      0,   // uStartScan 
      0,   // cScanLines 
      NULL,  // lpvBits 
      &bi, 
      DIB_RGB_COLORS 
      )) { 

      BLENDFUNCTION blendFunc; 
      blendFunc.BlendOp = AC_SRC_OVER; 
      blendFunc.BlendFlags = 0; 
      blendFunc.SourceConstantAlpha = 255; 
      blendFunc.AlphaFormat = AC_SRC_ALPHA; 

      AlphaBlend(hdc, 
       drawStruct->rcItem.left + 2, //dest X 
       drawStruct->rcItem.top + 3, //dest Y 
       bi.bmiHeader.biWidth, 
       bi.bmiHeader.biHeight, 
       hdcMem, 0, 0, 
       bi.bmiHeader.biWidth, 
       bi.bmiHeader.biHeight, blendFunc); 
     } 

     SelectObject(hdcMem, oldBitmap); 
     DeleteDC(hdcMem); 
    } 

    SelectObject(hdc, hOldBrush); 
    DeleteObject(backgroundBrush); 
    SetTextColor(hdc, oldColor); 

    return 0; 
} 

誰もがこのためのソリューションを知っていますか?

まったく同じ動作をしますゼロから作成完全な例は下記をご覧ください:

#include "stdafx.h" 
#include "TestList.h" 
#include <strsafe.h> 
#include <commctrl.h> 

#define MAX_LOADSTRING 100 
#define ID_LIST_BOX 200 

#define TIMER_ID_SMOOTH_SCROLL 100 

class ListData { 

    int scrollToDelta; 

    int currentScrollPos; 

    int numPixelsToChangeScrollPos; 

    int numPixelsChanged; 

public: 

    HWND listWindow; 

    WNDPROC defaultListProcedure; 

    void startSmoothScrolling(HWND hwnd, int delta) { 
     if (delta < 0) { 
      scrollToDelta = 100; 
     } else { 
      scrollToDelta = -100; 
     } 

     SCROLLINFO si; 
     si.cbSize = sizeof(si); 
     si.fMask = SIF_RANGE | SIF_POS; 

     if (GetScrollInfo(listWindow, SB_VERT, &si)) { 
      double count = SendMessage(listWindow, LVM_GETITEMCOUNT, 0, 0); 
      double totalHeight = count * 30; 

      currentScrollPos = (int)((totalHeight * (double)si.nPos)/(double)si.nMax); 
      numPixelsToChangeScrollPos = totalHeight/si.nMax; 
      numPixelsChanged = 0; 
     } else { 
      currentScrollPos = 0; 
      numPixelsChanged = 0; 
      numPixelsToChangeScrollPos = 30; 
     } 

    } 

    void smoothScroll(HWND listHandle) { 

     SCROLLINFO si; 
     si.cbSize = sizeof(si); 
     si.fMask = SIF_RANGE | SIF_POS; 

     DWORD linesDelta; 
     SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesDelta, 0); 

     if (scrollToDelta < 0) { 
      if (GetScrollInfo(listHandle, SB_VERT, &si)) { 
       if (si.nPos == 0) { 
        KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL); 
        return; 
       } 
      } 

      scrollToDelta += 5; 
      int step = -5; 
      if (scrollToDelta > -80) { 
       step = -4; 
      } else if (scrollToDelta > -60) { 
       step = -3; 
      } else if (scrollToDelta > -40) { 
       step = -3; 
      } else if (scrollToDelta > -20) { 
       step = -2; 
      } 

      numPixelsChanged += abs(step); 
      if (numPixelsChanged >= numPixelsToChangeScrollPos) { 
       int posDelta = numPixelsChanged/numPixelsToChangeScrollPos; 
       numPixelsChanged -= posDelta * numPixelsToChangeScrollPos; 
       si.nPos = si.nPos + posDelta; 
       si.fMask = SIF_POS; 
       SetScrollInfo(listHandle, SB_VERT, &si, TRUE); 
      } 

      ScrollWindowEx(
       listHandle, 
       0, 
       step * linesDelta, 
       NULL, 
       NULL, 
       0, 0, 
       SW_INVALIDATE); 

      if (scrollToDelta >= 0) { 
       KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL); 
      } 
     } else { 

      if (GetScrollInfo(listHandle, SB_VERT, &si)) { 
       int pos = GetScrollPos(listHandle, SB_VERT); 
       if (pos == si.nMax) { 
        KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL); 
        return; 
       } 
      } 

      scrollToDelta -= 5; 
      int step = 5; 
      if (scrollToDelta > -80) { 
       step = 4; 
      } else if (scrollToDelta > -60) { 
       step = 3; 
      } else if (scrollToDelta > -40) { 
       step = 3; 
      } else if (scrollToDelta > -20) { 
       step = 2; 
      } 

      numPixelsChanged += abs(step); 
      if (numPixelsChanged >= numPixelsToChangeScrollPos) { 
       int posDelta = numPixelsChanged/numPixelsToChangeScrollPos; 
       numPixelsChanged -= posDelta * numPixelsToChangeScrollPos; 
       si.nPos = si.nPos - posDelta; 
       si.fMask = SIF_POS; 
       SetScrollInfo(listHandle, SB_VERT, &si, TRUE); 
      } 

      ScrollWindowEx(
       listHandle, 
       0, 
       step * linesDelta, 
       NULL, 
       NULL, 
       0, 0, 0 
       ); 

      if (scrollToDelta <= 0) { 
       KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL); 
      } 
     } 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 

     //RedrawWindow(listHandle, NULL, NULL, 
     // RDW_UPDATENOW | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN | RDW_ERASENOW); 
     //InvalidateRect(listHandle, NULL, FALSE); 
     //SendMessage(listHandle, WM_PAINT, 0, 0); 
     UpdateWindow(listHandle); 
     //ListView_RedrawItems(listHandle, 0, 300); 

     //////////////////////////////////////////////////////////////////////////////////////////////////// 
    } 

}; 

struct Item { 
    WCHAR *name; 
}; 

// Global Variables: 
HINSTANCE hInst;        // current instance 
WCHAR szTitle[MAX_LOADSTRING];     // The title bar text 
WCHAR szWindowClass[MAX_LOADSTRING];   // the main window class name 

// Forward declarations of functions included in this code module: 
ATOM    MyRegisterClass(HINSTANCE hInstance); 
BOOL    InitInstance(HINSTANCE, int); 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); 

int APIENTRY wWinMain(_In_ HINSTANCE hInstance, 
        _In_opt_ HINSTANCE hPrevInstance, 
        _In_ LPWSTR lpCmdLine, 
        _In_ int  nCmdShow) 
{ 
    UNREFERENCED_PARAMETER(hPrevInstance); 
    UNREFERENCED_PARAMETER(lpCmdLine); 

    // TODO: Place code here. 

    // Initialize global strings 
    StringCchCopy(szTitle, MAX_LOADSTRING, L"Test"); 
    StringCchCopy(szWindowClass, MAX_LOADSTRING, L"TestClassList"); 
    MyRegisterClass(hInstance); 

    // Perform application initialization: 
    if (!InitInstance (hInstance, nCmdShow)) 
    { 
     return FALSE; 
    } 

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTLIST)); 

    MSG msg; 

    // Main message loop: 
    while (GetMessage(&msg, nullptr, 0, 0)) 
    { 
     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 

    return (int) msg.wParam; 
} 



// 
// FUNCTION: MyRegisterClass() 
// 
// PURPOSE: Registers the window class. 
// 
ATOM MyRegisterClass(HINSTANCE hInstance) 
{ 
    WNDCLASSEXW wcex; 

    wcex.cbSize = sizeof(WNDCLASSEX); 

    wcex.style   = CS_HREDRAW | CS_VREDRAW; 
    wcex.lpfnWndProc = WndProc; 
    wcex.cbClsExtra  = 0; 
    wcex.cbWndExtra  = 0; 
    wcex.hInstance  = hInstance; 
    wcex.hIcon   = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TESTLIST)); 
    wcex.hCursor  = LoadCursor(nullptr, IDC_ARROW); 
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTLIST); 
    wcex.lpszClassName = szWindowClass; 
    wcex.hIconSm  = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); 

    return RegisterClassExW(&wcex); 
} 

LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) { 

    Item *itemData = (Item *)drawStruct->itemData; 
    HDC hdc = drawStruct->hDC; 

    COLORREF backgroundColor; 
    //pcd->clrTextBk; 
    COLORREF oldColor; 

    if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) { 
     backgroundColor = GetSysColor(COLOR_HIGHLIGHT); 
     oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); 
    } else { 
     backgroundColor = RGB(255, 255, 255); 
     oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT)); 
    } 

    HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor); 

    HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush); 
    FillRect(hdc, &drawStruct->rcItem, backgroundBrush); 

    drawStruct->rcItem.left += 5; 
    drawStruct->rcItem.right -= 5; 

    drawStruct->rcItem.left += 30; 
    DrawText(hdc, itemData->name, -1, &drawStruct->rcItem, 
     DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS); 
    drawStruct->rcItem.left -= 30; 

    SelectObject(hdc, hOldBrush); 
    DeleteObject(backgroundBrush); 
    SetTextColor(hdc, oldColor); 

    return 0; 
} 

LRESULT CALLBACK ListViewWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 

    switch (uMsg) { 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     case WM_TIMER: { 
      if (wParam == TIMER_ID_SMOOTH_SCROLL) { 
       ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); 
       listData->smoothScroll(hwnd); 
      } 
      break; 
     } 
     case WM_MOUSEWHEEL: { 
      int delta = HIWORD(wParam); 
      ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); 
      listData->startSmoothScrolling(hwnd, delta); 
      SetTimer(hwnd, TIMER_ID_SMOOTH_SCROLL, 200, NULL); 
     } 
     //////////////////////////////////////////////////////////////////////////////////////////////////// 
     default: 
      ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA); 
      return CallWindowProc(listData->defaultListProcedure, hwnd, uMsg, wParam, lParam); 
    } 
    return 0; 

} 

// 
// FUNCTION: InitInstance(HINSTANCE, int) 
// 
// PURPOSE: Saves instance handle and creates main window 
// 
// COMMENTS: 
// 
//  In this function, we save the instance handle in a global variable and 
//  create and display the main program window. 
// 
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 
{ 
    hInst = hInstance; // Store instance handle in our global variable 

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 
     100, 100, 400, 400, nullptr, nullptr, hInstance, nullptr); 

    if (!hWnd) { 
     return FALSE; 
    } 

    //////////////////////////////////////////////////////////////////////////////////////////////////// 
    HWND listWindow = CreateWindowEx(
     0, 
     WC_LISTVIEW, 
     L"", 
     WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER | 
     WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED, 
     1, //x 
     1, //y 
     400 - 20, //width 
     400 - 20, //height 
     hWnd, 
     (HMENU)ID_LIST_BOX, 
     hInstance, 
     NULL); 

    ListData *listData = new ListData(); 
    listData->listWindow = listWindow; 

    SetWindowLongPtr(listWindow, GWLP_USERDATA, (LPARAM)listData); 
    listData->defaultListProcedure = (WNDPROC)SetWindowLongPtr(listWindow, GWLP_WNDPROC, (LONG_PTR)ListViewWindowProc); 

    ListView_SetExtendedListViewStyle(listWindow, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS); 
    SendMessage(listWindow, LVM_SETTEXTBKCOLOR, 0, 0xFFFFFF); 

    LVCOLUMN col; 

    col.mask = LVCF_TEXT | LVCF_WIDTH; 
    col.pszText = L""; 
    col.cx = 390; 
    SendMessage(listWindow, LVM_INSERTCOLUMN, 0, (LPARAM)&col); 

    LVITEM item; 
    item.mask = LVIF_PARAM | LVIF_TEXT; 
    item.iSubItem = 0; 

    for (int i = 0; i < 300; i++) { 
     item.iItem = i; 
     Item *itemData = (Item*)malloc(sizeof(Item)); 
     WCHAR *name = (WCHAR*)malloc(sizeof(WCHAR) * 30);; 
     wsprintf(name, L"Item Name %d", i); 
     itemData->name = name; 
     item.pszText = name; 
     item.lParam = (LPARAM)itemData; 
     SendMessage(listWindow, LVM_INSERTITEM, 0, (LPARAM)&item); 
    } 

    //////////////////////////////////////////////////////////////////////////////////////////////////// 
    ShowWindow(hWnd, nCmdShow); 
    UpdateWindow(hWnd); 

    return TRUE; 
} 

// 
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) 
// 
// PURPOSE: Processes messages for the main window. 
// 
// WM_COMMAND - process the application menu 
// WM_PAINT - Paint the main window 
// WM_DESTROY - post a quit message and return 
// 
// 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    switch (message) { 
     case WM_DRAWITEM: { 
      //////////////////////////////////////////////////////////////////////////////////////////////////// 
      if (wParam == ID_LIST_BOX) { 
       DRAWITEMSTRUCT *drawStruct = (DRAWITEMSTRUCT*)lParam; 
       drawItem(drawStruct->hwndItem, drawStruct); 
       return TRUE; 
      } 
      //////////////////////////////////////////////////////////////////////////////////////////////////// 
      break; 
     } 
    case WM_PAINT: { 
      PAINTSTRUCT ps; 
      HDC hdc = BeginPaint(hWnd, &ps); 
      // TODO: Add any drawing code that uses hdc here... 
      EndPaint(hWnd, &ps); 
     } 
     break; 
    case WM_MEASUREITEM: { 
     if (wParam == ID_LIST_BOX) { 
      MEASUREITEMSTRUCT *measureStruct = (MEASUREITEMSTRUCT*)lParam; 
      measureStruct->itemHeight = 30; 
      measureStruct->itemWidth = 390; 
      return TRUE; 
     } 
     break; 
    } 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 
+0

あなたはWM_PAINTを送信することはありませんので、その考えを捨ててください。私たちは多くのコードを持っていません。おそらく、私たちが見ることができないコードには問題があります。 –

+2

問題を再現する完全な例が含まれています。 – Andrei

+0

「スムーズなスクロール」ホイールを再実装するのはなぜですか?ユーザーがこれを望む場合は、システムレベルでオンにすることができます。そして、*制御し続けることもできません。 –

答えて

0

ここでは、古いトリックです:ScrollWindowExを呼び出す前に()背景を消去せずに全体listHandle領域を無効化。

InvalidateRect(listHandle, NULL, FALSE); 
ScrollWindowEx(... 

楽しい一日を! ScrollWindowExのためのMSDNから

0

:SW_INVALIDATEとSW_ERASEフラグが指定されていない場合は

、ScrollWindowExからスクロールされる領域が無効になることはありません。これらのフラグのいずれかが設定されている場合、ScrollWindowExはこの領域を無効にします。この領域は、アプリケーションがUpdateWindow関数を呼び出すか、RDW_UPDATENOWまたはRDW_ERASENOWフラグを指定してRedrawWindow関数を呼び出すか、アプリケーションキューからWM_PAINTメッセージを取得するまで更新されません。

それでは、あなたがしようとしたものを見てみましょう:

UpdateWindow() - UpdateWindowは何もしませんので、screenshootは添付

何も、無効にしないので、更新領域は空でしまいました。すべての可能なオプションを備えた

RedrawWindow - 正しく呼び出された場合、ウィンドウが一度だけ

塗装され、これはクライアントを無効にする必要があります。すぐにWM_ERASEBKGNDを発生させることができますが、WM_PAINTメッセージはキューに何もないときにのみ来ます。 WM_TIMERがWM_PAINTより優先されるため、これはうまくいかないと思われます。 (どちらもあなたが値getMessageを呼び出し、他に何も保留されていない場合、彼らが実際に掲載さが、合成されないという点で特別なメッセージです。)私は仕事にこれを期待

InvalidateRect + UpdateWindow =同じ

2として無効にするには、フラグをScrollWindowExに渡す方が合理的なようです。 私は、コントロールがアイテムを非整数位置に描画するように設計されていないということが起こっていると思います。あなたは無効になっていますが、ウインドウはあなたが予想しているものとは異なるオフセットでアイテムを描画しようとしています。これを解決する簡単な方法はありません。

InvalidateRect +のSendMessage(HWND、WM_PAINT、0、0) - 有効WM_PAINTメッセージはありません2

同じ。それをしないでください。

0

問題は、ListViewがLVS_REPORTフラグを使用して作成されていることです。つまり、リストはスムーズにスクロールできず、行内のみでスクロールできません。私。行の高さがたとえば25の場合、20ピクセルスクロールするとリストが25ピクセルスクロールします。

もう1つの問題は、ScrollWindowExが実際にリストをスクロールしない(または少なくとも正しく使用されなかった)ことです。リストをスクロールするには、ListView_Scrollマクロを使用する必要があります(ピクセルではなく行単位でスクロールします)。

関連する問題