Что нового

Как правильно реализовать контекстное меню в TreeView

pvnn

Осваивающий
Сообщения
305
Репутация
32
Нижепреведенный код работает, но не очень корректно.
Нельзя правой клавишей мыши выделять пункты TreeView.
То есть, приходится сначала нажать левой клавишей на интересующий пункт TreeView, а уже потом применить к нему контекстное меню. Иначе контекстное меню будет применяться к последнему выделенному пунктуTreeView.

Как можно решить эту проблему?

Код:
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>

$iStyle = BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS)

$hForm1 = GUICreate("Form1", 400, 436, 123, 130)
$hTreeView = _GUICtrlTreeView_Create($hForm1, 8, 8, 225, 420, $iStyle, $WS_EX_CLIENTEDGE)


$hItem1 = _GUICtrlTreeView_Add($hTreeView, 0, 'Parent')
_GUICtrlTreeView_AddFirst($hTreeView, 0, 'Parent First')
_GUICtrlTreeView_AddChild($hTreeView, $hItem1, 'str1')
_GUICtrlTreeView_AddChild($hTreeView, $hItem1, 'str2')
_GUICtrlTreeView_Expand($hTreeView, $hItem1) ; Развернуть пункт

; ---- Контекстное Меню для TreeView
 $DummyMenu =   GUICtrlCreateDummy()
 $ContextMenu = GUICtrlCreateContextMenu($DummyMenu)
 $ContextMenu1 =  GUICtrlCreateMenuItem("Строка1", $ContextMenu)
 ;$ContextMenu2 =  GUICtrlCreateMenuItem("Строка2", $ContextMenu)

GUISetState()
GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
		Case $ContextMenu1
			$hItem=_GUICtrlTreeView_GetSelection($hTreeView)
            $ItemText=_GUICtrlTreeView_GetText($hTreeView,$hItem)
            MsgBox(64,'',$ItemText)
    EndSwitch
WEnd



; Show a menu in a given GUI window which belongs to a given GUI ctrl
Func ShowMenu($hWnd, $nContextID, $nContextControlID, $iMouse=0)
    Local $hMenu = GUICtrlGetHandle($nContextID)
    Local $iCtrlPos = ControlGetPos($hWnd, "", $nContextControlID)

    Local $X = $iCtrlPos[0]
    Local $Y = $iCtrlPos[1] + $iCtrlPos[3]

    ClientToScreen($hWnd, $X, $Y)

    If $iMouse Then
        $X = MouseGetPos(0)
        $Y = MouseGetPos(1)
    EndIf

    DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $X, "int", $Y, "hwnd", $hWnd, "ptr", 0)
EndFunc

; Convert the client (GUI) coordinates to screen (desktop) coordinates
Func ClientToScreen($hWnd, ByRef $x, ByRef $y)
    Local $stPoint = DllStructCreate("int;int")

    DllStructSetData($stPoint, 1, $x)
    DllStructSetData($stPoint, 2, $y)

    DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $hWnd, "ptr", DllStructGetPtr($stPoint))
    $x = DllStructGetData($stPoint, 1)
    $y = DllStructGetData($stPoint, 2)
    ; release Struct not really needed as it is a local
    $stPoint = 0
EndFunc

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
	#forceref $hWnd, $iMsg, $iwParam
    Local $hWndFrom,  $iCode, $tNMHDR, $hWndTreeView, $hWndListView, $tInfo

	; Контекстное меню Для TreeView
	$hWndTreeView = $hTreeView
    If Not IsHWnd($hTreeView) Then $hWndTreeView = GUICtrlGetHandle($hTreeView)

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

    Switch $hWndFrom
        Case $hWndTreeView
            Switch $iCode
				Case $NM_RCLICK ; Контекстное меню
                    $tInfo = DllStructCreate($tagNMITEMACTIVATE, $ilParam)
                    $iIndex = DllStructGetData($tInfo, "Index")
                    If $iIndex <> -1 Then
                        $iLast_LV_Index = $iIndex
                        ShowMenu($hWnd, $ContextMenu, $hTreeView, 1)
                    EndIf
				Case $NM_CLICK
			EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc
 

gregaz

AutoIT Гуру
Сообщения
1,166
Репутация
299
pvnn [?]
ак можно решить эту проблему?

Выделением элемента при правом клике в ф-ии WM_NOTIFY :
Код:
; ..........................
_GUICtrlTreeView_SelectItem($hWndFrom, $hItemTV)




Добавлено:
Сообщение автоматически объединено:

Судя по всему автор не понял.
Зачем применять для TreeView то, что предназначено для ListView ?

Это для TreeView :
Код:
Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam
    Local $hWndFrom,  $iCode, $tNMHDR, $hWndTreeView, $hWndListView, $tInfo

    ; Контекстное меню Для TreeView
    $hWndTreeView = $hTreeView
    If Not IsHWnd($hTreeView) Then $hWndTreeView = GUICtrlGetHandle($hTreeView)

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

    Switch $hWndFrom
        Case $hWndTreeView
            Switch $iCode
                Case $NM_RCLICK ; Контекстное меню
					$tPoint = _WinAPI_GetMousePos(1, $hWndFrom)
					$tTVHTI = _GUICtrlTreeView_HitTestEx($hWndFrom, DllStructGetData($tPoint, 1, 1), DllStructGetData($tPoint, 2))
					$hItemTV =DllStructGetData($tTVHTI, 'Item')
					_GUICtrlTreeView_SelectItem($hWndFrom, $hItemTV)
					ShowMenu($hWnd, $ContextMenu, $hTreeView, 1)
                Case $NM_CLICK
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
pvnn
Сделал пример на основе одной из моих программ, потом может ещё улучшу.

