【问题标题】:Vertical Scrolling multiline "listview" in a child window makes problems at bottom and top子窗口中的垂直滚动多行“listview”在底部和顶部产生问题
【发布时间】:2014-12-19 12:59:00
【问题描述】:

我已经实现了一种“多行列表视图”,创建了一个多行编辑框数组,这些编辑框相互接触并且可以滚动。
只要我在主窗口中执行此操作,它就可以正常工作。但是由于某些原因,我需要在主窗口的子窗口中执行此操作,在这里我在滚动到底部时遇到问题(前一页的丑陋剩余部分留在那里)并返回顶部(同样的问题)。

我做错了什么?

代码:
a) 在主窗口中滚动

#include <windows.h>
#include <stdio.h>

#define ITEMS  30   /*number of items in list*/
#define HEIGHT 80   /*height of one item in pixels*/

static int curr_y=20;     // current y position to add next item

static HWND hWnd;         // main windo handle

static int cxClient;// width;
static int cyClient;// height

//-------------------------------------------------------
void GetClientSize(HWND hWndParent)
{
RECT rcl;
   GetClientRect(hWndParent, &rcl);
   cxClient = rcl.right;
   cyClient = rcl.bottom;
}
//-------------------------------------------------------
void Debug(TCHAR *Fmt, ...)
{
    va_list argptr;
    TCHAR buf[1000];
    va_start(argptr, Fmt);
    vsprintf(buf, Fmt, argptr);
    OutputDebugString(buf);
    va_end(argptr);
}

//-------------------------------------------------------
static void VScrollClient(HWND hwnd, int pos)
{
    static int prev = 0;
    int cy = prev - pos;
    prev = pos;
    if(cy)  ScrollWindow(hwnd, 0, cy, NULL, NULL);
}
//-------------------------------------------------------
static void CreateMultilineBox(HWND hwnd, int id)
{
   GetClientSize(hwnd);

   TCHAR buf[100];
   sprintf(buf,TEXT("This is\r\nBox %d"),id);

   CreateWindow(TEXT("edit"), buf,
                WS_VISIBLE | WS_CHILD | WS_BORDER |  ES_MULTILINE ,
                50, curr_y, cxClient-100, HEIGHT+1,
                hwnd, (HMENU)id+100,
                GetModuleHandle(NULL), NULL);

   curr_y += HEIGHT;
}
//-------------------------------------------------------

static LRESULT
CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
      case WM_CREATE:
          {
             // setup scroll info (Min,Max,Page size)
             SCROLLINFO si = {0};
             si.cbSize = sizeof(SCROLLINFO);
             si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
             si.nPos = 0;
             si.nMin = 0;
             si.nMax = HEIGHT*ITEMS;
             si.nPage = cyClient;
             SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
          }
        break;

       case WM_SIZE:
       {
            int cyClient = HIWORD(lParam);

            // update scroll page size
            SCROLLINFO si = {0};
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_PAGE;
            si.nPage = cyClient;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

            // update window if maximized.
            if(wParam == SIZE_MAXIMIZED)
            {
                si.fMask = SIF_RANGE | SIF_POS;
                GetScrollInfo(hwnd, SB_VERT, &si);
                VScrollClient(hwnd, si.nPos);
            }
            return 0;
       }

       case WM_VSCROLL:
       {
            SCROLLINFO si = {0};
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
            GetScrollInfo(hwnd, SB_VERT, &si);

            const int minPos = si.nMin;
            const int maxPos = si.nMax - (si.nPage - HEIGHT);
            int pos = -1;

        Debug(TEXT("\n nMax=%d nPage=%d\n minPos=%d maxPos=%d nPos=%d"),
                si.nMax, si.nPage, minPos, maxPos, si.nPos);

            switch(LOWORD(wParam)) //code
            {
            case SB_LINEUP:
                pos = max(si.nPos - HEIGHT, minPos);
                break;

            case SB_LINEDOWN:
                pos = min(si.nPos + HEIGHT, maxPos);
                break;

            case SB_PAGEUP:
                pos = max(si.nPos - (int)si.nPage, minPos);
                break;

            case SB_PAGEDOWN:
                pos = min(si.nPos + (int)si.nPage, maxPos);
                break;

            case SB_TOP:
                pos = minPos;
                break;

            case SB_BOTTOM:
                pos = maxPos;
                break;

            case SB_THUMBTRACK:
                if(si.nTrackPos>=maxPos-HEIGHT) pos=maxPos;
                else pos = (si.nTrackPos/HEIGHT)*(HEIGHT);
                break;

            }
            if(pos == -1) return 0;
            SetScrollPos(hwnd, SB_VERT, pos, TRUE);
            VScrollClient(hwnd, pos);

            return 0;
       }
       case WM_KEYDOWN:
       {
           switch(wParam) // character code
           {
               case VK_UP:
                    SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0);
                    break;
               case VK_DOWN:
                    SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN,0);
                    break;
               case VK_PRIOR:
                    SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
                    break;
               case VK_NEXT:
                    SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
                    break;
               case VK_HOME:
                    SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0);
                    break;
               case VK_END:
                    SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0);
                    break;
               default:
                    return 0;
           }
           return 0;
       }

       case WM_PAINT:
       {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            EndPaint(hwnd, &ps);
            ReleaseDC(hwnd, hdc);
            return 0;
       }

      case WM_DESTROY:

        PostQuitMessage(0);
        break;
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

