ListView+Menu结合的随笔

ListView+Menu结合的子类化处理的随笔

Code:

#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include <WinAPIProc.au3>
#include <WinAPISys.au3>
#include <Misc.au3> ; 用于键盘检测

Global $g_hListView, $g_pOriginalListViewProc, $g_hMenu, $g_aLastHitTest[2]
Global $g_iMenuHeight = 150 ; 下拉菜单高度
Global $g_hListViewCallback ; 存储回调句柄

; 创建主界面
Local $hGUI = GUICreate("ListView双击弹出下拉菜单 - 完美实现", 600, 400)
$g_hListView = GUICtrlCreateListView("ID|产品名称|价格|库存|状态", 10, 10, 580, 380)
_GUICtrlListView_SetExtendedListViewStyle($g_hListView, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER))

; 设置列宽
_GUICtrlListView_SetColumnWidth($g_hListView, 0, 50)
_GUICtrlListView_SetColumnWidth($g_hListView, 1, 200)
_GUICtrlListView_SetColumnWidth($g_hListView, 2, 80)
_GUICtrlListView_SetColumnWidth($g_hListView, 3, 80)
_GUICtrlListView_SetColumnWidth($g_hListView, 4, 80)

; 添加示例数据
For $i = 1 To 25
    Local $sPrice = Random(10, 100, 1) & ".99"
    Local $iStock = Random(5, 50, 1)
    Local $sStatus = ($iStock > 20) ? "充足" : (($iStock > 10) ? "正常" : "短缺")
    GUICtrlCreateListViewItem($i & "|产品 " & $i & "|" & $sPrice & "|" & $iStock & "|" & $sStatus, $g_hListView)
Next

; 创建下拉菜单(使用按钮)
$g_hMenu = GUICreate("", 200, $g_iMenuHeight, -1, -1, $WS_POPUP, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST), $hGUI)
GUISetBkColor(0xF0F0F0) ; 设置背景色

; 创建菜单项按钮
Global $g_aMenuItems[6] ; 存储菜单项按钮ID
Local $aMenuText = StringSplit("编辑项目|复制项目|删除项目|导出数据|打印标签|发送通知", "|")
Local $iBtnHeight = 25
Local $iBtnWidth = 200

; 创建菜单按钮
For $i = 0 To UBound($aMenuText) - 2
    $g_aMenuItems[$i] = GUICtrlCreateButton($aMenuText[$i+1], 0, $i * $iBtnHeight, $iBtnWidth, $iBtnHeight)
    GUICtrlSetBkColor(-1, 0xF0F0F0) ; 设置按钮背景色
    GUICtrlSetColor(-1, 0x000000)   ; 设置文本颜色

    ; 添加悬停效果 - 使用简单方法
    GUICtrlSetCursor($g_aMenuItems[$i], 0) ; 手型光标

    ; 添加悬停效果
;~     GUICtrlSetOnHover($g_aMenuItems[$i], "_ButtonHover", "_ButtonLeave")
Next

GUISwitch($hGUI) ; 切换回主GUI
GUISetState(@SW_HIDE, $g_hMenu) ; 隐藏菜单

; 子类化ListView
SubclassListView($g_hListView)

; 添加状态栏
Global $g_hStatusBar = _GUICtrlStatusBar_Create($hGUI)
_GUICtrlStatusBar_SetText($g_hStatusBar, "就绪 - 双击任何单元格弹出菜单")

GUISetState(@SW_SHOW, $hGUI)

; 主消息循环
While 1
    $iMsg = GUIGetMsg()

    Switch $iMsg
        Case $GUI_EVENT_CLOSE
            ExitLoop

        ; 处理菜单项按钮点击
        Case $g_aMenuItems[0] To $g_aMenuItems[UBound($g_aMenuItems)-1]
            ; 获取点击的按钮索引
            $iIndex = $iMsg - $g_aMenuItems[0]

            ; 获取按钮文本
            $sSelected = GUICtrlRead($iMsg)

            ; 获取最后双击的项
            Local $iItem = $g_aLastHitTest[0]
            Local $iSubItem = $g_aLastHitTest[1]

            ; 显示操作信息
            Local $sItemText = _GUICtrlListView_GetItemText($g_hListView, $iItem, 0)
            Local $sSubItemText = _GUICtrlListView_GetItemText($g_hListView, $iItem, $iSubItem)
            Local $sColumnName = _GUICtrlListView_GetColumn($g_hListView, $iSubItem)

            ; 状态栏反馈
            _GUICtrlStatusBar_SetText($g_hStatusBar, "执行操作: " & $sSelected & " | 项目: " & $sItemText & " | 列: " & $sColumnName[5] & " | 值: " & $sSubItemText)

            ; 隐藏菜单
            GUISetState(@SW_HIDE, $g_hMenu)

        ; 点击窗口其他位置关闭菜单
        Case Else
            ; ESC键关闭菜单
            If _IsPressed("1B") Then ; ESC键
                GUISetState(@SW_HIDE, $g_hMenu)
                ; 等待按键释放
                While _IsPressed("1B")
                    Sleep(10)
                WEnd
            EndIf

            If WinGetState($g_hMenu) = @SW_SHOW Then
                ; 获取鼠标位置
                Local $aMP = GUIGetCursorInfo($hGUI)
                If Not IsArray($aMP) Then ContinueLoop

                ; 获取菜单位置
                Local $aMenuPos = WinGetPos($g_hMenu)
                If Not IsArray($aMenuPos) Then ContinueLoop

                ; 检查点击是否在菜单外部
                If $aMP[0] < $aMenuPos[0] Or $aMP[0] > $aMenuPos[0] + $aMenuPos[2] Or _
                   $aMP[1] < $aMenuPos[1] Or $aMP[1] > $aMenuPos[1] + $aMenuPos[3] Then
                    GUISetState(@SW_HIDE, $g_hMenu)
                EndIf
            EndIf
    EndSwitch
