Jump to content

Recommended Posts

Posted

Here's my code that allows me to "edit text" (simulate edit) text in a ListView.

I know GUIListViewEx (from Melba23) does this (and much much more), however,

I only need the editing part and I've never been able to get a stable program with GUIListViewEx

(probably due to my bad coding style? / too many ListViews? / too many GUIs and Child GUIs?, I don't know).

 

If that can make things easier for others too.

In reality, my code opens a TextBox over the field to be edited and SetItemText the field in the ListView. (100% stable)

 

#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <MsgBoxConstants.au3>
#include <SendMessage.au3>
#include <WindowsStylesConstants.au3>

Global $hWindow


$hGUI = GUICreate("listview editing", 810, 368)
$hWindow = $hGUI

$ListView1 = GUICtrlCreateListView("Col 0|Col 1|Col 2||Col 4||Col 6|Col 7|Col 8|Col 9|Col 10", 5, 70, 800, 250)
Local $colWidths[11] = [150, 60, 125, 10, 75, 10, 150, 150, 150, 100, 100]
For $i = 0 To UBound($colWidths) - 1
    _GUICtrlListView_SetColumnWidth($ListView1, $i, $colWidths[$i])
    _GUICtrlListView_JustifyColumn($ListView1, $i, 2)
Next

; Example: Filling from an array
Local $aData[3] = ["11|21|31||51||71|81|91|101|111", "12|22|32||52||72|82|92|102|112", "13|23|33||53||73|83|93|103|113"]
For $i = 0 To UBound($aData) - 1
    GUICtrlCreateListViewItem($aData[$i], $ListView1)
Next

$hGUI2_button = GUICtrlCreateButton("$hGUI2", 704, 11, 100, 20)

GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

GUISetState(@SW_SHOW)

While 1
    $Main_MSG = GUIGetMsg()
    Switch $Main_MSG
        Case $GUI_EVENT_CLOSE
            Exit

        Case $hGUI2_button
            $hGUI2 = GUICreate("Example - $hGUI2", 605, 270, Default, Default, $WS_SYSMENU, $WS_EX_MDICHILD, $hWindow)
            $hWindow = $hGUI2

            $ListView2 = GUICtrlCreateListView("Col 0|Col 1|Col 2||Col 4||Col 5", 5, 5, 590, 230)
            Local $cols[7] = [150, 100, 150, 10, 75, 10, 67]
            For $i = 0 To UBound($cols) - 1
                _GUICtrlListView_SetColumnWidth($ListView2, $i, $cols[$i])
                _GUICtrlListView_JustifyColumn($ListView2, $i, 2)
            Next

            ; Example: Filling from an array
            Local $aData[3] = ["16|26|36||56||76", "17|27|37||57||77", "18|28|38||58||78"]
            For $i = 0 To UBound($aData) - 1
                GUICtrlCreateListViewItem($aData[$i], $ListView2)
            Next

            GUISetState(@SW_SHOW, $hWindow)

            While 1
                $hGUI2_MSG = GUIGetMsg()
                Switch $hGUI2_MSG
                    Case $GUI_EVENT_CLOSE
                        ExitLoop

                EndSwitch

                Sleep(10) ; avoid crash due to fast loop
            WEnd
            GUIDelete($hWindow)
            $hWindow = $hGUI

    EndSwitch

    Sleep(10) ; avoid crash due to fast loop
WEnd

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam, $ilParam

    Local $hWndListView, $hWndListView2

    Switch $hWindow
        Case $hGUI
            If Not IsHWnd($ListView1) Then $hWndListView = GUICtrlGetHandle($ListView1)
        Case $hGUI2
            If Not IsHWnd($ListView2) Then $hWndListView2 = GUICtrlGetHandle($ListView2)

    EndSwitch

    Local $tNMHDR = DllStructCreate($tagNMHDR, $ilParam), _
            $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom")), _
            $iCode = DllStructGetData($tNMHDR, "Code")

    Switch $hWndFrom
        Case $hWndListView ;~ $ListView1
            Switch $iCode
                Case $NM_DBLCLK
                    Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam), _
                            $iIndex = DllStructGetData($tInfo, "Index"), _
                            $iSubItem = DllStructGetData($tInfo, "SubItem")

                    If $iIndex > -1 Then
                        Switch $iSubItem
                            Case 0, 1, 2, 4, 6, 8
                                Local $before = _GUICtrlListView_GetItemText($ListView1, $iIndex, $iSubItem)
                                Local $after = CustomInputBox($ListView1, $before, $iIndex, $iSubItem)

                                If $after <> "Cancel" Then
                                    _edited($ListView1, $iIndex, $iSubItem, $before, $after)
                                Else
                                    _canceled($ListView1, $iIndex, $iSubItem, $before, $after)
                                EndIf

                            Case 3, 5, 7, 9, 10
                                MsgBox("", "", "Column " & $iSubItem & " not editable !", 0, $hWindow)

                        EndSwitch
                    EndIf
            EndSwitch

        Case $hWndListView2 ;~ $ListView2
            Switch $iCode
                Case $NM_DBLCLK
                    Local $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam), _
                            $iIndex = DllStructGetData($tInfo, "Index"), _
                            $iSubItem = DllStructGetData($tInfo, "SubItem")

                    If $iIndex > -1 Then
                        Switch $iSubItem
                            Case 0, 1, 4, 6
                                Local $before = _GUICtrlListView_GetItemText($ListView2, $iIndex, $iSubItem)
                                Local $after = CustomInputBox($ListView2, $before, $iIndex, $iSubItem)

                                If $after <> "Cancel" Then
                                    _edited($ListView2, $iIndex, $iSubItem, $before, $after)
                                Else
                                    _canceled($ListView2, $iIndex, $iSubItem, $before, $after)
                                EndIf

                            Case 2, 3, 5
                                MsgBox("", "", "Column " & $iSubItem & " not editable !", 0, $hWindow)

                        EndSwitch
                    EndIf
            EndSwitch

    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