//-------------------------------------------------------

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PWSTR lpCmdLine, int nCmdShow)
{
  MSG  msg;
  WNDCLASSW wc = {0};
  wc.lpszClassName = L"myclass";
  wc.hInstance     = hInstance;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);


  RegisterClassW(&wc);

  hWnd=CreateWindowW(wc.lpszClassName, L"test scrolling",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_VSCROLL,
                10, 10, 900, 600, 0, 0, hInstance, 0);

    if(!hWnd) return 0;

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

  for(int i=0;i<ITEMS;i++)
  {
    CreateMultilineBox(hWnd, i);
  }

  while( GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (int) msg.wParam;
}

b) 在子窗口中滚动

#include <windows.h>
#include <stdio.h>

#define ITEMS  30   /*number of items in list*/
#define HEIGHT 80   /*height of one item in pixels*/

static int curr_y=20;     // current y position to add next item

static HWND hWnd;         // main windo handle
static HWND hChild;

static int cxClient;// width;
static int cyClient;// height

//-------------------------------------------------------
void GetClientSize(HWND hWndParent)
{
RECT rcl;
   GetClientRect(hWndParent, &rcl);
   cxClient = rcl.right;
   cyClient = rcl.bottom;
}
//-------------------------------------------------------
void Debug(TCHAR *Fmt, ...)
{
    va_list argptr;
    TCHAR buf[1000];
    va_start(argptr, Fmt);
    vsprintf(buf, Fmt, argptr);
    OutputDebugString(buf);
    va_end(argptr);
}

//-------------------------------------------------------
static void VScrollClient(HWND hwnd, int pos)
{
    static int prev = 0;
    int cy = prev - pos;
    prev = pos;
    if(cy)  ScrollWindow(hwnd, 0, cy, NULL, NULL);
}
//-------------------------------------------------------
static void CreateMultilineBox(HWND hwnd, int id)
{
   GetClientSize(hwnd);

   TCHAR buf[100];
   sprintf(buf,TEXT("This is\r\nBox %d"),id);

   CreateWindow(TEXT("edit"), buf,
                WS_VISIBLE | WS_CHILD | WS_BORDER |  ES_MULTILINE ,
                50, curr_y, cxClient-100, HEIGHT+1,
                hwnd, (HMENU)id+100,
                GetModuleHandle(NULL), NULL);

   curr_y += HEIGHT;
}
//-------------------------------------------------------

static LRESULT
CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
      case WM_CREATE:
          {
             // setup scroll info (Min,Max,Page size)
             SCROLLINFO si = {0};
             si.cbSize = sizeof(SCROLLINFO);
             si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
             si.nPos = 0;
             si.nMin = 0;
             si.nMax = HEIGHT*ITEMS;
             si.nPage = cyClient;
             SetScrollInfo(hwnd, SB_VERT, &si, FALSE);
          }
        break;

       case WM_SIZE:
       {
            int cyClient = HIWORD(lParam);

            // update scroll page size
            SCROLLINFO si = {0};
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_PAGE;
            si.nPage = cyClient;
            SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

            // update window if maximized.
            if(wParam == SIZE_MAXIMIZED)
            {
                si.fMask = SIF_RANGE | SIF_POS;
                GetScrollInfo(hwnd, SB_VERT, &si);
                VScrollClient(hwnd, si.nPos);
            }
            return 0;
       }

       case WM_VSCROLL:
       {
            SCROLLINFO si = {0};
            si.cbSize = sizeof(SCROLLINFO);
            si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
            GetScrollInfo(hwnd, SB_VERT, &si);

            const int minPos = si.nMin;
            const int maxPos = si.nMax - (si.nPage - HEIGHT);
            int pos = -1;

        Debug(TEXT("\n nMax=%d nPage=%d\n minPos=%d maxPos=%d nPos=%d"),
                si.nMax, si.nPage, minPos, maxPos, si.nPos);

            switch(LOWORD(wParam)) //code
            {
            case SB_LINEUP:
                pos = max(si.nPos - HEIGHT, minPos);
                break;

            case SB_LINEDOWN:
                pos = min(si.nPos + HEIGHT, maxPos);
                break;

            case SB_PAGEUP:
                pos = max(si.nPos - (int)si.nPage, minPos);
                break;

            case SB_PAGEDOWN:
                pos = min(si.nPos + (int)si.nPage, maxPos);
                break;

            case SB_TOP:
                pos = minPos;
                break;

            case SB_BOTTOM:
                pos = maxPos;
                break;

            case SB_THUMBTRACK:
                if(si.nTrackPos>=maxPos-HEIGHT) pos=maxPos;
                else pos = (si.nTrackPos/HEIGHT)*(HEIGHT);
                break;

            }
            if(pos == -1) return 0;
            SetScrollPos(hwnd, SB_VERT, pos, TRUE);
            VScrollClient(hwnd, pos);

            return 0;
       }
       case WM_KEYDOWN:
       {
           switch(wParam) // character code
           {
               case VK_UP:
                    SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0);
                    break;
               case VK_DOWN:
                    SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN,0);
                    break;
               case VK_PRIOR:
                    SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0);
                    break;
               case VK_NEXT:
                    SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
                    break;
               case VK_HOME:
                    SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0);
                    break;
               case VK_END:
                    SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0);
                    break;
               default:
                    return 0;
           }
           return 0;
       }

       case WM_PAINT:
       {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            EndPaint(hwnd, &ps);
            ReleaseDC(hwnd, hdc);
            return 0;
       }

      case WM_DESTROY:

        PostQuitMessage(0);
        break;
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);

} /* ChildProc */