WEnd

; 清理
_WinAPI_SetWindowLong(GUICtrlGetHandle($g_hListView), $GWL_WNDPROC, $g_pOriginalListViewProc)
DllCallbackFree($g_hListViewCallback)
GUIDelete($hGUI)
GUIDelete($g_hMenu)

; ================ 全局变量 ================
; Global $g_aLastHitTest[2] ; 存储最后点击的位置 [行, 列]

; ================ 自定义函数 ================

; ListView子类化处理函数
Func ListViewSubclassProc($hWnd, $iMsg, $wParam, $lParam)
    Switch $iMsg
        Case $WM_LBUTTONDBLCLK ; 双击消息
            ; 获取点击位置
            Local $iX = BitAND($lParam, 0xFFFF)
            Local $iY = BitShift($lParam, 16)

            ; 创建LVHITTESTINFO结构
            Local $tLVHITTESTINFO = DllStructCreate("int X;int Y;uint Flags;int Item;int SubItem")
            DllStructSetData($tLVHITTESTINFO, "X", $iX)
            DllStructSetData($tLVHITTESTINFO, "Y", $iY)

            ; 发送LVM_SUBITEMHITTEST消息
            Local $iResult = _SendMessage($hWnd, $LVM_SUBITEMHITTEST, 0, DllStructGetPtr($tLVHITTESTINFO))

            ; 保存点击信息
            $g_aLastHitTest[0] = DllStructGetData($tLVHITTESTINFO, "Item") ; 行索引
            $g_aLastHitTest[1] = DllStructGetData($tLVHITTESTINFO, "SubItem") ; 列索引

            ; 如果点击有效项
            If $g_aLastHitTest[0] >= 0 Then
                ; 获取项的位置
                Local $aItemRect = _GUICtrlListView_GetSubItemRect($hWnd, $g_aLastHitTest[0], $g_aLastHitTest[1])

                ; 转换为屏幕坐标
                Local $aPos = WinGetPos($hWnd)
                Local $iXPos = $aPos[0] + $aItemRect[0] + ($aItemRect[2] - $aItemRect[0]) / 2
                Local $iYPos = $aPos[1] + $aItemRect[3] + 5

                ; 显示菜单
                ShowDropDownMenu($iXPos, $iYPos)

                ; 阻止默认的双击行为
                Return 0
            EndIf
    EndSwitch

    ; 调用原始窗口过程
    Return _WinAPI_CallWindowProc($g_pOriginalListViewProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc

; 显示下拉菜单
Func ShowDropDownMenu($iX, $iY)
    ; 获取屏幕高度
    Local $iScreenHeight = @DesktopHeight
    Local $iScreenWidth = @DesktopWidth

    ; 调整菜单位置(防止超出屏幕)
    Local $iMenuX = $iX - 100
    Local $iMenuY = $iY

    ; 如果菜单底部超出屏幕,则向上弹出
    If ($iMenuY + $g_iMenuHeight) > $iScreenHeight Then
        $iMenuY = $iScreenHeight - $g_iMenuHeight - 20
    EndIf

    ; 如果菜单右侧超出屏幕,则向左调整
    If ($iMenuX + 200) > $iScreenWidth Then
        $iMenuX = $iScreenWidth - 220
    EndIf

    ; 设置菜单位置
    WinMove($g_hMenu, "", $iMenuX, $iMenuY)

    ; 显示菜单
    GUISetState(@SW_SHOW, $g_hMenu)

    ; 激活菜单窗口
    WinActivate($g_hMenu)

    ; 状态栏提示
    _GUICtrlStatusBar_SetText($g_hStatusBar, "菜单已显示 - 选择操作或按ESC取消")
EndFunc

; 子类化ListView控件
Func SubclassListView($iCtrlID)
    Local $hListView = GUICtrlGetHandle($iCtrlID)

    ; 注册回调函数
    $g_hListViewCallback = DllCallbackRegister("ListViewSubclassProc", "ptr", "hwnd;uint;wparam;lparam")

    ; 替换窗口过程
    $g_pOriginalListViewProc = _WinAPI_SetWindowLong($hListView, $GWL_WNDPROC, DllCallbackGetPtr($g_hListViewCallback))

    If @error Then
        MsgBox(16, "错误", "ListView子类化失败!")
        Exit
    EndIf
EndFunc

; 按钮悬停效果
Func _ButtonHover($iCtrlID)
    GUICtrlSetBkColor($iCtrlID, 0x0078D7) ; 悬停时蓝色背景
    GUICtrlSetColor($iCtrlID, 0xFFFFFF)   ; 白色文字
EndFunc

Func _ButtonLeave($iCtrlID)
    GUICtrlSetBkColor($iCtrlID, 0xF0F0F0) ; 恢复背景色
    GUICtrlSetColor($iCtrlID, 0x000000)   ; 恢复文字颜色
EndFunc

; 自定义OnHover函数
Func GUICtrlSetOnHover($iControlID, $sEnterFunction, $sLeaveFunction)
    Local $hControl = GUICtrlGetHandle($iControlID)
    If $hControl = 0 Then Return

    Local $hParent = _WinAPI_GetParent($hControl)
    Local $iOldProc = _WinAPI_GetWindowLong($hParent, $GWL_WNDPROC)

    ; 注册回调
    Local $hCallback = DllCallbackRegister("_HoverProc", "ptr", "hwnd;uint;wparam;lparam")
    _WinAPI_SetWindowLong($hParent, $GWL_WNDPROC, DllCallbackGetPtr($hCallback))

    ; 保存信息
    _WinAPI_SetProp($hParent, "OldProc", $iOldProc)
    _WinAPI_SetProp($hParent, "Callback", $hCallback)
    _WinAPI_SetProp($hParent, "ControlID", $iControlID)
    _WinAPI_SetProp($hParent, "EnterFunc", $sEnterFunction)
    _WinAPI_SetProp($hParent, "LeaveFunc", $sLeaveFunction)
    _WinAPI_SetProp($hParent, "HoverState", 0)
EndFunc

Func _HoverProc($hWnd, $iMsg, $wParam, $lParam)
    Local $iOldProc = _WinAPI_GetProp($hWnd, "OldProc")
    Local $iControlID = _WinAPI_GetProp($hWnd, "ControlID")
    Local $sEnterFunc = _WinAPI_GetProp($hWnd, "EnterFunc")
    Local $sLeaveFunc = _WinAPI_GetProp($hWnd, "LeaveFunc")
    Local $iHoverState = _WinAPI_GetProp($hWnd, "HoverState")

    Switch $iMsg
        Case $WM_MOUSEMOVE
            Local $hControl = GUICtrlGetHandle($iControlID)
            Local $aMP = GUIGetCursorInfo($hWnd)
            Local $aCP = ControlGetPos($hWnd, "", $iControlID)

            If IsArray($aMP) And IsArray($aCP) Then
                ; 检查鼠标是否在控件上
                Local $bOnControl = ($aMP[0] >= $aCP[0] And $aMP[0] <= $aCP[0] + $aCP[2] And _
                                     $aMP[1] >= $aCP[1] And $aMP[1] <= $aCP[1] + $aCP[3])

                If $bOnControl And Not $iHoverState Then
                    Call($sEnterFunc, $iControlID)
                    _WinAPI_SetProp($hWnd, "HoverState", 1)
                ElseIf Not $bOnControl And $iHoverState Then
                    Call($sLeaveFunc, $iControlID)
                    _WinAPI_SetProp($hWnd, "HoverState", 0)
                EndIf
            EndIf
    EndSwitch

    Return _WinAPI_CallWindowProc($iOldProc, $hWnd, $iMsg, $wParam, $lParam)
EndFunc

; ==============================================================================
; 自定义 WinAPI 函数(替代 WinAPIEx.au3 中的功能)
; ==============================================================================

Func _WinAPI_SetProp($hWnd, $sName, $vData)
    ; 使用 DllCall 直接调用 Windows API
    Local $aRet = DllCall("user32.dll", "bool", "SetPropW", _
        "hwnd", $hWnd, _
        "wstr", $sName, _
        "handle", $vData)

    If @error Then Return SetError(@error, @extended, False)
    Return $aRet[0]
EndFunc

Func _WinAPI_GetProp($hWnd, $sName)
    ; 获取属性值
    Local $aRet = DllCall("user32.dll", "handle", "GetPropW", _
        "hwnd", $hWnd, _
        "wstr", $sName)

    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc

Func _WinAPI_RemoveProp($hWnd, $sName)
    ; 移除属性
    Local $aRet = DllCall("user32.dll", "handle", "RemovePropW", _
        "hwnd", $hWnd, _
        "wstr", $sName)

    If @error Then Return SetError(@error, @extended, 0)
    Return $aRet[0]
EndFunc

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值