Код:
#NoTrayIcon
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiTreeView.au3>

Opt("GUIResizeMode", 802) ; не перемещать элементы

; En
$LngTitle = 'Context menu, TreeView'
$LngPath = 'Path'
$LngInd = 'Index'
$LngDel = 'Delete'
$LngItm = 'Item'

; Ru
; если русская локализация, то русский язык
If @OSLang = 0419 Then
	$LngTitle = 'Контекстное меню TreeView'
	$LngPath = 'Путь из имён'
	$LngInd = 'Путь из индексов'
	$LngDel = 'Удалить'
	$LngItm = 'Пункт'
EndIf

$hGui = GUICreate($LngTitle, 350, 375, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_OVERLAPPEDWINDOW, $WS_CLIPCHILDREN))

$iTreeView = GUICtrlCreateTreeView(5, 5, 200, 340, -1, $WS_EX_CLIENTEDGE + $WS_EX_COMPOSITED)
GUICtrlSetResizing(-1, 256 + 64 + 32 + 2)
$hTreeView = GUICtrlGetHandle(-1)

; Создаём пункты дерева
For $i = 0 To 10
	$tmp = GUICtrlCreateTreeViewItem($LngItm & ' ' & $i, $iTreeView)
	For $x = 0 To 3
		GUICtrlCreateTreeViewItem($LngItm & ' ' & $i & '_' & $x, $tmp)
	Next
Next

$iDummy = GUICtrlCreateDummy()

$ContMenu = GUICtrlCreateContextMenu($iDummy)
$hMenu = GUICtrlGetHandle($ContMenu)
$iCM_Path = GUICtrlCreateMenuItem($LngPath & @TAB & 'Enter', $ContMenu)
$iCM_Ind = GUICtrlCreateMenuItem($LngInd & @TAB & 'Ctrl+Enter', $ContMenu)
$iCM_Del = GUICtrlCreateMenuItem($LngDel & @TAB & 'Ctrl+Del', $ContMenu)

Dim $AccelKeys[3][2] = [["{Enter}", $iDummy],["^{DEL}", $iCM_Del],["^{Enter}", $iCM_Ind]] ; установка горячих клавиш на пункты контекстного меню
GUISetAccelerators($AccelKeys)

GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")

GUISetState()
While 1
	Switch GUIGetMsg()
		Case $iCM_Path
			_OpenExplorer()
		Case $iCM_Del
			$hItem = _GUICtrlTreeView_GetSelection($hTreeView)
			_GUICtrlTreeView_Delete($hTreeView, $hItem)
		Case $iCM_Ind
			MsgBox(0, 'Сообщение', ControlTreeView($hGui, '', 'SysTreeView321', 'GetSelected', 1), 0, $hGui)
		Case $iDummy ; Событие по кнопке Enter, и действие взависимости от того, какой элемент в фокусе
			; MsgBox(0, '', ControlGetFocus($hGui), 0, $hGui)
			Switch ControlGetFocus($hGui)
				; Case 'Edit1'
				; _Read()
				; Case 'Edit4', 'Edit2', 'Edit3'
				; _Add()
				; Case 'SysListView321'
				; _OpenExplorer()
				Case 'SysTreeView321'
					_OpenExplorer()
			EndSwitch
		Case $GUI_EVENT_CLOSE
			Exit
	EndSwitch
WEnd

Func _OpenExplorer()
	$ind = ControlTreeView($hGui, '', 'SysTreeView321', 'GetSelected')
	MsgBox(0, 'Сообщение', $ind, 0, $hGui)
EndFunc   ;==>_OpenExplorer

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
	Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR

	$tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
	$hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
	$iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
	$iCode = DllStructGetData($tNMHDR, "Code")
	Switch $hWndFrom
		Case $hTreeView
			Switch $iCode
				Case $NM_RCLICK ; Вызов контекстного меню правой кнопкой мыши
					Local $tMPos = _WinAPI_GetMousePos(True, $hWndFrom) ; координаты относительно элемента
					Local $hItem = _GUICtrlTreeView_HitTestItem($hWndFrom, DllStructGetData($tMPos, 1), DllStructGetData($tMPos, 2)) ; получаем дескриптор пункта
					If $hItem > 0 Then ; проверка, чтобы вызов был на пункте
						$x = MouseGetPos(0)
						$y = MouseGetPos(1)
						_GUICtrlTreeView_SelectItem($hTreeView, $hItem)
						DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $x, "int", $y, "hwnd", $hGui, "ptr", 0) ; заставляет появится меню в указанных координатах
					EndIf
			EndSwitch
	EndSwitch
	Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY
 
Автор
P

pvnn

Осваивающий
Сообщения
305
Репутация
32
gregaz, да код изначально был для ListView, никак не мог его корректно переделать для TreeView. Спасибо за помощь, вы мне очень помогли!

AZJIO классный пример, тоже возьму его себе на вооружение. Большое спасибо!

Всем большое спасибо за оперативность в решении проблемы
 
Верх