PDA

View Full Version : Listview %lvn_keydown



martin
26-08-2009, 11:20
Hi :mrgreen:

This is my problem for today:

I detect if the DOWN key is pressed in a listview. I do this with CASE %LVN_KEYDOWN and GETWINDOWKEYSTATE. This works good, but the problem is that if down-key is pressed my code is executed and AFTER THAT the listview respond once again to the KEY (this must be done by internal code of listview, not by my script). This means that the blue select-bar will be moved and I don't want that.

Is there a way to solve this? I guess not :(

:anyone:

ErosOlmi
26-08-2009, 11:24
martin,

I need a little example because there can be many ways things can go well or bad in this situation.
GETWINDOWKEYSTATE usage can be one of the problems.

Ciao
Eros

martin
26-08-2009, 11:37
Hi Eros,

Not a full example if you don't mind but a snippet. I think it will be enough to understand my problem. If not, I will make full example for you.



SELECT CASE cbMsg
CASE %WM_NOTIFY
SELECT CASE cbCTL
CASE %listview1
lvnm = cblParam '---lParam contains a pointer to a listview notification structure, so assign it to our lvnm pointer
SELECT CASE lvnm.hdr.code '---Now check the notification event occurred
CASE %lvn_keydown
IF GETWINDOWKEYSTATE(hdlg,%vk_down)<>0 THEN
msgbox 0,"the blue select bar in the listview will move one row down now, but i don't want that, i want my own code here"
end if
end select
end select
end select

ErosOlmi
26-08-2009, 12:22
Ok, you are going in something never done in thinBasic so I had to need to investigate a bit and find reference.

See official Microsoft doc: http://msdn.microsoft.com/en-us/library/bb774849(VS.85).aspx
Because thinBasic try to strictly follow MS documentation, usually MSDN web site is the solution.

Attached the thinBasic solution.

I will add all necessary UDT structure natively to thinBasic UI module during next update.
For the moment you have to survive with a new UDT structure in your script.

Ciao
Eros

PS: better never use GETWINDOWKEYSTATE for windows events.

martin
26-08-2009, 12:40
Hi Eros,

Thanks for your research, I often look at mdsn but I do not always know how to convert things to ThinBasic.
However your example doesn't do what I want: the blue bar still moves a row down when I press the down-key. In other words: I want to disable respond to UP and DOWN key in the listview, and replace it with my own code. Do you think that's possible? I hope that you understand what i mean.

Martin

ErosOlmi
26-08-2009, 13:03
Ok, sorry. I concentrated only on one aspect.

... mumbling ... :telephone:
I do not know right now other than suggesting to fill the listview only with the necessary items to fill the screen and if (for example) %VK_DOWN after the last remove first and add a new one and things like that.
Otherwise I have the doubt you should go by own drawing the listview but not possible in thinBasic due to the necessity to have pointer to a compiled function.

But I will investigate further later this night.

Eros

martin
26-08-2009, 13:13
I do not know right now other than suggesting to fill the listview only with the necessary items to fill the screen and if (for example) %VK_DOWN after the last remove first and add a new one and things like that.

That's exactly what I am doing! But still have this problem with the DOWN key. But if I replace case %vk_down with case %vk_q and press Q-key instead of DOWN-key everything works perfect! (unfortunately Q-key is not the most common way to navigate :wink:)

martin
26-08-2009, 13:39
I found a temporary solution, but not a very nice one. I check in a Timer function which Row should be selected.

So if the user press key Down, first the correct row is selected (done by my %LVN_KEYDOWN code), then the first row is selected (I have no control on this), and then the Timer function select the correct row again.

martin
26-08-2009, 15:52
hmmm problem could be probably solved if selecting rows is completely disabled. It seems to be possible if %LVN_ITEMCHANGING returns value 0. I don't know how to do that, but if this is possible I can make my own selecting-bar (a coloured label in the listview). In fact I already made one, but the original (listview) bar still flickers in the screen when I navigate.

Maybe I must give up the microsoft listview and design my own (virtual) listview. Must be possible with labels I think...but it's more work, so I still hope that there's a solution for my problem.

I'm going to relax now :occasion:, struggled too much today :grrrr:

ErosOlmi
26-08-2009, 16:15
martin,

maybe if you can post a working example that shows exactly the problem others other me can help.
In any case, I'm working here to add new ListView native functions that maybe can help. I will upload an updated thinBasic beta version soon.

Eros

martin
26-08-2009, 17:26
I have it ready faster then I thought. The code below is an unfinished virtual listview. Moving horizontal and vertical scrollbars will execute code from the script, not from the listview itself. You can resize the columns and also the form. I made for this example a little array of 100 records. But it in my original script the listview is filled from a database-file with 30.000 records and it ''loads'' within a millisecond ;) The code is a little mess, because I cut it out from my original script, sorry. You can see the bad behavour from the blue selectbar if you hold the Down key for a while.


