Can you make a window scrollable only via the scroll bar?

So I'm working on this project in which I need to use synchronized lists. I've seen a lot of complicated ways to do this, but I'm still a bit new at windows programming so instead I went for subclassing my list and intercepting all the WM_VSCROLL messages.. like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LRESULT CALLBACK ListProc3(HWND, UINT, WPARAM, LPARAM);
WNDPROC OriginalListProc3;

LRESULT CALLBACK ListProc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   if (message == WM_VSCROLL)
   {
        HWND hCuc=FindWindow(NULL,LPCSTR("Main Dialog Window"));
        HWND hListw[3];
        hListw[0]=GetDlgItem(hCuc,IDL_L1);
        hListw[1]=GetDlgItem(hCuc,IDL_L2);
        hListw[2]=GetDlgItem(hCuc,IDL_L3);
        for(int i=0;i<=2;i++)
                SendMessage(hListw[i],WM_VSCROLL,wParam,lParam);
   }
   return CallWindowProc(OriginalListProc3, hwnd, message, wParam, lParam);
}


So now every time my main list (a multiple selection list with a scrollbar) receives the WM_VSCROLL message it forwards the message to 3 other lists (all with LBS_NOSEL and without a scrollbar). OK, so far so good and up until now I've been very satisfied with the result... until I found out the hard way that you can scroll a list without sending the WM_VSCROLL message.. for instance you select an item and you press the up/down/pgUp/pgDown keys or you hold click and drag your mouse to the bottom/top of the list and whatever other methods I don't know about.

Is there any way to prevent this?.. to make my listbox scrollable only via the scrollbar?.. or atleastis there some way I can work around it?

Thanks in advance!
What you are asking is very easy to do if you approach scrolling in the correct way, and subclassing some other control like you are doing likely isn't the best way. To be perfectly honest, I would never even thought of taking that approach. If you do it the right way, adding keyboard support actually takes more work. First cut through, only the scroll bars work, because the only messages scroll bars send to their parent are scroll messages such as SB_LINEUP, SB_LINEDOWN, SB_PAGEUP, SB_PAGEDOWN, etc. They don't send any keyboard messages. If you want scrolling to occur when the user presses the cursor motion keys or something like that, you need to intercept those messages, for example through a WM_KEYDOWN, and then call whatever scroll code you want to occur through that keypress. So like I said, if you were going about it the right way, you would already have it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
//Main.cpp
// Tested With GCC 4.8 Series MinGW-w64 64/32-bit Edition
// Tested with GCC 4.4.1 32 bit
#ifndef UNICODE
    #define UNICODE
#endif
#ifndef _UNICODE
    #define _UNICODE
#endif
#include  <windows.h>
#include  <stdio.h>
#include  <string.h>
#include  "WinTypes.h"


long fnWndProc_OnCreate(lpWndEventArgs Wea)     //Offset     What's Stored In WNDCLASSEX::cbWndExtra Bytes
{                                               //=====================================================
 unsigned int iLineCount=0,i=0;                 //0  -  7    iLineCount
 wchar_t szBuffer[352];                         //8  -  15   ptrPtrBuffer
 wchar_t** ptrPtrBuffer=NULL;                   //16 -  23   cyChar
 HFONT hFont,hTmp;
 HANDLE hHeap=NULL;
 FILE* fp1=NULL;
 TEXTMETRIC tm;
 wchar_t* p=NULL;
 HDC hDC;

 Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
 fp1=_wfopen(L"Main.cpp", L"r");
 if(fp1)
 {
    do   // First, read through Main.cpp to see how many lines
    {
     p=fgetws(szBuffer,352,fp1);
     if(!p)
        break;
     else
        iLineCount++;
    }while(1);
    rewind(fp1);
    if(iLineCount)
    {
       SetWindowLongPtr(Wea->hWnd,0*sizeof(void*),iLineCount); // set linecount at offset 0
       hHeap=GetProcessHeap();
       ptrPtrBuffer = (wchar_t**)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(wchar_t*) * iLineCount); // a buffer to hold string
       if(ptrPtrBuffer)                                                                             // pointers
       {
          SetWindowLongPtr(Wea->hWnd, 1*sizeof(void*), (LONG_PTR)ptrPtrBuffer);                     // store that at offset 1
          for(i=0; i<iLineCount; i++)
          {
              fgetws(szBuffer,352,fp1);   // read line into szBuffer, calculate length, allocate memory, then move string to there
              ptrPtrBuffer[i]=(wchar_t*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(wchar_t*) * (wcslen(szBuffer)+sizeof(wchar_t)));
              wcscpy(ptrPtrBuffer[i],szBuffer); // move szBuffer line to allocated memory
          }
       }
       hDC=GetDC(Wea->hWnd);  // calculate character height
       hFont=CreateFont(18,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,L"Courier New");
       hTmp=(HFONT)SelectObject(hDC,hFont);
       GetTextMetrics(hDC,&tm);
       SetWindowLongPtr(Wea->hWnd,2*sizeof(void*),(LONG_PTR)tm.tmHeight);
       DeleteObject(SelectObject(hDC,hTmp));
       ReleaseDC(Wea->hWnd,hDC);
    }
    else
    {
       fclose(fp1);
       return -1;    //will cause CreateWindow() down in WinMain() to fail.
    }
    fclose(fp1);
 }
 else
    return -1;  //will cause CreateWindow() down in WinMain() to fail.

 return 0;
}



