Scrolling Windows Controls Instead of Text
The past two tutorials showed how to scroll text on a Form/Dialog. But what if you have a small dialog or form that for whatever reason can’t be made larger, and you need to put quite a few controls on it – and they don’t all fit? Controls such as textboxes, combo boxes, or anything else for that matter can also be scrolled about within a larger ‘container’. However, the technique is a bit different than with scrolling text. What works best is to create a ‘pane’, that is, an intermediate window that is itself a child of the main window or dialog, and upon this ‘pane’ place the subordinate child window controls such as text boxes or whatever. Then this pane is moved about with Windows Api calls so as to bring hidden or ‘clipped’ parts of the pane into view. This technique can be understood by noting the variable types of the parameters to the CreateWindowEx() and MoveWindow() calls relating to the size and location of the window. Here is the description of CreateWindowEx()…
Code:
HWND CreateWindowEx ( DWORD dwExStyle, // extended window style LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name DWORD dwStyle, // window style int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // handle to menu, or child-window identifier HINSTANCE hInstance, // handle to application instance LPVOID lpParam // pointer to window-creation data );
Scrolling controls is made possible by the MoveWindow() call however, and here is its description…
Code:
BOOL MoveWindow ( HWND hWnd, // handle to window int X, // horizontal position int Y, // vertical position int nWidth, // width int nHeight, // height BOOL bRepaint // repaint flag );
Code:
#Compile Exe #Include "Win32api.inc" %ID__PANE = 1500 'Equate numbers as proxies %IDC_FIRST_NAME = 1505 'for hWnds %IDC_MIDDLE_NAME = 1510 %IDC_LAST_NAME = 1515 %IDC_ADDRESS1 = 1520 %IDC_ADDRESS2 = 1525 %IDC_CITY = 1530 %IDC_STATE = 1535 %IDC_COUNTRY = 1540 %IDC_ZIP = 1545 %IDC_EMAIL = 1550 %IDC_TELEPHONE = 1555 %IDC_INTERESTS = 1560 %IDC_SUBMIT = 1565 Type WndEventArgs 'Just collecting Window Procedure wParam As Long 'parameters into a type lParam As Long hWnd As Dword hInst As Dword End Type Type MessageHandler 'Type to associate a Window's Message with the run time address wMessage As Long 'of the function that handles the message. There are only three dwFnPtr As Dword 'messages for the main window that this program handles, i.e., End Type 'WM_CREATE, WM_VSCROLL, and WM_CLOSE. Note that there are no calls Global MsgHdlr() As MessageHandler 'in this program to fnPtr. This is just a model procedure for Declare Function fnPtr(Wea As WndEventArgs) As Long 'PowerBASIC's use in setting up the Call Dword mechanism. Function fnPaneProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long If wMsg=%WM_COMMAND And Lowrd(wParam)=%IDC_SUBMIT Then MsgBox("You Apparently Want To Submit The Information") 'This is the Window Procedure for the 'Pane' End If 'window that contains labels, textboxes, and 'the button. It is this window which 'scrolls'. fnPaneProc=DefWindowProc(hWnd,wMsg,wParam,lParam) End Function Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long Local pCreateStruct As CREATESTRUCT Ptr Local hCtrl,hPane As Dword Local szPane As Asciiz*8 Local vsi As SCROLLINFO Local wc As WndClassEx Local iMsg As Long Local rc As RECT pCreateStruct=Wea.lParam 'When a WM_CREATE message is received, the lParam is a pointer to a [email protected] 'CREATESTRUCT which contains all the parameters of the CreateWindow() 'call that created the window (so you don't need global variables to 'Set up 'Pane' class 'maintain the state of any CreateWindow() parameters here). szPane="Pane" wc.cbSize=SizeOf(WNDCLASSEX) 'Note that an instance of this class will be created just below to wc.style=%CS_HREDRAW Or %CS_VREDRAW 'serve as a container for the labels, textboxes, and button. It is wc.lpfnWndProc=CodePtr(fnPaneProc) 'this 'Pane' object which actually scrolls. Further note that the wc.cbClsExtra=0 'pane is a child of the main program window, and the labels, textboxes wc.cbWndExtra=0 'and button are childs of the pane window. If you examine the wc.hInstance=Wea.hInst 'CreateWindow() call just below, you'll see that the height (y) of the wc.hIcon=0 'pane was set to 490 pixels. If you look down in WinMain() where the wc.hCursor=LoadCursor(%NULL,ByVal %IDC_ARROW) 'CreateWindow() call is located that creates the main program window, wc.hbrBackground=%COLOR_BTNFACE+1 'you'll see that the main program window is only 300 pixels. Therefore, wc.lpszMenuName=%NULL 'the pane is 190 pixels larger than the main program window, and is large wc.lpszClassName=VarPtr(szPane) 'enough to contain all the child window controls. The child window controls wc.hIconSm=0 'that are brought into view by scrolling are the result of MoveWindow() Call RegisterClassEx(wc) 'calls in the Message Handler for WM_VSCROLL. hPane=CreateWindowEx(0,szPane,"",%WS_CHILD Or %WS_VISIBLE ,0,0,325,490,Wea.hWnd,%ID__PANE,Wea.hInst,ByVal 0) Call SetWindowLong(Wea.hWnd,0,hPane) 'Store hPane in cbWndExtra bytes because we'll need it in fnWndProc_OnVScroll() 'Create all the child window controls on the 'Pane' hCtrl=CreateWindowEx(0,"static","First Name",%WS_CHILD Or %WS_VISIBLE,10,10,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,10,150,25,hPane,%IDC_FIRST_NAME,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Middle Name",%WS_CHILD Or %WS_VISIBLE,10,40,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,40,150,25,hPane,%IDC_MIDDLE_NAME,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Last Name",%WS_CHILD Or %WS_VISIBLE,10,70,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,70,150,25,hPane,%IDC_LAST_NAME,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Address1",%WS_CHILD Or %WS_VISIBLE,10,100,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,100,150,25,hPane,%IDC_ADDRESS1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Address2",%WS_CHILD Or %WS_VISIBLE,10,130,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,130,150,25,hPane,%IDC_ADDRESS2,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","City",%WS_CHILD Or %WS_VISIBLE,10,160,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,160,150,25,hPane,%IDC_CITY,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","State",%WS_CHILD Or %WS_VISIBLE,10,190,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,190,150,25,hPane,%IDC_STATE,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Country",%WS_CHILD Or %WS_VISIBLE,10,220,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,220,150,25,hPane,%IDC_COUNTRY,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Zip Code",%WS_CHILD Or %WS_VISIBLE,10,250,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,250,150,25,hPane,%IDC_ZIP,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Email",%WS_CHILD Or %WS_VISIBLE,10,280,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,280,150,25,hPane,%IDC_EMAIL,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Telephone",%WS_CHILD Or %WS_VISIBLE,10,310,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER,140,310,150,25,hPane,%IDC_TELEPHONE,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"static","Interests",%WS_CHILD Or %WS_VISIBLE,10,340,100,25,hPane,-1,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(%WS_EX_CLIENTEDGE,"edit","",%WS_CHILD Or %WS_VISIBLE Or %WS_BORDER Or %ES_MULTILINE,140,340,150,100,hPane,%IDC_TELEPHONE,Wea.hInst,Byval %NULL) hCtrl=CreateWindowEx(0,"button","Submit",%WS_CHILD Or %WS_VISIBLE,100,455,100,25,hPane,%IDC_SUBMIT,Wea.hInst,Byval %NULL) 'Initialize Window's internal scrolling apparatus (vsi think verticle scroll info) Call GetClientRect(Wea.hWnd,rc) 'Need size of main window's client area to determine .nMax vsi.cbSize=Sizeof(SCROLLINFO) 'Api docs say to do this vsi.nMin=0 vsi.nMax=490-rc.nBottom vsi.nPos=0 vsi.fMask=%SIF_POS Or %SIF_RANGE Call SetScrollInfo(Wea.hWnd,%SB_VERT,vsi,%TRUE) fnWndProc_OnCreate=0 End Function Function fnWndProc_OnVScroll(Wea As WndEventArgs) As Long Local vsi As SCROLLINFO Local hPane As Dword hPane=GetWindowLong(Wea.hWnd,0) Select Case As Long Lowrd(Wea.wParam) Case %SB_LINEUP vsi.cbSize=Sizeof(SCROLLINFO) vsi.fMask=%SIF_POS Or %SIF_RANGE Call GetScrollInfo(Wea.hWnd,%SB_VERT,vsi) If vsi.nPos>0 Then vsi.nPos=vsi.nPos-10 If vsi.nPos<0 Then vsi.nPos=0 End If Call MoveWindow(hPane,0,-1*vsi.nPos,325,500,%TRUE) vsi.fMask=%SIF_POS Call SetScrollInfo(Wea.hWnd,%SB_VERT,vsi,%TRUE) End If Case %SB_LINEDOWN vsi.cbSize=Sizeof(SCROLLINFO) vsi.fMask=%SIF_POS Or %SIF_RANGE Call GetScrollInfo(Wea.hWnd,%SB_VERT,vsi) If vsi.nPos<vsi.nMax Then vsi.nPos=vsi.nPos+10 Call MoveWindow(hPane,0,-1*vsi.nPos,325,500,%TRUE) vsi.fMask=%SIF_POS Call SetScrollInfo(Wea.hWnd,%SB_VERT,vsi,%TRUE) End If End Select fnWndProc_OnVScroll=0 End Function Function fnWndProc_OnClose(wea As WndEventArgs) As Long Call PostQuitMessage(0) fnWndProc_OnClose=0 End Function Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long Register i As Long For i=0 To 2 If wMsg=MsgHdlr(i).wMessage Then Local wea As WndEventArgs Local iReturn As Long wea.hWnd=hWnd: wea.wParam=wParam: wea.lParam=lParam Call Dword MsgHdlr(i).dwFnPtr Using fnPtr(wea) To iReturn fnWndProc=iReturn Exit Function End If Next i fnWndProc=DefWindowProc(hWnd,wMsg,wParam,lParam) End Function Sub AttachMessageHandlers() ReDim MsgHdlr(2) As MessageHandler 'Associate Windows Message With Message Handlers MsgHdlr(0).wMessage=%WM_CREATE : MsgHdlr(0).dwFnPtr=CodePtr(fnWndProc_OnCreate) MsgHdlr(1).wMessage=%WM_VSCROLL : MsgHdlr(1).dwFnPtr=CodePtr(fnWndProc_OnVScroll) MsgHdlr(2).wMessage=%WM_CLOSE : MsgHdlr(2).dwFnPtr=CodePtr(fnWndProc_OnClose) End Sub Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long Local hWnd As Dword, dwStyle As Dword Local szAppName As Asciiz * 16 Local winclass As WndClassEx Local Msg As tagMsg szAppName="ScrollControls" Call AttachMessageHandlers() winclass.cbSize=SizeOf(winclass) winclass.style=%CS_HREDRAW Or %CS_VREDRAW winclass.lpfnWndProc=CodePtr(fnWndProc) winclass.cbClsExtra=0 winclass.cbWndExtra=8 winclass.hInstance=hIns winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION) winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW) winclass.hbrBackground=GetStockObject(%WHITE_BRUSH) winclass.lpszMenuName=%NULL winclass.lpszClassName=VarPtr(szAppName) winclass.hIconSm=LoadIcon(hIns, ByVal %IDI_APPLICATION) RegisterClassEx winclass dwStyle=%WS_CAPTION Or %WS_MINIMIZEBOX Or %WS_VISIBLE Or %WS_VSCROLL Or %WS_SYSMENU hWnd=CreateWindowEx(0,szAppName,szAppName,dwStyle,200,100,325,300,0,0,hIns,ByVal 0) Call ShowWindow(hWnd,iShow) While GetMessage(Msg,%NULL,0,0) TranslateMessage Msg DispatchMessage Msg Wend Function=msg.wParam End Function
Leave a comment: