Sign in to follow this  
Followers 0
c.haslam

Keyboard navigation in a ListView

13 posts in this topic

#1 ·  Posted (edited)

The following snippet excerpted (and simplified) from a multi-form application, is an attempt to allow the user to use down, up and other keys to move from item to item in a ListView.

#include <constants.au3>
#include <guiconstants.au3>
#include <WindowsConstants.au3>
#include <guiconstantsEx.au3>
#include <GuiListView.au3>
#include <date.au3>
#include <math.au3>
#include <misc.au3>

Opt("MustDeclareVars",1)

Global Const $kFnam=1,$kWid=2,$kHt=3,$kTxt=4,$kCalced=5,$kDatTim=6,$kWjpg=7,$kHjpg=8,$kMaxRowIx=8
Local $fmPreview = GUICreate("Preview: jpg's in Directory but not in Project", 930, 665, 32, 36, BitOR($WS_SYSMENU,$WS_CAPTION,$WS_POPUP,$WS_POPUPWINDOW,$WS_BORDER,$WS_CLIPSIBLINGS))
Local $gPrvBtnClose = GUICtrlCreateButton("Close", 872, 616, 41, 25, 0)
Local $gPrvListView1 = GUICtrlCreateListView("Filename|Date and time", 8, 8, 380, 512)
GUICtrlSendMsg(-1, 0x101E, 0, 190)  ; set column widths
GUICtrlSendMsg(-1, 0x101E, 1, 175)
Local $gPrvlblSize = GUICtrlCreateLabel("", 592, 534, 148, 17)

Local $nuAr[41][$kMaxRowIx+1]
For $i = 1 To 40
    $nuAr[$i][$kFnam] = "fnam"&$i
    $nuAr[$i][$kDatTim] = _Now()
Next

Local $qPics = UBound($nuAr, 1) - 1
Local $cidVec[$qPics + 1]
For $i = 1 To $qPics
    Local $t = $nuAr[$i][$kFnam] & "|" & $nuAr[$i][$kDatTim]
    $cidVec[$i] = GUICtrlCreateListViewItem($t, $gPrvListView1)
Next
_GUICtrlListView_SetItemSelected($gPrvListView1, 0)
ShowPreviewPic($nuAr, 1)
GUISetState(@SW_SHOW, $fmPreview)

Local $gUser32Dll = DllOpen("user32.dll")   
Local $nrow = 1,$nMsg,$cidIsLVitem

While 1
;#cs        
    Select
        Case _IsPressed(28,$gUser32Dll) And $nrow<$qPics    ; down key
        $nMsg = $cidVec[$nrow+1]
    Case _IsPressed(26,$gUser32Dll) And $nrow>1    ; up key
        $nMsg = $cidVec[$nrow-1]
    Case _IsPressed(22,$gUser32Dll)        ; Pg Dn key
        $nMsg = $cidVec[_Min($nrow+35,$qPics)]
    Case _IsPressed(21,$gUser32Dll)        ; Pg Up key
        $nMsg = $cidVec[_Max($nrow-35,1)]
    Case _IsPressed(23,$gUser32Dll)        ; End key
        $nMsg = $cidVec[$qPics]
    Case _IsPressed(24,$gUser32Dll)        ; Home key
        $nMsg = $cidVec[1]
    Case Else
;#ce            
    $nMsg = GUIGetMsg()
;#cs    
    EndSelect
;#ce
    $cidIsLVitem = False
    For $i = 1 To $qPics
        If $nMsg = $cidVec[$i] Then
            $nrow = $i
            ShowPreviewPic($nuAr, $nrow)
            $cidIsLVitem = True
            ExitLoop
        EndIf
    Next
    If Not $cidIsLVitem Then
        Switch $nMsg
            Case $GUI_EVENT_CLOSE, $gPrvBtnClose
                ExitLoop
        EndSwitch
    EndIf
WEnd
DllClose($gUser32Dll)

Func ShowPreviewPic($nuAr,$nrow)
    GUICtrlSetData($gPrvlblSize,$nuAr[$nrow][$kFnam]&"   "& $nuAr[$nrow][$kDatTim])
EndFuncoÝ÷ Ù8b²Ç+p¢¹,8«¬x¬zÚ,zW¶+^Â$-«Hq©æjG­+¡×¦¢·}øéíëi­ë,x-çhÂv«®${'hzÉ÷öÜ(®F¬{^r×jwH$0 ¨Èz»ayú%"®*mjëh×6#Include 
While True
    For $i= 1 To 255
        If _IsPressed($i) Then
            ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $i = ' & $i & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
        EndIf
    Next
    If _IsPressed(58) Then Exit
WEnd

_IsPressed() continues to return true as long as the down arrow key is pressed. For even the briefest touch on the key, there are 3 or 4 writes to the console.

This is why, in the GUI script, pressing the down arrow when fnam1 is selected causes fnam40 to be displayed.

It appears to me that _IsPressed() is not the function for this application. Perhaps someone can tell me what approach I should take.

I have thought of calling GuiCtrlRead($gPrvListView1), but where in the message loop should I do it?

Help will be much appreciated.

...chris

Edited by c.haslam

...chris

Share this post


Link to post
Share on other sites



The following snippet excerpted (and simplified) from a multi-form application, is an attempt to allow the user to use down, up and other keys to move from item to item in a ListView.

Your example does not work. Post a small and working example to duplicate your problem.

Share this post


Link to post
Share on other sites

rasim,

Sorry about that. When I prepared the post, I clicked on Insert Special Item and chose AutoIt. I then pasted into the box. But when I previewed, the names of the included files were missing. I then keyed them in but made a mistake, i.e. I put math.au3 twice instead of math.au3 and misc.au3.

I think I have seen this problem before. Is there a bug in the forum software?

The script works AOK if you remove the ;'s ahead of #cs and #ce -- twice.

...chris


...chris

Share this post


Link to post
Share on other sites

c.haslam

For keys pressing processing use the LVN_KEYDOWN notification.

#include <GuiConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>

$hGUI = GUICreate("Test", 300, 200)

$ctlListView = GUICtrlCreateListView("Items|SubItems", 10, 10, 280, 180)

For $i = 1 To 10
    GUICtrlCreateListViewItem("Item " & $i, $ctlListView)
Next

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func _WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $hWndListView, $tNMHDR, $hWndFrom, $iCode
    
    $hWndListView = $ctlListView
    If Not IsHWnd($hWndListView) Then $hWndListView = GUICtrlGetHandle($hWndListView)
    
    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    
    Switch $hWndFrom
        Case $hWndListView
            Switch $iCode
                Case $LVN_KEYDOWN
                    $tNMLVKEYDOWN = DllStructCreate($tagNMHDR & ";short VKey;uint Flags", $lParam)
                    ConsoleWrite("Virtual key code: " & DllStructGetData($tNMLVKEYDOWN, "VKey") & @LF)
            EndSwitch
    EndSwitch
    
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

:)

Share this post


Link to post
Share on other sites

Many thanks. BTW where is $LVN_KEYDOWN defined?

...chris


...chris

Share this post


Link to post
Share on other sites

rasim,

Consider the following script. It is yours but I added to it:

#include <GuiConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
Opt("MustDeclareVars",1)

Local $hGUI = GUICreate("Test", 300, 200)

Local $ctlListView = GUICtrlCreateListView("Items|SubItems", 10, 10, 280, 180)

For $i = 1 To 10
    GUICtrlCreateListViewItem("Item " & $i, $ctlListView)
Next

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")

GUISetState()
_GUICtrlListView_SetItemSelected($ctlListView, 0)

Global $keyDown = -1,$nrow = 0
Local $nMsg

While True
    If $keyDown<>-1 Then
        If $keyDown=40 Then ; {Down} key
            If $nrow<=9 Then
                $nrow += 1
ConsoleWrite(_GUICtrlListView_GetItemText($ctlListView,$nrow)&@CRLF)
            EndIf
        EndIf
        $keyDown = -1
    Else
        $nMsg = GUIGetMsg()
        If $nMsg=$GUI_EVENT_CLOSE Then ExitLoop
    EndIf
WEnd        
GUIRegisterMsg($WM_NOTIFY, "")

Func _WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $hWndListView, $tNMHDR, $hWndFrom, $iCode
   
    $hWndListView = $ctlListView
    If Not IsHWnd($hWndListView) Then $hWndListView = GUICtrlGetHandle($hWndListView)
   
    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
   
    Switch $hWndFrom
        Case $hWndListView
            Switch $iCode
                Case $LVN_KEYDOWN
                    Local $tNMLVKEYDOWN = DllStructCreate($tagNMHDR & ";short VKey;uint Flags", $lParam)
                          $keyDown = DllStructGetData($tNMLVKEYDOWN, "VKey") 
            EndSwitch
    EndSwitch
   
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

The first press of Down Arrow does not update the UI. How do I correct this problem?

...chris


...chris

Share this post


Link to post
Share on other sites

c.haslam

Hi! Maybe this solution be easy:

#include <GuiConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListview.au3>

Global $iLastItem = -1

Local $hGUI = GUICreate("Test", 300, 200)

Local $ctlListView = GUICtrlCreateListView("Items|SubItems", 10, 10, 280, 180)

For $i = 1 To 10
    GUICtrlCreateListViewItem("Item " & $i, $ctlListView)
Next

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

Func _WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $hWndListView, $tNMHDR, $hWndFrom, $iCode

    $hWndListView = $ctlListView
    If Not IsHWnd($hWndListView) Then $hWndListView = GUICtrlGetHandle($hWndListView)

    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hWndListView
            Switch $iCode
                Case $LVN_ITEMCHANGED
                    Local $tNMLISTVIEW = DllStructCreate($tagNMLISTVIEW, $lParam)
                    Local $iItem = DllStructGetData($tNMLISTVIEW, "Item")
                    
                    If ($iItem <> -1) And ($iItem <> $iLastItem) Then ConsoleWrite(_GUICtrlListView_GetItemText($hWndFrom, $iItem) & @LF)
                    $iLastItem = $iItem
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

:)

Share this post


Link to post
Share on other sites

rasim,

Thanks.

I got your script to do what I need by inserting Send("{DOWN}") after GUISetState(). Now when the form appears, Item 1 is selected.

Now to integrate your approach into my application.

...chris


...chris

Share this post


Link to post
Share on other sites

rasim,

Thanks.

I got your script to do what I need by inserting Send("{DOWN}") after GUISetState(). Now when the form appears, Item 1 is selected.

Now to integrate your approach into my application.

...chris

Your welcome :)

Share this post


Link to post
Share on other sites

For others who may wish to use a ListView with the keyboard as well as the mouse, here is a sample script:

#include <ButtonConstants.au3>
#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <StaticConstants.au3>
#include <StructureConstants.au3>
#include <WindowsConstants.au3>

Global $iLastItem = -1
Local $nRow = 0    ; ref 1

$hGUI = GUICreate("Test", 387, 196, 193, 126)
$ctlListView = GUICtrlCreateListView("Items|SubItems", 10, 10, 280, 180)
$Button1 = GUICtrlCreateButton("Button1", 320, 24, 41, 25, 0)
$Label1 = GUICtrlCreateLabel("Label1", 328, 72, 36, 17)
$Label2 = GUICtrlCreateLabel("Label2", 327, 115, 36, 17)

For $i = 1 To 10
    GUICtrlCreateListViewItem("Item " & $i, $ctlListView)
Next

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")

GUISetState()
Send("{DOWN}")        ; select first item

While 1
    If $nRow<>0 Then
        GUICtrlSetData($Label2,"Item "&$nrow)
        $nRow = 0
    Else
        $nMsg = GUIGetMsg()
        Switch $nMsg
            Case $GUI_EVENT_CLOSE
                ExitLoop
            Case $Button1
                GUICtrlSetData($Label1,"Button")
                Sleep(2000)
                GUICtrlSetData($Label1,"")
        EndSwitch
    EndIf
WEnd

Func _WM_NOTIFY($hWnd, $Msg, $wParam, $lParam)
    Local $hWndListView, $tNMHDR, $hWndFrom, $iCode

    $hWndListView = $ctlListView
    If Not IsHWnd($hWndListView) Then $hWndListView = GUICtrlGetHandle($hWndListView)

    $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hWndListView
            Switch $iCode
                Case $LVN_ITEMCHANGED
                    Local $tNMLISTVIEW = DllStructCreate($tagNMLISTVIEW, $lParam)
                    Local $iItem = DllStructGetData($tNMLISTVIEW, "Item")
                    If ($iItem <> -1) And ($iItem <> $iLastItem) Then $nRow = $iItem + 1
                    $iLastItem = $iItem
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

Suggestions for improvements are welcome.

...chris


...chris

Share this post


Link to post
Share on other sites

Couldn't you use a guictrlsetdata to select the first item instead of using down key?


Giggity

Share this post


Link to post
Share on other sites

I tried replacing Send("{Down}") with GUICtrlSetData($ctlListView,"Item 1|").It didn't select the first item.

Is this what you were suggesting?


...chris

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

Old thread... but thought I would share my findings after many hours of frustration with slow performance. $LVN_ITEMCHANGED gives very slow performance for some reason in the ListView, many lags, and also causes bugs when pushing "Up" to scroll past the top of the ListView. I do not recommend using $LVN_ITEMCHANGED. It is much better to use:

Case $LVN_KEYDOWN
        $rowNum = _GUICtrlListView_GetSelectedIndices($hListView, False)
Edited by robertcollier4

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0