USES "ui"

BEGIN CONST
%Listview1 = 13
%ID_SLAVE=14
%timer3=702
%ID_Main_TAB=200
%ID_scrollbarV =9
%ID_scrollbarH =10
%line1=4
END CONST

type LVKEYDOWN
hdr as NMHDR
wVKey as WORD
flags as dword
end type

GLOBAL WindowMain,hslave as long
global reccount as long=100
GLOBAL selecteditem AS LONG=1
GLOBAL lvnm AS NM_LISTVIEW PTR
dim lvitem(100,4) as string

FUNCTION TBMAIN()
for i as long = 1 to 100
lvitem(i,1)="column 1, row " & i
lvitem(i,2)="column 2, row " & i
lvitem(i,3)="column 3, row " & i
lvitem(i,4)=tstr$(I)
next i

DIALOG NEW PIXELS, 0, "Virtual Listview Test", -1,-1, 400,400, %WS_DLGFRAME | %WS_CAPTION | %WS_SYSMENU | %WS_OVERLAPPEDWINDOW | %WS_CLIPSIBLINGS, 0 TO WindowMain
DIALOG SHOW MODAL WindowMain CALL windowmainproc
END FUNCTION


CALLBACK FUNCTION cbSlave()
static lvnm AS NM_LISTVIEW PTR
static pNM_LISTVIEW as NM_LISTVIEW ptr
static pLVKEYDOWN as LVKEYDOWN ptr

SELECT CASE cbMsg
CASE %WM_NOTIFY
SELECT CASE cbCTL
CASE %listview1
lvnm = cblParam
SELECT CASE lvnm.hdr.code
CASE %nm_click
selecteditem=VAL(LISTVIEW_GETITEMTEXT(hslave, %listview1,LISTVIEW_GETNEXTITEM(hslave, %listview1, -1, %LVNI_SELECTED),4))
CASE %NM_SETFOCUS
case %LVN_ITEMCHANGING
CASE %lvn_keydown
LOCAL Inscreen AS BOOLEAN=%false

pLVKEYDOWN = cblparam

select case pLVKEYDOWN.wVKey
case %vk_up
IF selecteditem>1 THEN DECR selecteditem
FOR rij AS LONG = 1 TO VisibleRows
IF selecteditem=VAL(LISTVIEW_GETITEMTEXT(hslave, %listview1,rij,4)) THEN
InScreen=%true
EXIT FOR
END IF
NEXT rij

IF NOT Inscreen THEN
SCROLLBAR_SETPOS(tabdatabase, %ID_ScrollBarV, SCROLLBAR_GETPOS(tabdatabase, %ID_ScrollBarV) - 1)
loadrecords
END IF


case %vk_down
IF selecteditem<reccount THEN INCR selecteditem
FOR rij AS LONG = 1 TO VisibleRows
IF selecteditem=VAL(LISTVIEW_GETITEMTEXT(hslave, %listview1,rij,4)) THEN
InScreen=%true
EXIT FOR
END IF
NEXT rij

IF NOT Inscreen THEN
SCROLLBAR_SETPOS(tabdatabase, %ID_ScrollBarV, SCROLLBAR_GETPOS(tabdatabase, %ID_ScrollBarV)+1)
loadrecords
END IF

end select
END SELECT
END SELECT
END SELECT
END FUNCTION




CALLBACK FUNCTION cb_Tab_1()
LOCAL I,w,h AS LONG

SELECT CASE cbMsg
CASE %WM_INITDIALOG

DIALOG GET CLIENT CBHNDL TO w, h

DIALOG NEW PIXELS, CBHNDL, "", 1, 38, w - 22, h - 100, %WS_CHILD TO hSlave
DIALOG SHOW MODELESS hSlave, CALL cbSlave

DIALOG GET CLIENT hSlave TO w, h

