Почему не удается получить сообщение WM_MOUSEMOVE при вызове SetCapture для элемента управления списком?

#windows

Вопрос:

прежде всего

Я не являюсь носителем английского языка. В предложениях может быть много грамматических ошибок. Я постараюсь, насколько смогу, выразить результат, которого я пытаюсь достичь, и проблемы, с которыми я сталкиваюсь. Это на какое-то время заблокировало меня,угнетало. Мне действительно нужна помощь, чтобы указать, где я был неправ и что мне следует делать. Спасибо вам за ваше внимание.

ЧТО Я ДЕЛАЮ

Я пытаюсь перерисовать стандартный элемент управления win32 WC_LISTBOX. Согласно MSDN, вы можете изменить цвет фона , шрифт текста , цвет текста , размер границы, сделать свой собственный рисунок каждого элемента списка. Интерфейс элемента управления WC_LISTBOX, предоставляемого Microsoft, работает очень хорошо, за исключением встроенной полосы прокрутки .

Полоса прокрутки по умолчанию для этого элемента управления-это какая-то белая или серая часть, две стрелки, прокрутка chanel, блок большого пальца.

Я хочу сделать свою собственную картину для полосы прокрутки элемента управления. Я выбрал темный цвет для элемента управления. Цвет шрифта, цвет фона, цвет текста и так далее, как указано выше, можно настроить с помощью предоставленного интерфейса. Но для полосы прокрутки практически нет интерфейса. Кажется, единственный выбор-переписать NCPaint.