Func _edited($hListView, $row, $column, $before, $after)
    If Not IsHWnd($hListView) Then $hListView = GUICtrlGetHandle($hListView)

    Switch $hListView
        Case GUICtrlGetHandle($ListView1)
            _GUICtrlListView_BeginUpdate($ListView1)
            _GUICtrlListView_SetItemText($ListView1, $row, $after, $column)
            _GUICtrlListView_EndUpdate($ListView1)
            MsgBox("", "", "Enter pressed in $ListView1 !")

        Case GUICtrlGetHandle($ListView2)
            _GUICtrlListView_BeginUpdate($ListView2)
            _GUICtrlListView_SetItemText($ListView2, $row, $after, $column)
            _GUICtrlListView_EndUpdate($ListView2)
            MsgBox("", "", "Enter pressed in $ListView2 !")

    EndSwitch
EndFunc   ;==>_edited

Func _canceled($hListView, $row, $column, $before, $after)
    If Not IsHWnd($hListView) Then $hListView = GUICtrlGetHandle($hListView)

    Switch $hListView
        Case GUICtrlGetHandle($ListView1)
            MsgBox("", "", "Esc pressed in $ListView1 !")

        Case GUICtrlGetHandle($ListView2)
            MsgBox("", "", "Esc pressed in $ListView2 !")

    EndSwitch
EndFunc   ;==>_canceled

Func CustomInputBox($listview, $hBefore, $iIndex, $iSubItem)
    GuiDisableEnable($hWindow, 0, 35)

    Local $iXstart, $iYend ; <----- offset for the inputbox
    Switch $listview
        Case $ListView1
;~ how to calculate : $ListView1 = GUICtrlCreateListView("Col 0|Col 1|...|Col 10", 5, 70, 800, 250)
;~     $iXstart = The left side of the control. (5)
;~     $iYend = The top of the control. 70 - 22 = 48
;~          ** 20 is the listview item height but its slighty to high so 22 is a perfect number.. but its a case by case (listview by listview)..

            $iXstart = 5
            $iYend = 48

        Case $ListView2
;~ how to calculate : $ListView2 = GUICtrlCreateListView("Col 0|Col 1|...|Col 5", 5, 5, 590, 230)
;~     $iXstart = The left side of the control. (5)
;~     $iYend = The top of the control. 5 - 22 = -17

            $iXstart = 5
            $iYend = -17

    EndSwitch

    ; Ensure handle
    If Not IsHWnd($listview) Then $listview = GUICtrlGetHandle($listview)

    ; Validate indexes
    Local $iCols = _GUICtrlListView_GetColumnCount($listview)
    If $iSubItem >= $iCols Then Return ""

    Local $iItems = _GUICtrlListView_GetItemCount($listview)
    If $iIndex >= $iItems Then Return ""

    ; Compute X offset by summing column widths
    Local $iXOffset = 0
    For $i = 0 To $iSubItem - 1
        $iXOffset += _GUICtrlListView_GetColumnWidth($listview, $i)
    Next

    ; Compute Y offset by summing column widths
    Local $iTopIndex = _GUICtrlListView_GetTopIndex($listview)
    $iYOffset = ($iIndex - $iTopIndex) * 19

    ; Column width
    Local $iWidth = _GUICtrlListView_GetColumnWidth($listview, $iSubItem)

    ; =========================
    ; CONVERT TO SCREEN COORDS
    ; =========================

    ; Get ListView position (client coords)
    Local $hGUI = _WinAPI_GetParent($listview)
    Local $aLVPos = ControlGetPos($hGUI, "", $listview)
    If @error Then Return ""

    ; Scroll offset
    Local $aOrigin = _GUICtrlListView_GetOrigin($listview)
    Local $iScrollX = $aOrigin[0]

    ; Position relative to GUI client
    Local $iX = $aLVPos[0] + $iXOffset - $iScrollX - $iXstart
    Local $iY = $aLVPos[1] + $iYOffset - $iYend

    ; Convert to SCREEN coordinates (THIS is the key)
    Local $tPoint = DllStructCreate("int X;int Y")
    DllStructSetData($tPoint, "X", $iX)
    DllStructSetData($tPoint, "Y", $iY)

    _WinAPI_ClientToScreen($listview, $tPoint)

    $iX = DllStructGetData($tPoint, "X")
    $iY = DllStructGetData($tPoint, "Y")

    ; Small padding (visual polish)
    $iX += 1
    $iY += 1

    Local $CustomInputBoxhGUI = GUICreate("", $iWidth, 20, $iX, $iY, $WS_POPUP, $WS_EX_TOPMOST)
    GUISetBkColor(0xFFFFFF)

    Local $hInput = GUICtrlCreateInput($hBefore, 0, 0, $iWidth, 20)

    Local $hOkButton = GUICtrlCreateDummy()
    Local $iCLOSE = GUICtrlCreateDummy()

    Dim $AccelKeysF3[2][2] = [["{ENTER}", $hOkButton], ["{ESC}", $iCLOSE]]
    GUISetAccelerators($AccelKeysF3, $CustomInputBoxhGUI)

    GUISetState(@SW_SHOW)
    WinSetOnTop($CustomInputBoxhGUI, "", 1)
    ControlFocus($CustomInputBoxhGUI, "", $hInput)
    _SendMessage(GUICtrlGetHandle($hInput), $EM_SETSEL, 0, -1)

    Local $Msg, $input_result = ""

    While 1
        $Msg = GUIGetMsg()
        Switch $Msg
            Case $GUI_EVENT_CLOSE, $iCLOSE
                GUIDelete($CustomInputBoxhGUI)
                GUISetAccelerators("")
                GuiDisableEnable($hWindow, 1)
                Return "Cancel"

            Case $hOkButton
                $input_result = GUICtrlRead($hInput)
                GUIDelete($CustomInputBoxhGUI)
                ExitLoop
        EndSwitch

        Sleep(10) ; avoid crash due to fast loop
    WEnd

    GUISetAccelerators("")
    GuiDisableEnable($hWindow, 1)

    Return $input_result