CONTROL ADD LISTVIEW hslave, %listview1, "", 0,0, w + WIN_GETSYSTEMMETRICS(%SM_CXVSCROLL),h + WIN_GETSYSTEMMETRICS(%SM_CXHSCROLL), %LVS_SINGLESEL | %WS_VISIBLE | %WS_CHILD | %LVS_SHOWSELALWAYS, %LVS_EX_FULLROWSELECT | %LVS_EX_LABELTIP | %LVS_EX_GRIDLINES
LISTVIEW_SETVIEW hSlave, %listview1, %LV_VIEW_DETAILS
LISTVIEW_INSERTCOLUMN hslave, %listview1, 1, 100, "Column 1", %LVCF_FMT | %LVCF_TEXT | %LVCF_WIDTH, %LVCFMT_LEFT
LISTVIEW_INSERTCOLUMN hslave, %listview1, 2, 100, "Column 2", %LVCF_FMT | %LVCF_TEXT | %LVCF_WIDTH, %LVCFMT_LEFT
LISTVIEW_INSERTCOLUMN hslave, %listview1, 3, 100, "Column 3", %LVCF_FMT | %LVCF_TEXT | %LVCF_WIDTH, %LVCFMT_LEFT
LISTVIEW_INSERTCOLUMN hslave, %listview1, 4, 0, "Hidden Column", %LVCF_FMT | %LVCF_TEXT | %LVCF_WIDTH, %LVCFMT_LEFT

CONTROL ADD SCROLLBAR, cbhndl, %ID_ScrollBarV, "", w,56,17,h-17, %SBS_VERT CALL cb_SBV_proc
CONTROL ADD SCROLLBAR, cbhndl, %ID_ScrollBarH, "", 1,38+h,w,17, %SBS_HORZ CALL cb_SBH_proc

CONTROL DISABLE cbhndl, %ID_ScrollBarV
CONTROL DISABLE cbhndl, %ID_ScrollBarH
CONTROL ADD LINE, cbhndl, %line1, "",0, 36, w+19, h+21

SCROLLBAR_SETRANGE(cbhndl,%ID_ScrollBarH,1,kolbreedte)
SCROLLBAR_SETPAGESIZE(cbhndl, %ID_ScrollBarH,w)
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, 1)

loadrecords

DIALOG SET TIMER windowmain,%timer3,15
CASE %WM_PAINT
CONTROL REDRAW hslave,%listview1
CASE %WM_SIZE
DIALOG GET CLIENT CBHNDL TO w, h
DIALOG SET SIZE hslave,w-22,h-100

DIALOG GET CLIENT hSlave TO w, h
CONTROL SET LOC cbhndl, %ID_ScrollBarV, w,56
CONTROL SET SIZE cbhndl, %ID_ScrollBarV,-1,h-17
CONTROL SET LOC cbhndl, %ID_ScrollBarH, 0,38+h
CONTROL SET SIZE cbhndl, %ID_ScrollBarH,w,-1
CONTROL SET SIZE cbhndl, %line1, w+19, h+21

LOCAL x,y AS LONG
CONTROL GET SIZE cbhndl, %ID_ScrollBarH TO x,y
CONTROL SET SIZE hSlave, %listview1, x+17, h+17
CONTROL SET LOC hslave,%listview1,-SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) ,-1

SCROLLBAR_SETPAGESIZE(cbhndl, %ID_ScrollBarH,w)
SCROLLBAR_SETRANGE(cbhndl,%ID_ScrollBarH,1,kolbreedte)

loadrecords
END SELECT
END FUNCTION

FUNCTION kolbreedte() AS LONG
LOCAL i,kol AS LONG
FOR i=1 TO 4
kol+=LISTVIEW_GETCOLUMNWIDTH(hslave, %listview1, i)
NEXT i
FUNCTION=kol
END FUNCTION

CALLBACK FUNCTION cb_SBV_proc() AS LONG
SELECT CASE cbmsg
CASE %WM_VSCROLL
SELECT CASE LOWRD(cbwParam)
CASE %SB_TOP
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETRANGELOW(cbhndl, %ID_ScrollBarV))
CASE %SB_BOTTOM
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETRANGEHI(cbhndl, %ID_ScrollBarV))
CASE %SB_LINEUP
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarV) - 1)
CASE %SB_LINEDOWN
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarV) + 1)
CASE %SB_PAGEUP
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarV) - SCROLLBAR_GETPAGESIZE(cbhndl, %ID_ScrollBarV))
CASE %SB_PAGEDOWN
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarV) + SCROLLBAR_GETPAGESIZE(cbhndl, %ID_ScrollBarV))
CASE %SB_THUMBTRACK
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarV, SCROLLBAR_GETTRACKPOS(cbhndl, %ID_ScrollBarV))
END SELECT
loadrecords
END SELECT
END FUNCTION