После долгих поисков и тестовых кодировок несколько сообщений нужно обработать самостоятельно. Я думаю о том, что я делаю : если пользователь нажмет левую кнопку мыши на прямой линии большого пальца полосы прокрутки, это вызовет действие перетаскивания. Когда пользователь перетаскивает зону большого пальца, я фиксирую сообщение о перемещении мыши , обновляю информацию о прокрутке на полосе прокрутки и рисую полосу прокрутки в соответствии с обновленной информацией о прокрутке.

  1. WM_NCCALCSIZE : Мне нужно вычислить и установить зону прямой кишки Не-клиента и клиента.
 int ListBox_NCCalcSize(HWND hwnd,LPNCCALCSIZE_PARAMS calc)
{
    RECT rect_new;
    RECT rect_old;
    RECT client_rect_new;
    RECT client_rect_old;
    pLBStyle ls=ListBox_GetSettings(hwnd);
    BOOL IsVScroll=(((UINT)GetWindowLongPtr(hwnd,GWL_STYLE)amp;WS_VSCROLL)==WS_VSCROLL);

    CopyRect(amp;rect_new,amp;(calc->rgrc[0]));
    CopyRect(amp;rect_old,amp;(calc->rgrc[1]));
    CopyRect(amp;client_rect_old,amp;(calc->rgrc[2]));
    
    //set new rect for listbox control.
    client_rect_new = {rect_new.left ls->margin_border,
                       rect_new.top ls->margin_border,
                       rect_new.right-ls->margin_border-(IsVScroll?SCROLLBAR_PIXLS:0),
                       rect_new.bottom-ls->margin_border};
    CopyRect(amp;(calc->rgrc[0]),amp;client_rect_new);
    CopyRect(amp;(calc->rgrc[1]),amp;rect_new);
    CopyRect(amp;(calc->rgrc[2]),amp;rect_old);
    
    return WVR_VALIDRECTS;
}
 
  1. WM_NCACTIVATE / WM_NCPAINT : Repaint the border and the scrollbar of the control.
 int ListBox_NCPaint(HWND hwnd)
{
    pLBStyle ls=ListBox_GetSettings(hwnd);
    
    if(!ls) return -1;
    
    HDC hdc=GetWindowDC(hwnd);
    UINT style=GetWindowLongPtr(hwnd,GWL_STYLE);
    RECT rc={0},rc_mem;
    GetWindowRect(hwnd,amp;rc);
    CopyRect(amp;rc_mem,amp;rc);
    OffsetRect(amp;rc_mem,-rc_mem.left,-rc_mem.top);
    
    //memory device context
    HDC memdc=CreateCompatibleDC(hdc);
    HBITMAP bmp=CreateCompatibleBitmap(hdc,rc_mem.right,rc_mem.bottom);
    HBITMAP pre_bmp=(HBITMAP)SelectObject(memdc,bmp);
    
    //reflesh the background
    COLORREF color_trans=RGB(0,0,1);
    HBRUSH brush=CreateSolidBrush(color_trans);
    FillRect(memdc,amp;rc_mem,brush);
    
    //border
    if((styleamp;WS_BORDER)==WS_BORDER) {
        RECT rc_tmp;
        CopyRect(amp;rc_tmp,amp;rc_mem);
        for(int index=0;index<ls->margin_border;index  ) {
            FrameRect(memdc,amp;rc_tmp,ls->brush_border);
            InflateRect(amp;rc_tmp,-1,-1);
        }
    }
    
    //Blt
    TransparentBlt(hdc,0,0,(rc.right-rc.left),(rc.bottom-rc.top),
                   memdc,0,0,rc_mem.right,rc_mem.bottom,color_trans);
    
    DeleteObject(brush);
    DeleteObject(SelectObject(memdc,pre_bmp));
    DeleteDC(memdc);
    ReleaseDC(hwnd,hdc);
    return 0;
}
 
 int ListBox_DrawScrollBar(HWND hwnd)
{
    pLBStyle ls=ListBox_GetSettings(hwnd);
    if(!ls) return -1;
    
    RECT rc_mem,rc_vs,rc_thumb;
    
    if(ListBox_GetZoneRect(hwnd,ZVSCROLL,amp;rc_vs,TRUE)!=0) return -1;
    CopyRect(amp;rc_mem,amp;rc_vs);
    OffsetRect(amp;rc_mem,-rc_vs.left,-rc_vs.top);

    //initial gdi resource
    HDC hdc=GetWindowDC(hwnd);
    HDC memdc=CreateCompatibleDC(hdc);
    HBITMAP bmp=CreateCompatibleBitmap(hdc,rc_mem.right-rc_mem.left,rc_mem.bottom-rc_mem.top);
    HBITMAP pre_bmp=(HBITMAP)SelectObject(memdc,bmp);
    
    //background of the scrollbar
    FillRect(memdc,amp;rc_mem,ls->brush);
    
    if(ListBox_GetZoneRect(hwnd,ZVSTHUMB,amp;rc_thumb,TRUE)!=0) return -1;
    OffsetRect(amp;rc_thumb,-rc_vs.left,-rc_vs.top);

    //thumb rect
    Graphics graphic(memdc);
    graphic.SetSmoothingMode(SmoothingModeHighQuality);
    GraphicsPath path;
    LinearGradientBrush pbrush(Rect(rc_thumb.left,rc_thumb.top,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS),
                              Color(255,10,10,10),Color(255,90,90,90),LinearGradientModeHorizontal);
    path.AddArc(rc_thumb.left,rc_thumb.top,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,180,180);
    path.AddArc(rc_thumb.left,rc_thumb.bottom-SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,SCROLLBAR_PIXLS,0,180);
    graphic.FillPath(amp;pbrush,amp;path);
    
    //blt
    BitBlt(hdc,rc_vs.left,rc_vs.top,rc_vs.right-rc_vs.left,rc_vs.bottom-rc_vs.top,
           memdc,0,0,SRCCOPY);        
    
    //release gdi resource
    DeleteObject(SelectObject(memdc,pre_bmp));
    DeleteDC(memdc);
    ReleaseDC(hwnd,hdc);
    return 0;
}
 
  1. WM_NCHITTEST : Recognize the scrollbar zone hit.
  2. WM_NCLBUTTONDOWN : Recognize if the hit on the scrollbar zone, if is, determining it is a dragging action of thumb rect. Howerver, acccording some source code , people don’t do this in WM_NCLBUTTONDOWN procedure, they usually do this in WM_SETCURSOR . I don’t know why.
  3. WM_SETCURSOR : If NC_HitTest was in scrollbar rect, and the WM_SETCURSOR was triggered by WM_NCLUBTTONDOWN, that means user trying to scroll the control, now user doing dragging actions.
     case WM_SETCURSOR: {
        UINT hit_pos=LOWORD(lParam);
        UINT trigger_msg=HIWORD(lParam);
        
        if(hit_pos!=HTVSCROLL) return CallWindowProc(ls->pre_proc,hwnd,msg,wParam,lParam);
        
        if(trigger_msg==WM_LBUTTONDOWN||trigger_msg==WM_NCLBUTTONDOWN) {
            POINT pt={0};
            RECT rc;
            
            GetCursorPos(amp;pt);
            //ScreenToClient(hwnd,amp;pt);
            ListBox_GetZoneRect(hwnd,ZVSTHUMB,amp;rc,FALSE);
            
            if(PtInRect(amp;rc,pt)) {
                SetCapture(hwnd);
                ls->scroll_state=THUMB_VCLICK;
            }
        }
    } break;
 

