dkl
Full Member
Posts: 234
|
Post by dkl on Jan 2, 2022 19:45:56 GMT -5
Can one link multiple listboxes together so that they all scroll vertically at the same time? So they all stay in alignment, thus keeping the info in each box relevant to the next? I was hoping there was a stylebits command, but couldn't find one. I'm sure I've seen one somewhere.
Any ideas? please......
|
|
|
Post by Rod on Jan 3, 2022 2:53:59 GMT -5
You are discussing a listview control. Search out examples on the forum and the LBPE.
|
|
|
Post by Brandon Parker on Jan 3, 2022 8:23:48 GMT -5
You can definitely do this, but it takes a little work. Rod is definitely correct in stating that a ListView is the way to go unless you absolutely need different ListBox controls. Aside from LBPE, you can search this forum for ListView with my name and find an example or two that might point you in a good direction. Here is one... libertybasiccom.proboards.com/post/12128/thread{:0) Brandon Parker
|
|
|
Post by Walt Decker on Jan 3, 2022 9:47:17 GMT -5
Here is a listview controlIf you want to stick with list boxes it is easily done by sending all clicks to one event handler. You can do it either with 2 api calls or parsing an array that has the handles of each list box.
|
|
|
Post by Walt Decker on Jan 3, 2022 10:56:23 GMT -5
' DIM Lft$(5) DIM Rgt$(5) DIM Handles$(1)
J = 6 FOR I = 0 TO 5 Lft$(I) = STR$(I) J = J - 1 Rgt$(I) = STR$(J) NEXT I
Handles$(0) = "#DMO.LBXL" Handles$(1) = "#DMO.LBXR"
LISTBOX #DMO.LBXL, Lft$(), SYNC, 5, 5, 75, 65 LISTBOX #DMO.LBXR, Rgt$(), SYNC, 80, 5, 75, 65
OPEN "LBX" FOR WINDOW AS #DMO PRINT #DMO, "TRAPCLOSE END.DMO" WAIT
'------------------------------------------------------ '------------------------------------------------------
SUB SYNC LbxHndl$
Item = 0 PRINT #LbxHndl$, "selectionindex? Item"
IF LbxHndl$ = Handles$(0) THEN LbxHndl$ = Handles$(1) PRINT #LbxHndl$, "selectindex ";Item EXIT SUB END IF
IF LbxHndl$ = Handles$(1) THEN LbxHndl$ = Handles$(0) END IF
PRINT #LbxHndl$, "selectindex ";Item
END SUB
'----------------------------------------------------- '-----------------------------------------------------
SUB END.DMO Win$
CLOSE #DMO END END SUB
'
If you what the do it on the high lighted row that can be accomplished using some simple API calls.
|
|
dkl
Full Member
Posts: 234
|
Post by dkl on Jan 3, 2022 18:26:04 GMT -5
Thank you for everyone's help. Yes,I suppose it is ListView Controls I need to look into, but in all the examples I found it seems and aweful lot of code for one simple action. I have to admit I couldn't figure out which bits I needed and those I didn't! Walt, I understand what you did in your code and that double clicking one listbox highlighted the adjacent box, but I couldn't get then to scroll together. Perhaps I'm doing something wrong. I am going to study your excellent notes on "Things You Don't Want To Know......" to give me a bit more enlightenment about a whole would of code I know nothing about and I'm to scared to look at! I want to display 3 pieces of info, so I think maybe concatenating the 3 pieces of info into one string and displaying it in a single listbox is probably the simplest way to go. I always try to search LB Forum and Alyce Watson's website before bothering you all with my questions. There is definitely an article/programme out there that does exactly what I want, because I remember thinking "Why would anyone want to do that!!" LOL. Well now I know Have a good Day/Evening
|
|
|
Post by Walt Decker on Jan 3, 2022 18:56:55 GMT -5
Perhaps I misunderstood. There is no native LB way to get the list boxes to scroll together without clicking on one. That being said, it can be done with API if you are interested.
Do not be afraid of API. Unless you are messing with the registry there is no way you can harm your computer by using API.
|
|
dkl
Full Member
Posts: 234
|
Post by dkl on Jan 4, 2022 18:07:06 GMT -5
Hi Walt, Haha, I suppose 'scared' of API is the wrong way of putting it - makes it sound like a Stephen King horror movie! But I never understand what the code is doing, but that's probably because I haven't studied it enough. I would be interested in seeing how it can be done in API if it's not too much trouble, but I have found a workaround for the moment and used the method I mentioned above.
|
|
|
Post by Walt Decker on Jan 5, 2022 8:36:45 GMT -5
Ok, I will whip one up with lots of info concerning what is going on.
|
|
dkl
Full Member
Posts: 234
|
Post by dkl on Jan 6, 2022 5:25:42 GMT -5
That would be marvelous. Many thanks, I appreciate your time
|
|
|
Post by Walt Decker on Jan 6, 2022 15:57:37 GMT -5
Here you go. All the API stuff can be found in "Things You Do Not Want to Know..." I hope I have done a half-way decent job of explaining what is happening. You can get NUMBERMANDLL here' LBS.DISABLENOSCROLL = HEXDEC("&H1000") '<--- listbox style to keep the 'vertical scroll bar visible at 'all times
VK.LBUTTON = 1 '<--- left mouse button VK.RETURN = HEXDEC("&H0D") '<--- keyboard enter key
SB.VERT = 1 '<--- scroll bar type SB.THUMBPOSITION = 4 '<--- scroll bar position flag
WM.VSCROLL = HEXDEC("&H0115") '<--- call back function message 'sending this message with the proper 'parameters will scroll the list box
LB.ITEMFROMPOINT = HEXDEC("&H01A9") '<--- list box message to find the 'list box item from the mouse cursor 'position
OPEN "User32.dll" FOR DLL AS #USER OPEN "NUMBERMANDLL" FOR DLL AS #NUM
I = 0 '<--- counters J = 0 K = 0
CtlLow = 0 '<--- lowest control ID number 'each control on a form window 'is given a unique ID number. 'this is used to calculate the 'element of Handles$() to use.
LstThumb = 0 '<--- scroll bar thumb position. The 'thumb is the box in the scroll bar. LstCtrl = 0 '<--- the control that last had the focus 'used if none of the list boxes has the 'focus. Ubnd = 2 '<--- number of row elements in Handles$()
DIM Lft$(50) DIM Mdl$(50) DIM Rgt$(50) DIM Handles$(2, 2)
J = 50 K = 51 FOR I = 0 TO 49 Lft$(I) = STR$(I) K = K + 1 Mdl$(I) = STR$(K) J = J - 1 Rgt$(I) = STR$(J) NEXT I
Handles$(0, 0) = "#DMO.LBXL" Handles$(1, 0) = "#DMO.LBXM" Handles$(2, 0) = "#DMO.LBXR"
STYLEBITS #DMO.LBXL, LBS.DISABLENOSCROLL, 0, 0, 0 STYLEBITS #DMO.LBXM, LBS.DISABLENOSCROLL, 0, 0, 0 STYLEBITS #DMO.LBXR, LBS.DISABLENOSCROLL, 0, 0, 0
LISTBOX #DMO.LBXL, Lft$(), SYNC, 5, 5, 75, 65 LISTBOX #DMO.LBXM, Mdl$(), SYNC, 80, 5, 75, 65 LISTBOX #DMO.LBXR, Rgt$(), SYNC, 155, 5, 75, 65
OPEN "LBX" FOR WINDOW AS #DMO PRINT #DMO, "TRAPCLOSE END.DMO"
PRINT #DMO.LBXL, "SELECTINDEX 1" '<--- set initial list box item PRINT #DMO.LBXM, "SELECTINDEX 1" PRINT #DMO.LBXR, "SELECTINDEX 1" PRINT #DMO.LBXL, "SETFOCUS"
Handles$(0, 1) = STR$(FN.GetHandle("DMO.LBXL")) '<--- get listbox handles Handles$(1, 1) = STR$(FN.GetHandle("DMO.LBXM")) Handles$(2, 1) = STR$(FN.GetHandle("DMO.LBXR"))
WinHndl = FN.GetHandle("#DMO.LBXL") LstCtrl = WinHndl
CtlId = FN.GetCtlId(WinHndl) '<--- get listbox control ID number 'the fist listbox defined will have the 'lowest ID number CtlLow = CtlId '<--- set for later calculations
Handles$(0, 2) = STR$(CltId) '<--- save control ID numbers Handles$(1, 2) = STR$(CltId + 1) Handles$(2, 2) = STR$(CltId + 2)
[BEGIN.TIMER] '<--- begin event trap TIMER 100, [GET.SCROLL]
WAIT
'------------------------------------------------------ '------------------------------------------------------
[GET.SCROLL] '##################################################### ' This is the work-horse of the demo. It is similar to ' a windows callback function except it does not fire ' all the time. Normally I would put most of this in ' functions but I think that keeping it together is a ' little more instructive. '##################################################### RetVal = 0 '<--- mostly a dummy variable CtlHndl = 0 '<--- handle of current control ArayPos = 0 '<--- row element of Handles$() 'calculated using the current 'control Id and CtlLow CurScroll= 0 '<--- current position of the 'scroll bar thumb Clicked = 0 '<--- value indicating whether the 'left mouse button was clicked LbxIndex = 0 '<--- index of item in the current 'list box
Ulx = 0 '<--- control size/position Uly = 0 Brx = 0 Bry = 0
Cpx = 0 '<--- client position of mouse cursor Cpy = 0
Wcpx = 0 '<--- screen position of mouse cursor Wcpy = 0
I = 0 '<--- counter
HndlStr$ = "" '<--- control tag e. g. "#DMO.LBXL"
CtlHndl = FN.GetFocus() '<--- control or window with focus CtlId = FN.GetCtlId(CtlHndl) '<--- control ID number ArayPos = CtlId - CtlLow '<--- calculate row element of Handles$()
IF (ArayPos < 0) OR (ArayPos > Ubnd) THEN '<--- if a control looses RetVal = FN.SetFocus(LstCtrl) 'the focus set it back to GOTO [BEGIN.TIMER] 'the last control END IF
LstCtrl = CtlHndl '<--- set the last control to the current control
CurScroll = FN.ScrollPos(CtlHndl) '<--- get the current scroll position
'############################################################### ' Here we check the current position of the scroll bar thumb. ' If it has changed we need to scroll the other list boxes to ' keep them in sync. '############################################################### IF CurScroll <> LstThumb THEN LstThumb = CurScroll
'<--- the WM.VSCROLL message requires two pieces of information ---> '<--- stuffed into one variable. The following does that ---------> CALLDLL #NUM, "FN_SetLong", SB.THUMBPOSITION AS SHORT, LstThumb AS SHORT, _ CurScroll AS LONG
'##################################################################### ' run through the list boxes, skip the current list box, and ' send a WM.VSCROLL message to the ones that do not have the ' focus. Under certain circumstances there will be a slight ' lag. '##################################################################### FOR I = 0 TO Ubnd
IF I = ArayPos THEN GOTO [NXT.SEND] '<--- skip the current control 'it is scrolled '<------------ send the WM.VSCROLL message to the other controls ----> CtlHndl = VAL(Handles$(I, 1))
'<-------- the WM.VSCROLL message handler is in the list box control ----> CALLDLL #USER, "SendMessageA", CtlHndl AS ULONG, WM.VSCROLL AS ULONG, _ CurScroll AS LONG, CtlHndl AS ULONG, RetVal AS LONG
[NXT.SEND] NEXT I GOTO [BEGIN.TIMER] '<--- go back END IF
Clicked = FN.KeyState(VK.LBUTTON) '<--- has left mouse button been clicked? IF Clicked THEN '<--- yes
RetVal = FN.ClientSize(CtlHndl, Brx, Bry) '<--- get the size of the 'list box client area. 'that is where items are 'displayed RetVal = FN.CursorPos(Cpx, Cpy) '<--- get location of the mouse cursor 'the position is in screen coordinates Wcpx = Cpx '<--- save position for possible later use Wcpy = Cpy
RetVal = FN.ScreenToClient(CtlHndl, Cpx, Cpy) '<--- translate cursor 'position to list box 'position IF (Cpx >= 0) AND (Cpx <= Brx) THEN '<--- check to see it mouse position IF (Cpy >= 0) AND (Cpy <= Bry) THEN 'is in list box client area
'<--------- prepare to find the nearest list box item -----------> CALLDLL #NUM, "FN_SetLong", Cpx AS SHORT, Cpy AS SHORT, _ CurScroll AS LONG
'<----- find the neares list box item to the mouse cursor --------> CALLDLL #USER, "SendMessageA", CtlHndl AS ULONG, _ LB.ITEMFROMPOINT AS ULONG, 0 AS LONG, CurScroll AS LONG, _ RetVal AS LONG
'<--- the zero-base index item is in the low word of the retrun value -----> CALLDLL #NUM, "FN_GetLowWord", RetVal AS LONG, LbxIndex AS LONG
'############################################################### ' Parse the control, scroll the selected item into view if ' necessary and highlight the item in the other list boxes '############################################################### FOR I = 0 TO Ubnd IF VAL(Handles$(I, 1)) = CtlHndl THEN GOTO [NXT.ITEM]'<--- skip current 'control HndlStr$ = Handles$(I, 0) PRINT #HndlStr$, "selectindex "; LbxIndex + 1
[NXT.ITEM] NEXT I GOTO [BEGIN.TIMER] '<--- go back END IF ELSE '################################################################ ' Liberty Basic will not change the focus, i. e. the control that ' receives keyboard or mouse input unless either the client area ' is clicked or the tab button is pressed. Therefor we have to ' find which scroll bar was clicked and change the focus to that ' control. '################################################################
CtlHndl = FN.FindLbx(Ubnd, Wcpx, Wcpy) '<--- find the control RetVal = FN.SetFocus(CtlHndl) '<--- set the new focus GOTO [BEGIN.TIMER] '<--- go back END IF END IF
'################################################################ ' When you use the up/down arrow keys with a list box the control ' moves the item focus to the next or previous list box item. ' However, the control does not know how to handle the enter key. ' The following small piece of code takes care of that. '################################################################
RetVal = FN.KeyState(VK.RETURN) '<--- was enter pressed IF RetVal THEN '<--- yes; simulate left button double click HndlStr$ = Handles$(ArayPos, 0) CALL SYNC HndlStr$ END IF
GOTO [BEGIN.TIMER] [GET.SCROLL.END]
'------------------------------------------------------ '------------------------------------------------------
SUB SYNC LbxHndl$ '####################################################### ' Get the synchronized items from the list boxes '#######################################################
Item = 0
ItmStr$ = "" StrIn$ = ""
PRINT #LbxHndl$, "selectionindex? Item"
FOR I = 0 TO 2 LbxHndl$ = Handles$(I, 0) PRINT #LbxHndl$, "selectindex "; Item PRINT #LbxHndl$, "selection? ItmStr$" StrIn$ = StrIn$ + ItmStr$ + " , " NEXT I
PRINT StrIn$
END SUB
'----------------------------------------------------- '-----------------------------------------------------
SUB END.DMO Win$
CLOSE #USER CLOSE #NUM CLOSE #DMO END END SUB
'----------------------------------------------------- '-----------------------------------------------------
FUNCTION FN.CheckHandle$(Tag$)
IF LEFT$(Tag$, 1) <> "#" THEN Tag$ = "#" + Tag$
FN.CheckHandle$ = Tag$ END FUNCTION
'----------------------------------------------------- '-----------------------------------------------------
FUNCTION FN.GetHandle(WinTag$)
Handle = 0 WinTag$ = FN.CheckHandle$(WinTag$)
Handle = HWND(#WinTag$) FN.GetHandle = Handle END FUNCTION
'-------------------------------------------------------------------- '--------------------------------------------------------------------
FUNCTION FN.GetCtlId(CtlHndl) '################################################################ ' Each control has a numeric ID number. This module gets that ' ID number '################################################################
CtlId = 0 CALLDLL #USER, "GetDlgCtrlID", CtlHndl AS ULONG, CtlId AS LONG
FN.GetCtlId = CtlId END FUNCTION
'-------------------------------------------------------------------- '--------------------------------------------------------------------
FUNCTION FN.GetFocus() '################################################################ ' This module finds the control or window that has the focus. ' Only a window or control that has the focus can receive input ' vias the keyboard or the mouse. '################################################################
CtlHndl = 0 CALLDLL #USER, "GetFocus", CtlHndl AS ULONG
FN.GetFocus = CtlHndl END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.SetFocus(CtrlHndl) '################################################################ ' This module sets the window or control so it can receive input ' from the mouse or keyboard '################################################################
RetVal = 0 CALLDLL #USER, "SetFocus", CtrlHndl AS ULONG, RetVal AS VOID
END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.ScrollPos(CtlHndl) '################################################################ ' Retrieve the position of the scroll bar thumb, i. e. then box ' in the scroll bar. The API function GetScrollPos() is obsolete ' but can still be used. The preferred method is to use the ' GetScrollInfo() function, but GetScrollPos() is a little ' easier to understand. '################################################################
SB.VERT = 1
CurPos = 0 CALLDLL #USER, "GetScrollPos", CtlHndl AS ULONG, SB.VERT AS LONG, _ CurPos AS LONG
FN.ScrollPos = CurPos
END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.ClientSize(WinHndl, BYREF Bx, BYREF By) '################################################################ ' Retrieve the client area of a window (form or control). Since ' the upper left corner of the client area is always zero, zero ' the size of the client area is determined by the lower right ' corner of the client rectangle. ' ' ARGUMENTS: ' WinHndl: Handle of the window (form or control). ' Bx, By: Lower right corner of the client rectangle '################################################################
STRUCT tRect, _ X AS LONG, _ Y AS LONG, _ X1 AS LONG, _ Y1 AS LONG
RetVal = 0 '<--- dummy variable CALLDLL #USER, "GetClientRect", WinHndl AS ULONG, tRect AS STRUCT, _ RetVal AS VOID
Bx = tRect.X1.struct By = tRect.Y1.struct END FUNCTION
'--------------------------------------------------------------- '---------------------------------------------------------------
FUNCTION FN.WindowSize(Hndl, BYREF Ux, BYREF Uy, BYREF Bx, BYREF By) '################################################################ ' Retrieve the total size of the window or control '################################################################
STRUCT tRect, _ X AS LONG, _ Y AS LONG, _ X1 AS LONG, _ Y1 AS LONG
CALLDLL #USER, "GetWindowRect", Hndl AS ULONG, tRect AS STRUCT, RetVal AS VOID
Ux = tRect.X.struct Uy = tRect.Y.struct Bx = tRect.X1.struct By = tRect.Y1.struct
END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.KeyState(Vkey) '################################################################ ' Determine whether Vkey is pressed '################################################################
VK.SHIFT = HEXDEC("&H10") '<--- keyboard shift key VK.CONTROL = HEXDEC("&H11") '<--- keyboard control key
KeyPressed = 0
Value = 0
Mask = HEXDEC("&H8000") '<--- value used to determine if the 'pressed bit is set
'<--- the following checks to see if 2 keys are pressed at the same time ---> CALLDLL #USER, "GetAsyncKeyState", VK.SHIFT AS LONG, KeyPressed AS SHORT IF KeyPressed AND Mask THEN CALLDLL #USER, "GetAsyncKeyState", Vkey AS LONG, KeyPressed AS SHORT IF KeyPressed AND Mask THEN FN.KeyState = 2 EXIT FUNCTION END IF END IF
CALLDLL #USER, "GetAsyncKeyState", VK.CONTROL AS LONG, KeyPressed AS SHORT IF KeyPressed AND Mask THEN CALLDLL #USER, "GetAsyncKeyState", Vkey AS LONG, KeyPressed AS SHORT IF KeyPressed AND Mask THEN FN.KeyState = 2 EXIT FUNCTION END IF END IF
'<--- only the Vkey parameter is pressed -----------> CALLDLL #USER, "GetAsyncKeyState", Vkey AS LONG, KeyPressed AS SHORT
IF KeyPressed AND Mask THEN FN.KeyState = 1
END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.CursorPos(BYREF Cpx, BYREF Cpy) '################################################################ ' Retrieve the position of the mouse cursor. The position is ' in screen coordinates. '################################################################
STRUCT tPnt, _ X AS LONG, _ Y AS LONG
RetVal = 0 CALLDLL #USER, "GetCursorPos", tPnt AS STRUCT, RetVal AS VOID
Cpx = tPnt.X.struct Cpy = tPnt.Y.struct END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.ScreenToClient(WinHndl, BYREF Cpx, BYREF Cpy) '################################################################ ' Translate screen coordinates to window or control client ' coordinates. Client coordinages are those coordinates that ' are not caption or border. '################################################################
STRUCT tPnt, _ X AS LONG, _ Y AS LONG
RetVal = 0 tPnt.X.struct = Cpx tPnt.Y.struct = Cpy
CALLDLL #USER, "ScreenToClient", WinHndl AS ULONG, tPnt AS STRUCT, _ RetVal AS VOID
Cpx = tPnt.X.struct Cpy = tPnt.Y.struct END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------
FUNCTION FN.FindLbx(NumElms, Cpx, Cpy) '################################################################ ' Find the list box that contains the mouse cursor when the left ' mouse button was clicked. This module uses the entire window ' size since the mouse coordinates are in window coordinates. '################################################################
Ulx = 0 Uly = 0 Brx = 0 Bry = 0 Crx = 0 Cry = 0
RetVal = 0 CtlHndl = 0
FOR I = 0 TO NumElms CtlHndl = VAL(Handles$(I, 1))
'<---- get the window coordinates of the list box -------> RetVal = FN.WindowSize(CtlHndl, Ulx, Uly, Brx, Bry)
IF (Cpx >= Ulx) AND (Cpx <= Brx) THEN '<--- is the mouse cursor in this IF (Cpy >= Uly) AND (Cpy <= Bry) THEN 'list box? FN.FindLbx = CtlHndl EXIT FUNCTION END IF END IF NEXT I END FUNCTION
'------------------------------------------------------------------ '------------------------------------------------------------------ '
|
|
dkl
Full Member
Posts: 234
|
Post by dkl on Jan 6, 2022 18:41:08 GMT -5
Many Thanks Walt. I have just taken a brief look and I see that the listboxes in the example here do scroll together, where as in the example I mention above they did not do that. Obviously a different version as here there are 3 listboxes where as the above version only had 2. I have to admit, I still find the amount of coding for such a small desired effect quite daunting. I have all your info on "Things You Do Not Want to Know...". Many Thanks, I appreciate what you have done and your time and effort
|
|
|
Post by Walt Decker on Jan 7, 2022 7:46:32 GMT -5
In the other languages I use the amount of additional code to produce the desired effect is minimal, about 10 additional lines because I write my own callback functions. When one does not have access to the callback function a different route must be used which results in a plethora of additional code.
One might be able to use the LBS_MULTICOLUMN style for a list box and then set the the column width, in pixels, to the longest column string. I have not tried that in LB.
A list view control is a much better approach. It contains more options and more information with very little additional code other than set-up.
|
|