CALLBACK FUNCTION cb_SBH_proc() AS LONG
LOCAL w,h AS LONG

CONTROL SET SIZE hSlave, %listview1, kolbreedte , -1

DIALOG GET CLIENT hslave TO w, h
SCROLLBAR_SETPAGESIZE(cbhndl, %ID_ScrollBarH,w)
SCROLLBAR_SETRANGE(cbhndl,%ID_ScrollBarH,1,kolbreedte)

SELECT CASE cbmsg
CASE %WM_HSCROLL
SELECT CASE LOWRD(cbwParam)
CASE %SB_TOP
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETRANGELOW(cbhndl, %ID_ScrollBarH))
CASE %SB_BOTTOM
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETRANGEHI(cbhndl, %ID_ScrollBarH))
CASE %SB_LINEUP 'links
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) - 5)
CASE %SB_LINEDOWN 'rechts
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) + 5)
CASE %SB_PAGEUP
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) - SCROLLBAR_GETPAGESIZE(cbhndl, %ID_ScrollBarH))
CASE %SB_PAGEDOWN
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) + SCROLLBAR_GETPAGESIZE(cbhndl, %ID_ScrollBarH))
CASE %SB_THUMBTRACK
SCROLLBAR_SETPOS(cbhndl, %ID_ScrollBarH, SCROLLBAR_GETTRACKPOS(cbhndl, %ID_ScrollBarH))
END SELECT
CONTROL SET LOC hslave,%listview1,-SCROLLBAR_GETPOS(cbhndl, %ID_ScrollBarH) ,-1
END SELECT
END FUNCTION


CALLBACK FUNCTION cb_Tab_2() 'HELP TAB
LOCAL xx,yy AS LONG
SELECT CASE cbMsg
CASE %WM_INITDIALOG
CASE %WM_SIZE
CASE %WM_DESTROY
END SELECT
END FUNCTION



CALLBACK FUNCTION WindowMainProc() AS LONG 'Callback for dialog
SELECT CASE CBMSG 'Test for messages
CASE %WM_INITDIALOG
LOCAL xpos,ypos,sbWidth, sbHeight AS LONG
DIALOG GET CLIENT cbhndl TO xPos, yPos

CONTROL ADD TAB, cbhndl, %ID_Main_TAB, "", 0, 0, xpos, ypos - sbHeight
TAB_PAGEINSERT cbhndl, %ID_Main_TAB, 1, 0, "Virtual Listview" CALL cb_Tab_1
TAB_PAGEINSERT cbhndl, %ID_Main_TAB, 2, 0, "Nothing" CALL cb_Tab_2

CONTROL SET RESIZE cbhndl, %ID_Main_TAB, 1, 1, 1, 1
CONTROL UNTHEME cbhndl, %id_main_tab

GLOBAL TabDatabase AS LONG = TAB_PAGEGETHANDLE(cbhndl, %ID_Main_TAB, 1)


CASE %WM_TIMER
SELECT CASE cbwparam
CASE %timer3
SetScrollbars
END SELECT
CASE %WM_SIZE
TAB_ONRESIZE(cbhndl, %ID_Main_TAB)
CASE %WM_DESTROY
TAB_ONDESTROY(cbhndl, %ID_Main_TAB)
CASE %WM_NOTIFY
SELECT CASE cbCTL
CASE %ID_Main_TAB
TAB_ONNOTIFY(cbhndl, %ID_Main_TAB, cblparam)
END SELECT
CASE %WM_COMMAND
CASE %WM_CLOSE
DIALOG KILL TIMER windowmain,%timer3
STOP
END SELECT
END FUNCTION

FUNCTION SetScrollbars()
LOCAL w,h AS LONG

DIALOG GET CLIENT hSlave TO w, h
IF kolbreedte>w THEN
CONTROL ENABLE tabdatabase, %ID_ScrollBarH

SCROLLBAR_SETPAGESIZE(tabdatabase, %ID_ScrollBarH,w)
SCROLLBAR_SETRANGE(tabdatabase,%ID_ScrollBarH,1,kolbreedte)
ELSE
CONTROL DISABLE tabdatabase, %ID_ScrollBarH
END IF