WHAT IS THE TROUBLE

  1. WM_MOUSEMOVE : Update the scrollbar info , repaint the scrollbar according the scrollinfo updated.
 case WM_MOUSEMOVE: {
    if(GetCapture()!=hwnd) break;
    if(ls->scroll_state!=THUMB_VCLICK) break;
    
    //...

} break;
 

However, I can’t get any WM_MOUSEMOVE message when dragging the thumb rect. Why ?

THE REST PART

Those messages down below are irrelevant for the problem yet. But I will still write the purpose of each.

  1. WM_NCLBUTTONUP / WM_LBUTTONUP : Release the capture of the control, and it is a signal for stopping thumb dragging action.
  2. WM_PAINT : Repaint the scrollbar.

PS: Я понял это и записываю здесь.

Почему не удается получить сообщение WM_MOUSEMOVE при вызове SetCapture для элемента управления списком? Есть очень небольшое изменение для моего кода.

Проблема заключается в дескрипторе WM_SETCURSOR или WM_NCLBUTTONDOWN.

     case WM_SETCURSOR: {
        UINT hit_pos=LOWORD(lParam);
        UINT trigger_msg=HIWORD(lParam);

        if(hit_pos!=HTVSCROLL) return CallWindowProc(ls->pre_proc,hwnd,msg,wParam,lParam);

        if(trigger_msg==WM_LBUTTONDOWN||trigger_msg==WM_NCLBUTTONDOWN) {
            POINT pt={0};
            RECT rc;

            GetCursorPos(amp;pt);
            //ScreenToClient(hwnd,amp;pt);
            ListBox_GetZoneRect(hwnd,ZVSTHUMB,amp;rc,FALSE);

            if(PtInRect(amp;rc,pt)) {
                SetCapture(hwnd);
                ls->scroll_state=THUMB_VCLICK;

                return 0;// here it is.
            }
        }
    } break;

 

Когда вы SetCapture(hwnd) , вы должны вернуть 0 и завершить обработку сообщения, если break; здесь , процедура контроля по умолчанию возьмет верх и разрушит все, что вы сделали.

Каждый раз , когда я чувствую себя в ловушке, это всегда оказывается чем-то слишком крошечным, чтобы меня это волновало. Хм, каждый раз.

Комментарии:

1. Не меняйте состояние внутри WM_SETCURSOR . Просто установите курсор. Изменится ли состояние WM_NCLBUTTONDOWN .

2. @Raymond, Ты прав. Это работает только внутри WM_NCLBUTTONDOWN . Я не знаю, почему. Кроме того, эти трюки, похоже, НЕ работают с управлением WC_EDIT. Если я применил WS_VSCROLL WC_EDIT элемент управления, начальный графический интерфейс по умолчанию имеет полосу прокрутки, даже если в поле редактирования нет содержимого. Это отличается от WC_LISTBOX контроля. Это почему?