Hi again Jermy,
As promised, here is my example of adding free-standing scrollbar controls to a designer form. I wanted it to be general enough so I could see in the designer where the scrollbars would be, and I could add as many as I want without duplicating effort, horizontal or vertical. There is an attached picture of the designed form. The two frames, HFrame and VFrame, mark the locations where the scrollbars will be. The form needs to declare the Load and AllEvents events.
First, you include the following file in your project. It defines a class called a Scroller that handles the mechanics of the scrollbars. There should be enough comments to explain it.
/' ScrollBar control
The scrollbar control is an adaptation of the scrollbars used to scroll windows that are too
small for their contents. In the case of the control, it is simply a source of numbers that
looks like a volume control. It consists of a vertical or horizontal rectangle with arrows at
either end and a "thumb", which is a small rectangle between the arrows that can be dragged with
the mouse. When the arrows are clicked, the value of the control goes up or down by one "line",
which I have defaulted to 1 unit. When the space between the thumb and an arrow is clicked, the
value of the control is changed by one "page", which I default to one-tenth of the range of the
control. When the thumb is dragged with the mouse, the value of the control is set to the
proportional spot in the range conrresponding to the location of the thumb.
Since WinFBE does not (currently) provide a scroller, this class will create one on
a WinFBE form and give properties and methods to use it.
Usage example:
1. main .BAS file
#include once "scroller.inc" 'That's this file. Insert any necessary path information.
2. Design time:
Put a frame on the form to mark the location and size of the scroller.
If you make the frame higher than wide, the scroller will operate vertically.
Because of a Windows quirk, vertical scrollers are a little bit different
from horizontal ones.
3. Form file, module level:
Dim Shared as scroller scroll1
4. Form Load event:
scroll1.Initialize(frmmain, frmmain.frame1) 'Creates the scroller as a child of frmmain
'in the location and size of frame1
scroll1.SetRange(lowval, highval) 'default values 0 to 100
scroll1.SetVal(startval) 'starting value of the scroller
5. When you need the value of the scroller:
number = scroll1.GetVal()
To set the value of the scroller from the program
scroll1.SetVal(newvalue)
Change the range of the scroller (the page value will be set to one-tenth of the range)
scroll1.SetRange(newlow, newhigh) 'if you enter them in the wrong order, they will be swapped
Retrieve the current range limits:
scroll1.GetRange(curlow, curhigh)
6. The scroller only works if it responds to event messages WM_VSCROLL or WM_HSCROLL, as
appropriate. You need to give the form an ALLEVENTS event and test for scroll events
directed to the hscroll member of the scroller class. When you detect such an event,
call the UpDate method of the scroller class.
The following structure defined by Windows:
TYPE SCROLLINFO
cbSize AS UINT
fMask AS UINT
nMin AS LONG
nMax AS LONG
nPage AS UINT
nPos AS LONG
nTrackPos AS LONG
END TYPE
'/
type Scroller
as wfxForm form 'the form that is the home of this scroller
as hwnd hscroll 'the handle of the the new scroller
as boolean vert 'oriented vertically
as ScrollInfo info 'a structure that holds info including position and range
declare sub Initialize(form as wfxform, frame as wfxframe)
declare function GetVal() as long
declare sub SetVal(newval as long)
declare sub SetRange(RangeLow as long, RangeHigh as long)
declare sub GetRange(byref RangeLow as long, byref RangeHigh as long)
declare sub UpDate(e as EventArgs)
end type
sub scroller.Initialize(form as wfxform, frame as wfxframe)
dim as long idc = form.getnextctrlid()
dim as long dstyle
this.info.cbsize = sizeof(this.info)
if frame.height < frame.width then
dstyle = WS_VISIBLE OR sbs_horz 'or ws_border
this.vert = false
else
dstyle = WS_VISIBLE OR sbs_vert 'or ws_border
this.vert = true
end if
this.hscroll = form.pwindow->AddControl( "ScrollBar", , idc, "", _
frame.left, frame.top, _
frame.width, frame.height, _
dstyle)
this.info.nmin = 0
this.info.nmax = 100
this.info.npage = 10
this.info.npos = 50
this.info.fmask = sif_page or sif_pos or sif_range
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
enablescrollbar(this.hscroll, sb_ctl, esb_enable_both)
frame.Visible = false
end sub
/' From WinFBX:
DECLARE FUNCTION AddControl( BYREF wszClassName AS WSTRING, _
BYVAL hParent AS HWND = NULL, _
BYVAL cID AS LONG_PTR = 0, _
BYREF wszTitle AS WSTRING = "", _
BYVAL x AS LONG = 0, BYVAL y AS LONG = 0, _
BYVAL nWidth AS LONG = 0, BYVAL nHeight AS LONG = 0, _
BYVAL dwStyle AS LONG = -1, BYVAL dwExStyle AS LONG = -1, _
BYVAL lpParam AS LONG_PTR = 0, _
BYVAL pWndProc AS SUBCLASSPROC = NULL, _
BYVAL uIdSubclass AS UINT_PTR = &HFFFFFFFF, _
BYVAL dwRefData AS DWORD_PTR = NULL) _
AS HWND
'/
function scroller.GetVal() as long
this.info.fmask = sif_pos or sif_trackpos or sif_page
if getscrollinfo(this.hscroll, sb_ctl, @this.info) = 0 then
'afxmsg("Scroller failed to retrieve value", "Error")
end if
if this.vert then
return this.info.nmax + this.info.nmin - this.info.npos
else
return this.info.npos
end if
end function
sub scroller.SetVal( newval as long )
this.info.fmask = sif_pos
if this.vert then
this.info.npos = this.info.nmax + this.info.nmin - newval
else
this.info.npos = newval
end if
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
end sub
sub scroller.SetRange(RangeLow as long, RangeHigh as long)
if (rangehigh < rangelow) then
swap rangelow, rangehigh
end if
this.info.fmask = sif_range or sif_page
this.info.nmin = rangelow
this.info.nmax = rangehigh
this.info.npage = abs(rangehigh-rangelow)/10
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
end sub
sub scroller.GetRange(byref RangeLow as long, byref RangeHigh as long)
this.info.fmask = sif_range
getscrollinfo(this.hscroll, sb_ctl, @this.info)
rangelow = this.info.nmin
rangehigh = this.info.nmax
end sub
sub scroller.UpDate(e as EventArgs)
select case loword(e.wParam)
case sb_lineup, sb_lineleft
this.info.fmask = sif_pos
this.info.npos -= 1
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
case sb_linedown, sb_lineright
this.info.fmask = sif_pos
this.info.npos += 1
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
case sb_pageup, sb_pageleft
this.info.fmask = sif_pos
this.info.npos -= this.info.npage
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
case sb_pagedown, sb_pageright
this.info.fmask = sif_pos
this.info.npos += this.info.npage
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
case sb_thumbposition
this.info.fmask = sif_trackpos
getscrollinfo(this.hscroll, sb_ctl, @this.info)
this.info.fmask = sif_pos
this.info.npos = this.info.ntrackpos
setscrollinfo(this.hscroll, sb_ctl, @this.info, true)
end select
end sub
This is the frmMain file. It allocates a couple of scrollers, initializes them in the Load event, and responds to the WM_HSCROLL and WM_VSCROLL messages in the AllEvents event. One of the buttons sets the value of the scrollers to the middle of their ranges, and the other button prints the current value of the scrollers in the labels above.
' frmMain form code file
''
''
' Include the scroller class
#include once "scroller.inc"
'Allocate a couple of scrollers
dim shared as scroller hscroll 'This one is horizontal because it will overlap a horizontal frame
dim shared as scroller vscroll 'This one is vertical because it overlaps a vertical frame
Function frmMain_Load( ByRef sender As wfxForm, ByRef e As EventArgs ) As LRESULT
'Create horizontal scroller
hscroll.Initialize(frmmain, frmmain.frame1) 'Initialize it into a (horizontal) frame.
hscroll.SetRange(-500, 500) 'Set the range
hscroll.SetVal(200) 'Initial value
'Create vertical scroller
vscroll.Initialize(frmmain, frmmain.Frame2) 'This time the frame is vertical
vscroll.SetRange(0, 50000) 'A bigger range than the other one
vscroll.SetVal(1000)
Function = 0
End Function
''
''
Function frmMain_AllEvents( ByRef sender As wfxForm, ByRef e As EventArgs ) As LRESULT
'Test for scroll messages directed to the handle of a Scroller
select case e.Message
case wm_vscroll
if e.lParam = vscroll.hscroll then
vscroll.UpDate(e)
end if
case wm_hscroll
if e.lParam = hscroll.hscroll then
hscroll.UpDate(e)
end if
end select
Function = 0
End Function
''
''
Function frmMain_Button1_Click( ByRef sender As wfxButton, ByRef e As EventArgs ) As LRESULT
'Center the values of the controls in their ranges.
dim as long n, m
hscroll.GetRange(n, m)
hscroll.SetVal((n+m)\2)
vscroll.GetRange(n, m)
vscroll.SetVal((n+m)\2)
Function = 0
End Function
''
''
Function frmMain_Button2_Click( ByRef sender As wfxButton, ByRef e As EventArgs ) As LRESULT
'print the current values of the Scrollers.
frmmain.hlabel.Text = str(hscroll.GetVal())
frmmain.vlabel.Text = str(vscroll.GetVal())
Function = 0
End Function
Feel free to use any of this if it has value to you.