https://www.codeproject.com/Articles/1293/Control-Subclassing
هنوز تست نشده ولی نحوه ی کار به این شکل است که اندازه ی ناحیه Client رو عوض میکنند تا دو مستطیل با حالت باتن در پائین و بالای آن بکشند ، بعنوان Scroll Up/Down و NCHITTEST هم زمانی که ماوس روی آن قسمت ها قرار می گیرد یا موقعیت عوض میشود عدد ثابتی رو بر میگردونه و طبق همون و NCLBUTTONDOWN تابعی رو صدا میزنن که حالت PUSH بگیره وقتی فشرده شه یا به حالت اولش برگرده .
در NCCALCSIZE و WPARAM=1 اندازه ی دوباره داده میشود البته SWP_FRAMECHANGED نباید فراموش شود بعد از ساب کلاس کردن فرضا
Private listboxProc As LongPtr
listboxProc=0
HookWindow
در HOOKPROC زمانیکه پیام HCBT_CREATEWND دریافت میشود برای هنگ نکردن یا عدم Crash باید اگر listbox=0 و wparam برابر با کلاس پنجره با تابع GetClassName و نام پنجره ( منظور کپشن آن ) همان نام پنجره (قلاب شده) شد منظور با GETWINDOWTEXT به تابع WndProc ریفر داده شود جهت تسخیر پیام های ارسالی
WndProc
Select Case Msg
Case WM_ENABLE ' WM_SHOWWINDOW
listboxproc=SetWindowLongPtrA(hlist,GWL_WNDPROC,AddressOf
(fnlist
SetWindowpos 0,0,0,0,0,0,SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOZORDER Or SWP_FRAMECHANGED
WM_DESTROY
SetWindowLongPtrA hwnd,GWL_WNDOROC,listboxProc
End Function
تابعی برای Capture کردن یا تسخیر پیام های دریافتی ( لیست باکس )
Function fnlist(ByVal hwnd As LongPtr,ByVal Msg As Long,ByVal wParam As LongPtr,ByVal lParam As LongPtr) As LongPtr
Dim nccsp As NCCALCSIZE_PARAMS
Select Case Msg
Case WM_NCCALCSIZE
CallWindProcA listboxProc,hwnd,Msg,wParam,lParam
Use CopyMemory'
Case WM_NCPAINT
End Select
(fnlist=CallWindProcA(listboxProc,hwnd,Msg,wParam,lParam
End Function
در NCPAINT کشیدن Scroll انجام میشود
SetBkColor(COLORREF crBkColor,COLORREF
(crSelectedColor
{
Deletes previous brush. Must do in order to create a'
new one '
DeleteObject m_BkBrush
Sets the brush the specified background color'
m_BkBrush=CreateSolidBrush(crBkColor)
Invalidate 'Forces Redraw
()Function DrawBorders
GetClientRect hlist,Crect
InflateRect Crect,GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)
'Since we'll be using m_bOver, let's 'initialize it. Add m_bOver = 'FALSE; on PreSubclassWindow.
if (m_bOver) Then
DrawEdge hdc,Crect,EDGE_BUMP,BF_RECT
Else
DrawEdge hdc,Crect,EDGE_SUNKEN,BF_RECT
End If
ReleaseDC hdc
End Function
WM_MOUSLEAVE
m_bOver=FALSE
()DrawBorders
if (!m_bOver)
m_bOver=TRUE 'Now the mouse is over DrawBorders() 'Self explanatory //Add TRACKMOUSEEVENT track 'Declares structure
(track.cbSize=sizeof(track
track.dwFlags=TME_LEAVE 'Notify us when the mouse leaves
track.hwndTrack=m_hWnd 'Assigns this window's hwnd
TrackMouseEvent &track
MouseMove
'If m_bOver==FALSE,and this function is 'called, it means that the mouse entered.
if (!m_bOver) Then
m_bOver=TRUE 'Now the mouse is over
DrawBorders() ' Self explanatory
We then set them to an initial value under PreSubclassWindow:
m_bOver = FALSE;
m_ItemHeight=18; m_crTextHlt=GetSysColor(COLOR_HIGHLIGHTTEXT);
m_crTextClr=GetSysColor(COLOR_WINDOWTEXT);
m_HBkColor=GetSysColor(COLOR_HIGHLIGHT);
m_BmpWidth=16;
m_BmpHeight=16;
: MeasureItem
lpMeasureItemStruct->itemHeight=m_ItemHeight;
4. Scrollbars
For simplicity purposes. the scrollbars that we are going to make are going to be static, always shown regardless of whether they are needed. I don't think we are using the correct term since they don't have bars but who cares. As we all know, we must draw them. However, the problem is how to do it so that it is within the listbox rect and does not cover any item. There's a simple solution, we can resize the client area. This can be done by receiving the message WM_NCCALCSIZE
. Add a function for it, and we get:
lpncsp->rgrc[0].top += 16; //Top
lpncsp->rgrc[0].bottom -= 16; //Bottom
WM_NCPAINT
static BOOL before=FALSE
if (!before) Then
SetWindowPos(NULL,0,0,0,0,SWP_FRAMECHANGED Or SWP_NOMOVE Or SWP_NOSIZE before=TRUE
()DrawBorders
(DrawScrolls(UINT WhichOne, UINT State
()pDC=GetDC
CRect rect
GetClientRect hwnd,Crect
if (IsWindowEnabled())State=SC_DISABLED; //Expands the so that it does not draw over the borders
Crect.left=Crect.left-GetSystemMetrics(SM_CYEDGE) Crect.right= Crect.right+GetSystemMetrics(SM_CXEDGE)
(if (WhichOne==SC_UP
rect.bottom=rect.top-GetSystemMetrics(SM_CXEDGE) rect.top=rect.top-16-GetSystemMetrics(SM_CXEDGE)
Draws the scroll up '
DrawFrameControl pDC,Crect,DFC_SCROLL,State Or DFCS_SCROLLUP)
else
Needs to draw down
rect.top=rect.bottom+GetSystemMetrics(SM_CXEDGE) rect.bottom=rect.bottom+16+GetSystemMetrics(SM_CXEDGE); DrawFrameControl pDC,Crect,DFC_SCROLL,State Or DFCS_SCROLLDOWN
ReleaseDC pDC
pubic const SC_UP=2
public const SC_DOWN=3
WM_Enable
'SC_NORMAL will be changed to 'SC_DISABLED if the window is disabled DrawScrolls(SC_UP,SC_NORMAL) DrawScrolls(SC_DOWN,SC_NORMAL);
(OnNcLButtonDown(UINT nHitTest, CPoint point
if (nHitTest=HTVSCROLL) 'Up scroll Pressed DrawScrolls(SC_UP,SC_PRESSED) 'Scroll up 1 line SendMessage(WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0)
SetTimer(1,100,NULL)'Sets the timer ID 1
else if (nHitTest==HTHSCROLL)'Down scroll Pressed DrawScrolls(SC_DOWN,SC_PRESSED) ' Scroll down 1 line SendMessage(WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0) SetTimer(2,100,NULL) 'Sets the timer ID 2
(OnTimer(UINT nIDEvent
(result=GetKeyState(VK_LBUTTON
if (nIDEvent==1) ' Up timer If it returns negative then it is pressed
(if (result<0
SendMessage(WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),0)
else ' No longer pressed
(KillTimer(1
( DrawScrolls(SC_UP,SC_NORMAL
else 'Down timer
'If it returns negative then it is pressed
(if (result<0
SendMessage(WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),0)
else
(KillTimer(2
(DrawScrolls(SC_DOWN,SC_NORMAL
(OnNcHitTest(CPoint point
CRect rect,top,bottom
GetWindowRect hwnd,Crect
ScreenToClient hwnd,Crect
top=bottom=rect
top.bottom=rect.top+16
bottom.top=rect.bottom-16
Obtains where the mouse is '
UINT
(where=CListBox::OnNcHitTest(point
Converts the point so its relative to the client area'
ScreenToClient hwnd,&point
if (where == HTNOWHERE) 'If mouse is not in a place it recognizes
if (PtInRect(top,point)) 'Check to see if the mouse is on the top
where=HTVSCROLL
else if (PtInRect(bottom,point)) 'Check to see if its on the bottom
where=HTHSCROLL
return where ' Returns where it is
WM_NCLBUTTONDOWN 0x00A1
Parameters
wParam
The hit-test value returned by the DefWindowProc function as a result of processing the WM_NCHITTEST message. For a list of hit-test values, see WM_NCHITTEST.
lParam
A POINTS structure that contains the x- and y-coordinates of the cursor. The coordinates are relative to the upper-left corner of the screen.
case WM_NCCALCSIZE
ncParams=(LPNCCALCSIZE_PARAMS) lParam
ncParams.rgrc(0).top=ncParams.rgrc(0).top4
ncParams.rgrc(0).left=ncParams.rgrc(0).left+4
ncParams.rgrc(0).bottom=ncParams.rgrc(0).bottom-4
ncParams.rgrc(0).right=ncParams.rgrc(0).right-4
Function=0
case WM_NCPAINT
Crect As RECT
GetWindowRect hWnd, Crect
(hdc=GetDC(hwnd
((hpen=CreatePen(PS_INSIDEFRAME,4, RGB(255, 0, 0
(holdobj=SelectObject(dc,hpen
width=Crect.right-Crect.left
height=Crect.bottom-Crect.top
Rectangle hdc,0,0,width,height SelectObject hdc,holdobj
ReleaseDC hWnd, dc
DeleteObject hpen
Function=0
case WM_NCACTIVATE
RedrawWindow hWnd,0,0, RDW_UPDATENOW???
Function=0
lpncsp->rgrc[0].right -= 100