EndFunc   ;==>CustomInputBox

Func GuiDisableEnable($hGUI2, $sate, $br = 25, $DisableMenu = 1, $bColor = 0x000000)
    Local Static $avGUI[1] = [0]

    Switch $sate
        Case 0
            If $DisableMenu = 1 Then GuiMenuDisable($hGUI2, 0)
            $vGUI = _GUIDisable($hGUI2, 0, $br, $bColor)
            GUISetState(@SW_DISABLE, $vGUI)
            _ArrayAdd($avGUI, $vGUI)
            $avGUI[0] += 1

            If $DisableMenu = 1 Then GUISetState(@SW_DISABLE, $hGUI2)
        Case 1
            If $DisableMenu = 1 Then GuiMenuDisable($hGUI2, 1)
            $MaxVGui = UBound($avGUI) - 1
            If $MaxVGui = 0 Then Return
            _GUIDisable($avGUI[$MaxVGui], 1)
            _ArrayDelete($avGUI, $MaxVGui)
            $avGUI[0] -= 1

            If $DisableMenu = 1 Then GUISetState(@SW_ENABLE, $hGUI2)
            WinActivate(WinGetHandle($hGUI2))
    EndSwitch
EndFunc   ;==>GuiDisableEnable

;~ #Region Internal use
Func GuiMenuDisable($hWnd, $sate)
    Local $dSysMenu = DllCall("User32.dll", "hwnd", "GetSystemMenu", "hwnd", $hWnd, "int", $sate)
    DllCall("User32.dll", "int", "RemoveMenu", "hwnd", $dSysMenu[0], "int", 0xF060, "int", $sate)
    DllCall("User32.dll", "int", "DrawMenuBar", "hwnd", $hWnd)
EndFunc   ;==>GuiMenuDisable

Func _GUIDisable($hWnd = -1, $iDelete = 0, $iBrightness = Default, $bColor = 0x000000)
    Local $vGUI = 0

    If $iBrightness = Default Then $iBrightness = 5

    If $hWnd = -1 Then
        Local $iLabel = GUICtrlCreateLabel('', -99, -99, 1, 1)
        $hWnd = _WinAPI_GetParent(GUICtrlGetHandle($iLabel))
        If @error Then Return SetError(1, 0 * GUICtrlDelete($iLabel), 0)
        GUICtrlDelete($iLabel)
    EndIf

    If $iDelete Then
        $vGUI = GUIDelete($hWnd)
    Else
        Local $aWinGetPos = WinGetClientSize($hWnd)

        $vGUI = GUICreate('', $aWinGetPos[0], $aWinGetPos[1], 0, 0, $WS_POPUP, $WS_EX_MDICHILD, $hWnd)
        GUISetBkColor($bColor, $vGUI)
        WinSetTrans($vGUI, '', Round($iBrightness * (255 / 100)))
        GUISetState(@SW_SHOW, $vGUI)
    EndIf

    Return $vGUI
EndFunc   ;==>_GUIDisable

 

$before and $after represent the value before and after the modification (if you need them).

 

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
×
×
  • Create New...