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
728

被折叠的 条评论
为什么被折叠?



