Thanks Chris, I was using "n" for the index array initially and changed to r because of the over use of n in multiple spots I played with the indexing as I said but it was late and I may not have been concentrating good job finding it
appreciate the help
Change that '0' to a '1' and it should work fine.
Copy Code for r=r-1 to 1 step -1
I'll have to dig deeper your change fixes the display (ie no blank line) but when I select a row then click "edit" the row selected is out one index position. so for now I will leave the blank line till I understand whats going on
Post by Chris Iverson on Jan 3, 2019 1:05:19 GMT -5
That one's easy, too.
YOUR array is one-indexed.
All Win32 internal arrays, including the one that it maintains the listview with, are zero-indexed. (Note that your own indexes are not passed into the control's internal array; the addRow sub just tells the control there's new data to include.)
When you ask the Win32 control what item is selected, it returns it's own array index number, which is zero-indexed.
You need to add one to the iItem value returned to match your array, or you need to build your array zero-indexed in the first place. (If you do choose to zero-index YOUR array, that then means you have off-by-one problems with the LB SQL DLL, which handles it's row counts one-indexed, as well as returning duplicate results if you go above/below row count.)
As you can probably guess, off-by-one errors are one of the most common headaches for programmers, especially when moving across different programming languages with different semantics. The semantics of C(especially the way for loops and array definitions work), which the Win32 API is written with(and for, really), lend themselves to being zero-indexed by default. LB's own semantics lend it towards being one-indexed. You can do either in both languages, but you'll need to do more off-by-one correction work to handle it.
Thanks Chris, I believe it has sunk in, so I now have one based indexes and have changed the [editrow] line print RecFound$(LVITEM.iItem.struct+1)' watch for saving edited data to select the LVITEM.iItem.struct+1 (index) which i'm guessing is plus 1 because I load the rows in reverse
I just have to remember if I save back to the listview to allow for that offset or I just save changes back to the DB and re-load the listview.
I'm glad there's no limit on dumb questions here, I've learnt a lot on this forum.
which i'm guessing is plus 1 because I load the rows in reverse
Not quite. It's because the Windows API numbers it's arrays starting at zero, whereas a lot of LB users(yourself included) number your arrays starting at one. That's what zero/one-indexing means.
If the Win32 API(actually, C code) creates an array that can old 10 items, the items are numbered 0-9, and the first item is index #0.
int a; //Create array of 10 items a = 1; //Set first index's value to 1 a = 10; //Set last index's value to 10
In LB code, creating an array of 10 items creates an array numbered from 1-10. The first item is index #1. (This isn't the full truth of LB's arrays, but we'll leave off even more confusing details for now.)
dim a(10) 'Create array of 10 items a(1) = 1 'Set first index's value to 1 a(10) = 10 'Set last index's value to 10
Because of this, if you're moving data between the two interfaces, you need to bump the index +/- 1 to make sure they match up. Otherwise, you'll either start your data too early, or too late. Here's an example table of an 10-item array filled with the numbers 1-10, in both C and LB.
Our habit of starting arrays from number 1 is driven by how Liberty listboxes and comboboxes work. Here index 0 is reserved and the first entry is numbered 1 not 0. 0 is actually used to return "not found" or to set the selected item to "nothing".
So as a Liberty coder I often build matching arrays starting from 1. I just ignore the fact that there is actually an array element 0. So for n = 1 to 10 instead of for n = 0 to 9 Liberty helps by dimming 11 elements when you ask for 10. You get array positions 0 to 10 (eleven elements) but only use 1 to 10 (ten elements).
Using API creates a zero based index, unlike Liberty, so everything can be zero based, mixing one and zero just over complicates does it not.
metro, Thanks for your help. I like the look, but it sure seems like a lot of trouble just to do dynamic interfaces. I'm going to wait for LB5. Posed to have a grid. If it can be created dynamically then we may have a easier solution. Hopefully we can create dynamic buttons, check boxes etc in the grid.. We will see?
Like you, I don't have a lot of time lately. Just bought a 16 unit apartment and am busy improving and maintaining it. Dan..
We got sidetracked on this thread but rather than start a new I thought I'd post some code I've played with
to give me an editable array of API created textboxes....maybe useful for someone to expand on.
some credit goes to Cundo and RNBW for the start
focus is set to the first textbox initially then you can tab to the next tb. making changes to data if required.
'=================================================== ' CREATE TEXTBOX GRID CreateTextboxAPI_Grid_5.bas ' USING WINDOWS API '=================================================== ' modified from LB Newsletter API Corner ' with corrections by Cundo LB Conforum 19 July 2016 ' Further modifications by RNBW 21 July 2016 ' Some Mods by metro Jan 2019 '=================================================== dim newdat$(1000) Fname$="cowNumber , recogn , followNum , also , date , milkingTime , milkAmount , milkmeterNumber" nomainwin
startH = 40: startV = 40: hRow = 20 : width = 80 DIM hT(numRecs,numFields) FOR row = 1 to numRecs FOR col = 1 to numFields ' Set up the style for the Textboxes Style = _WS_CHILDWINDOW OR _WS_BORDER OR _WS_VISIBLE OR _WS_TABSTOP'_ES_RIGHT 'OR _ES_NUMBER hPos = startH + (col-1)*width vPos = startV + (row-1) * hRow hT(row,col) = CreateTextbox(hwnd(#1),hPos,vPos,width+1,hRow+1,Style) NEXT col NEXT row
'FILL GRID FOR row = 1 to numRecs FOR col = 1 to numFields Print SetText$(hT(row,col), Ldat$(row,col)) NEXT col NEXT row
FOR row = 1 to numRecs FOR col = 1 to numFields NewLine$( row,col)=GetWindowText$( hT(row,col)) ' print NewLine$(row,col) newdat$(row)=newdat$(row)+","+NewLine$(row,col) ' newdat$(row)= AFTER$(newdat$(row),",") NEXT col NEXT row for z= 1 to numRecs newdat$(z)= AFTER$(newdat$(z),",") next
'-------------------------------------------- ' SUBs and FUNCTIONs '--------------------------------------------
' Get the focus in the Texbox Function GetFocus() CALLDLL #user32, "GetFocus",_ GetFocus AS uLONG END Function
' Set the focus in the Texbox SUB SetFocus hWnd CALLDLL #user32, "SetFocus",_ hWnd AS uLONG,_ result AS LONG END SUB
'Get the text entered into the Textbox FUNCTION GetWindowText$(hWnd) total = GetWindowTextLength(hWnd) Title$ = SPACE$(total) + CHR$(0): l= LEN(Title$)
CALLDLL #user32, "GetWindowTextA",_ hWnd AS uLONG,_ Title$ AS PTR,_ l AS LONG,_ result AS LONG GetWindowText$ = TRIM$(Title$) END FUNCTION
'Get the length of the text entered into Textbox FUNCTION GetWindowTextLength(hW) CALLDLL #user32, "GetWindowTextLengthA",_ hW AS uLONG,_ GetWindowTextLength AS LONG END FUNCTION
' Function to create Textbox FUNCTION CreateTextbox(hW, x, y, w, h, Style) Style = _WS_CHILDWINDOW OR _WS_BORDER _ OR _WS_VISIBLE hInst = GetWindowLong(hW, _GWL_HINSTANCE)
CALLDLL #user32, "CreateWindowExA",_ 0 AS LONG,_ "EDIT" AS PTR,_ "" AS PTR,_ Style AS LONG,_ x AS LONG,_ y AS LONG,_ w AS LONG,_ h AS LONG,_ hW AS uLONG,_ 0 AS LONG,_ hInst AS LONG,_ 0 AS LONG,_ CreateTextbox AS uLONG END FUNCTION
FUNCTION GetWindowLong(hW, type) CALLDLL #user32, "GetWindowLongA", _ hW AS uLONG,_ type AS LONG,_ GetWindowLong AS LONG END FUNCTION
function SetText$(hWnd , caption$) calldll #user32, "SendMessageA",_ hWnd as ulong,_ _WM_SETTEXT as long,_ 0 as long,_ caption$ as PTR,_ result as long end function
Sub SendMessageLong hWnd,msg,wParam,lParam CallDLL #user32, "SendMessageA",_ hWnd As uLong, _ msg As Long,_ wParam As Long,_ lParam As Long,_ re As Long end sub
Thanks metro. Ya.. lost my shirt in Los Vegas. Now I'll probably lose everything else with the apartment..
I still need to put buttons, dropdowns, radio buttons, links, text, textarea, photos etc in the grid. Else all I can do is edit stuff in it. It also has to be dynamic so it's possible to lay it out with variables and position boxes via program control. I've come to a dead end. Unless someone has a way to do this, i'll wait for LB5 to see if it's still a problem.
May 4, 2009 at 6:15am Quote like Post Options Post by Carl Gundel - admin on May 4, 2009 at 6:15am The SQLite locking feature I described is planned for v1.0.2. We will also be supporting MySQL in the v1.0.2 Personal Server license. The PostgreSQL and other database accessors will be in the Pro license. Testing for the personal license should start first. This is still at least a couple of months away.
Just maybe Carl has a solution close at hand.. you could stick with RunBasic if RB1.02 is around the corner.
The SQLite locking feature I described is planned for v1.0.2. We will also be supporting MySQL in the v1.0.2 Personal Server license. The PostgreSQL and other database accessors will be in the Pro license. Testing for the personal license should start first. This is still at least a couple of months away.
Just maybe Carl has a solution close at hand.. you could stick with RunBasic if RB1.02 is around the corner.
Thanks metro, I did a quick try maintaining a SQLite database with a grid. It's a simple test where I display the rows of a table in a grid with the option to do add, change, or delete. I tried but couldn't figure out how to put buttons in the grad, or how to display data that can't be changed. This is my first try, and basically I'm not sure I've done it correctly.
If you can show me how to display without changes, insert photos and buttons into the grid, and also insert a grid into a grid. If not I think I'll wait for V1.0.2 of Run Basic. Any help will be appreciated... Thanks Dan...
for i = 1 to 100 #mem execute("INSERT INTO tblA VALUES (";i;",'tblA_";right$("000"+str$(i),3);"',";i*10;")") next i print "Insert 100 random records complete" #mem execute("CREATE UNIQUE INDEX tblA_num ON tblA(tblANum)") print "Index Created"
mem$ = "SELECT max(length(tblANum)) as w1, max(length(tblADescr)) as w2, max(length(tblACount)) as w3 FROM tblA" ' find widths of columns for grid setup #mem execute(mem$) #row = #mem #nextrow() w1 = #row w1() * 12 w2 = #row w2() * 12 w3 = #row w3() * 12 w3 = 120 print "Column widths are:";w1,w2,w3
mem$ = "SELECT * FROM tblA LIMIT 20" #mem execute(mem$) rows = #mem ROWCOUNT() 'Get the number of rows
col = 6 row = 20 dim a$(col, row) grid #w.grid, a$(), [clicked], 10, 10, 600, 300 'create a grid widget
print tblANum;chr$(9);tblADescr$;chr$(9);tblACount next i
' ------------------------------------ ' show record data in grid with ' Add, Chg, or Del ' ------------------------------------ open "Grid Test" for window as #w ' open the grid #w.grid columnnames("ADD","CHG","DEL","Num","----Description----","Count") 'label the colums #w.grid columnwidths(50,50,50,w1,w2,w3) 'set sizes for columns #w.grid rownames("Table A","Record","Maintenance") 'label the rows #w.grid rowlabelwidth(60) 'set sizes for rows '#w.grid selectxy(2, 2) 'cause col 3, row 3 to be selected
[clicked] xy$ = #w.grid cellxy$() 'column,row clicked x = val(word$(xy$,1," ")) y = val(word$(xy$,2," ")) print "clicked: xy";xy$;" x:";x;" y:";y acd$ = #w.grid value$() print "cell contents: ";acd$ 'contents of cell clicked if acd$ = "[ADD]" then goto [addLike] ' add like this record if acd$ = "[CHG]" then goto [chg] if acd$ = "[DEL]" then goto [del] wait [addLike] [chg] [del] print "Doing ";acd$
#w.grid selectxy(4, y) 'cause col 4, row y to be selected tblANum$ = #w.grid value$() 'print contents of selected cell
open "Maintenance" for window as #w1 grid #w1, a1$(), [clicked1], 10, 10, 100, 300 'create a grid widget #w1.grid columnnames("Maintenance") 'label the colums #w1.grid columnwidths(100) 'set sizes for columns #w1.grid rownames("","Num","Descr","Count") 'label the rows #w1.grid rowlabelwidth(60) 'set sizes for rows
acd1$ = #w1.grid value$() print "cell contents: ";acd$ 'contents of cell clicked if acd1$ = acd$ then goto [doMaint] 'add like this record if acd$ = "[Exit]" then close #w1 wait end if [doMaint] print "Do ";acd$ close #w1 wait
[eoj] print "** End of Update**" #mem disconnect() end
Last Edit: Feb 24, 2019 19:10:56 GMT -5 by meerkat
Ya Right! If I had a clue, I'd still have my shirt after my Las Vegas trip. From now on I'll simply skip the trip and send them a check, stay home hand have a brew, and save everyone a lot of time.
Actually the code is usable as is but not very polished. It just needs some more cell features like; - multi line text editing - display only cells - ability to place buttons - images - ability to open multiple grids in a window. Now it makes a new window. - fonts by cell - links
Maybe I should use it for what it was intended and not use it as a form. It's basically set up to act like a spread sheet. Maybe there is something in LB5, haven't found yet. If it's like LB4 most of the windows are static and you have to hard code the locations. Maybe I don't know yet, but if LB5 has variables on the x,y locations, and you can dynamically create them with the program, that solves a lot of stuff.
BTW - if you try to set the font (#w.grid "font areal 14") it locks up LB5 and you have to kill it with the task manager.
Hope things are good down under. Have a gday ... Dan