long fnWndProc_OnSize(lpWndEventArgs Wea)
{
 int iLinesVisible,iLineCount;
 unsigned int cyChar;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 iLineCount=(unsigned int)GetWindowLongPtr(Wea->hWnd,0);
 cyChar=(unsigned int)GetWindowLongPtr(Wea->hWnd,2*sizeof(void*));
 iLinesVisible=HIWORD(Wea->lParam)/cyChar;
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask =   SIF_RANGE|SIF_PAGE;
 si.nMin = 0;
 si.nMax = iLineCount-1;
 si.nPage=iLinesVisible;
 if(si.nMax<0)
    si.nMax=0;
 SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
 if(iLinesVisible<=iLineCount)
    InvalidateRect(Wea->hWnd,NULL,TRUE);

 return 0;
}



long fnWndProc_OnVScroll(lpWndEventArgs Wea)
{
 int iVScrollPos;
 SCROLLINFO si;

 ZeroMemory(&si, sizeof(SCROLLINFO));
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask=SIF_ALL;
 GetScrollInfo(Wea->hWnd,SB_VERT,&si);
 iVScrollPos=si.nPos;
 switch(LOWORD(Wea->wParam))
 {
  case SB_LINEUP:
    if(si.nPos>si.nMin)
       si.nPos--;
    break;
  case SB_PAGEUP:
    si.nPos = si.nPos - si.nPage;
    break;
  case SB_LINEDOWN:
    if(si.nPos<si.nMax)
       si.nPos++;
    break;
  case SB_PAGEDOWN:
    si.nPos = si.nPos + si.nPage;
    break;
  case SB_THUMBTRACK:
    si.nPos=si.nTrackPos;
    break;
 }
 si.fMask = SIF_POS;                           // Set the position and then retrieve it.  Due to adjustments
 SetScrollInfo(Wea->hWnd, SB_VERT, &si, TRUE); // by Windows it may not be the same as the value set.
 GetScrollInfo (Wea->hWnd, SB_VERT, &si);
 if(si.nPos != iVScrollPos)                    // If the position has changed, scroll the window and update it
    ScrollWindow(Wea->hWnd, 0,GetWindowLongPtr(Wea->hWnd,2*sizeof(void*))*(iVScrollPos-si.nPos), NULL, NULL);

 return 0;
}



long fnWndProc_OnPaint(lpWndEventArgs Wea)
{
 unsigned int iLineCount,i,iStart,iFinish,iLine,iPos;
 wchar_t** ptrPtrBuffer=NULL;
 HFONT hFont,hTmp;
 PAINTSTRUCT ps;
 SCROLLINFO si;
 long cyChar;
 HDC hDC;

 hDC=BeginPaint(Wea->hWnd,&ps);
 iLineCount=(unsigned int)GetWindowLongPtr(Wea->hWnd,0);
 ptrPtrBuffer=(wchar_t**)GetWindowLongPtr(Wea->hWnd,1*sizeof(void*));
 cyChar=GetWindowLong(Wea->hWnd,2*sizeof(void*));
 si.cbSize = sizeof(SCROLLINFO);
 si.fMask  = SIF_POS;
 GetScrollInfo(Wea->hWnd, SB_VERT, &si);
 iPos=si.nPos;
 iStart=ps.rcPaint.top/cyChar;
 iFinish=ps.rcPaint.bottom/cyChar;
 hFont=CreateFont(18,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,L"Courier New");
 hTmp=(HFONT)SelectObject(hDC,hFont);
 SetBkMode(hDC,TRANSPARENT);
 for(i=iStart;i<=iFinish;i++)
 {
     iLine=iPos+i;
     if(iLine<iLineCount)
        TextOut(hDC,0,i*cyChar,ptrPtrBuffer[iLine],wcslen(ptrPtrBuffer[iLine])-1);
 }
 DeleteObject(SelectObject(hDC,hTmp));
 EndPaint(Wea->hWnd,&ps);

 return 0;
}