//==============================================================================
static void ChildRegister(void)
{
    WNDCLASS wc = { 0 };
    wc.style = 0;
    wc.lpfnWndProc = ChildProc;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = TEXT("Child");
    RegisterClass(&wc);
}

//-------------------------------------------------------
static HWND CreateChild(HWND hwnd)
{
    GetClientSize(hwnd);

    HWND hChild = CreateWindow(TEXT("Child"), NULL,
       WS_VISIBLE | WS_CHILD | WS_VSCROLL,
        0, 0, cxClient, cyClient,
        hwnd, (HMENU)0, GetModuleHandle(NULL), NULL);
    if(!hChild)
    {
      Debug(TEXT("\r\nCreate Cont failed, error=%d"),GetLastError());
    }
    return hChild;
}

//------------------------------------------------------------

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
    WPARAM wParam, LPARAM lParam)
{

  switch(msg)
  {
      case WM_CREATE:

        break;

       case WM_SIZE:
       {
           cxClient = LOWORD (lParam) ; // width
           cyClient = HIWORD (lParam) ; // heigth
           MoveWindow(hChild, 0, 0, cxClient, cyClient, TRUE);
        break;
       }

      case WM_DESTROY:

        PostQuitMessage(0);
        break;
  }

  return DefWindowProcW(hwnd, msg, wParam, lParam);
}

//-------------------------------------------------------
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    PWSTR lpCmdLine, int nCmdShow)
{
  MSG  msg;
  WNDCLASSW wc = {0};
  wc.lpszClassName = L"myclass";
  wc.hInstance     = hInstance;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);


  RegisterClassW(&wc);

  hWnd=CreateWindowW(wc.lpszClassName, L"test scrolling",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                10, 10, 900, 600, 0, 0, hInstance, 0);

    if(!hWnd) return 0;

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

    ChildRegister();
    hChild =CreateChild(hWnd);
    if(!hChild) return 0;
    SetFocus(hChild);

    for(int i=0;i<ITEMS;i++)
    {
      CreateMultilineBox(hChild, i);
    }

  while( GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return (int) msg.wParam;
}

【问题讨论】:

  • 你能发一张截图来说明问题吗?
  • 如果你能看到图片,在底部应该显示“Box 29”和一个灰色条,在顶部显示“Box 0”和一个灰色条,但这里的灰色条被覆盖旧屏幕内容(仅在我在子窗口中滚动时)
  • “丑陋的残留物”是油漆问题。你肯定有,在你从 BeginPaint() 得到的 HDC 上调用 ReleaseDC() 是非法的。
  • 谢谢,不知道我是如何把这个 ReleaseDC() 放到我的 WM_PAINT 中的,但它当然是错误的。然而奇怪的是它并没有引起更多的问题。如果没有它,我在这里描述的问题仍然存在,我发现的唯一解决方案是在滚动到达底部或顶部时对整个 hWnd 进行 InvalidateRect,如我的回答中所述。顺便说一句,我自己什么都不画,因为编辑框自己画。

标签: listview winapi multiline vertical-scrolling


【解决方案1】:

这是我的解决方案:

如果 ((pos == maxPos)||(pos == minPos)) InvalidateRect(hWnd, NULL, TRUE);

【讨论】:

  • 有人能告诉我为什么只有在子窗口 hChild 中滚动而不是在我直接在主窗口 hWnd 中滚动时才需要这样做吗?
猜你喜欢
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多