IF RecCount>VisibleRows THEN
CONTROL ENABLE tabdatabase, %ID_ScrollBarV
ELSE
CONTROL DISABLE tabdatabase, %ID_ScrollBarV
END IF

IF LISTVIEW_GETCOLUMNWIDTH(hslave, %listview1, 4)>0 THEN LISTVIEW_SETCOLUMNWIDTH(hslave, %listview1, 4,0)

FOR rij AS LONG = 1 TO VisibleRows
IF selecteditem=VAL(LISTVIEW_GETITEMTEXT(hslave, %listview1,rij,4)) THEN
LISTVIEW_SETITEMSELECTED(hslave, %listview1, rij, %true)
exit for
END IF
NEXT rij

END FUNCTION

FUNCTION visiblerows() AS LONG
LOCAL x,y AS LONG
CONTROL GET SIZE tabdatabase,%ID_ScrollBarV TO x,y
FUNCTION =int(y/14)
END FUNCTION

FUNCTION loadrecords()
LOCAL rij,BlueRow,i AS LONG

SCROLLBAR_SETPAGESIZE(tabdatabase, %ID_ScrollBarV,visiblerows)
SCROLLBAR_SETRANGE(tabdatabase, %ID_ScrollBarV, 1,RecCount)
I=SCROLLBAR_GETPOS(tabdatabase, %ID_ScrollBarV)

LISTVIEW_BEGINUPDATE hslave, %listview1

LISTVIEW_DELETEALLITEMS hslave, %listview1

FOR rij = 1 TO VisibleRows
IF i>reccount THEN EXIT FOR

IF i=selecteditem THEN BlueRow=rij

LISTVIEW_INSERTITEM(hslave, %listview1, rij,lvitem(i,1) & "|" & lvitem(i,2) & "|" & lvitem(i,3)& "|" & lvitem(i,4))
INCR i
NEXT rij

IF BlueRow>0 THEN
LISTVIEW_SETITEMSELECTED(hslave, %listview1, BlueRow, %true)
LISTVIEW_ENSUREVISIBLE(hslave,%listview1,BlueRow,%false)
END IF

LISTVIEW_ENDUPDATE hslave, %listview1
eND FUNCTION

ErosOlmi
26-08-2009, 17:33
martin,

I think I've done a solution but I need to upload a new thinBasic beta version later night
When done, I will post here my solution. Mainly I've done a kind of virtual listview window that moves over a data array.

Ciao
Eros

martin
27-08-2009, 08:28
Cool!
I am looking forward to see it. :lol:

martin
02-09-2009, 13:14
Hi Eros!



Mainly I've done a kind of virtual listview window that moves over a data array.


I have no idea what you mean but I can't wait to see it :-) So i am just curious if you already have your solution ready. I guess not, because Thinair recovery has highest priority ofcourse. But if yes, could you please post it here?

Martin

ErosOlmi
02-09-2009, 13:28
Check the EXE attached and the source.
You cannot run the source because you need latest thinBasic but I can send you if my experiment is what you need.

AMENDED

martin
02-09-2009, 13:56
Thanks Eros, maybe this is a misunderstanding as I don't see what's virtual in this example. In the source code you add 500 items at once in the listview.



dim s(500, 4) as string
for Counter as long = 1 to 500
s(Counter,1) = "Folder " & Counter, Counter, "Whatever data here regarding Folder " & Counter, "Some notes " & Counter
next

listview_InsertData hSlave, %ID_LV_LEFT, s()

That's not what I want. In my own example (see post above) I fill the listview only with the items that should be visible. The only problem I have then, is the strange behaviour of pressing the down key. The listview respond to this key without any code, so if I add code for key down event, the listview respond TWICE.

ErosOlmi
02-09-2009, 14:10
:oops:
hooch sorry, I attached a completely wrong file.

Download new example attached again in previous post and see if it is what you need.

Eros

martin
02-09-2009, 14:23
That explains a lot :D

Eros, I got it working now, I noticed that you use FUNCTION=%TRUE in the KEYDOWN event. If I understand it well, this disable the listview-responding so we can place our own code there. I tried this too, and the problems are GONE! Damn, such a simple solution! To which address can I send the flowers? :lol:

ErosOlmi
02-09-2009, 14:28
To which address can I send the flowers? :lol:

To your nearest "Children Association"

Happy I was able to help.
Yes, usually (not always) to avoid "standard" behaves you want to manage in callbacks functions it is just necessary to return %TRUE. In this way standard function knows programmer has managed the event.