long fnWndProc_OnClose(lpWndEventArgs Wea)
{
 unsigned int iLineCount;
 wchar_t** ptrPtrBuffer=NULL;
 HANDLE hHeap=NULL;
 unsigned int i;

 iLineCount=(unsigned int)GetWindowLongPtr(Wea->hWnd,0);
 ptrPtrBuffer=(wchar_t**)GetWindowLongPtr(Wea->hWnd,1*sizeof(void*));
 hHeap=GetProcessHeap();
 for(i=0; i<iLineCount; i++)
     HeapFree(hHeap,NULL,ptrPtrBuffer[i]);
 HeapFree(hHeap,NULL,ptrPtrBuffer);
 DestroyWindow(Wea->hWnd);
 PostQuitMessage(0);

 return 0;
}



LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam, LPARAM lParam)
{
 WndEventArgs Wea;

 for(unsigned int i=0; i<dim(EventHandler); i++)
 {
     if(EventHandler[i].Code==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
 }

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



int WINAPI WinMain(HINSTANCE hIns,HINSTANCE hPrevIns,LPSTR lpszArgument,int iShow)
{
 wchar_t szClassName[]=L"ScrollWindow";
 WNDCLASSEX wc;
 MSG messages;
 HWND hWnd;

 wc.lpszClassName=szClassName;                          wc.lpfnWndProc=fnWndProc;
 wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=0;
 wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=hIns;
 wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);            wc.hCursor=LoadCursor(NULL,IDC_ARROW);
 wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=3*sizeof(void*);
 wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
 RegisterClassEx(&wc);
 hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW|WS_VSCROLL,50,50,1400,800,HWND_DESKTOP,0,hIns,0);
 ShowWindow(hWnd,iShow);
 UpdateWindow(hWnd);
 while(GetMessage(&messages,NULL,0,0))
 {
    TranslateMessage(&messages);
    DispatchMessage(&messages);
 }

 return messages.wParam;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H
#define dim(x) (sizeof(x) / sizeof(x[0]))

typedef struct           WindowsEventArguments
{
 HWND                    hWnd;
 WPARAM                  wParam;
 LPARAM                  lParam;
 HINSTANCE               hIns;
}WndEventArgs,           *lpWndEventArgs;

long fnWndProc_OnCreate  (lpWndEventArgs Wea);
long fnWndProc_OnSize    (lpWndEventArgs Wea);
long fnWndProc_OnVScroll (lpWndEventArgs Wea);
long fnWndProc_OnPaint   (lpWndEventArgs Wea);
long fnWndProc_OnClose   (lpWndEventArgs Wea);

struct EVENTHANDLER
{
 unsigned int            Code;
 long                    (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
 {WM_CREATE,             fnWndProc_OnCreate},
 {WM_PAINT,              fnWndProc_OnPaint},
 {WM_SIZE,               fnWndProc_OnSize},
 {WM_VSCROLL,            fnWndProc_OnVScroll},
 {WM_CLOSE,              fnWndProc_OnClose}
};
#endif 
You need to name the op file "Main.cpp" and the header "WinTypes.h". The project reads in Main.cpp and displays it in a window that scrolls. As you can see, none of the keyboard navigation keys cause any scrolling - only use of the scroll bar does that. Like I said, this way, if you want to have keyboard navigation, you would need to intercept WM_KEYDOWNS, and call the scroll code to make it happen. Hope this helps.
Thanks for the responses :).. my problem isn't really with the keyboard scrolling because, as someone has already said, I can intercept the WM_KEYDOWNS etc.. messages.. it's a little more tricky with mouse scrolling though.. i.e. if you hold down left click and move the mouse to the bottom of the list it will actually be scrolled, even if it's a no selection list..

I'll have to take some time to look through the code in the snippets as I haven't done that yet.. but I just wanted to thank you for responding
Topic archived. No new replies allowed.