Что нового

Выбор нескольких вариантов из выпадающего списка

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Доброго времени суток. Пробую создать выпадающий список, чтобы была возможность множественного выбора. Тоесть чтобы можно было выбрать несколько пунктов из выпадающего списка. Но пока у меня ничего не получается. На форуме нашел похожую тему https://autoit-script.ru/threads/gu...kix-variantov-iz-vypadajuschego-spiska.23324/
Хорошая штука, но интересно, есть выпадающие списки на основе GUICtrlCreateCombo или GUICtrlCreateCheckbox или нет?
Сообщение автоматически объединено:

Пробую на основе этого примера из меню сделать выпадающий комбосписок. Там функция-пример Example2() с кнопками. Делаю так:
Код:
#include <GUIConstantsEx.au3>
#include <ButtonConstants.au3>

#include <GUIConstantsEx.au3>
#include <ComboConstants.au3>
#include <WindowsConstants.au3>

; Opt('MustDeclareVars', 1) ; принуждает объявлять все переменные

Global $hGui = GUICreate("Чекбоксы из меню", 400, 400)

Global $hCombo = GUICtrlCreateCombo("Выбрать пункт", 10, 10, 100, 0,  $BS_FLAT) ; Если убрать $BS_FLAT, то остается только один пункт выпадающего списка. А с ним список заданного размера, при нажатии на который появляются пункты меню.

    ; Сначала создан элемент - пустышка (Dummy) и контекстное меню для него, которое будет пристроено к выпадающему списку
Global $OptionsDummy = GUICtrlCreateDummy()
;Создаем контекстное меню
Global $OptionsContext = GUICtrlCreateContextMenu($OptionsDummy)
; Создаем пункты меню
Global $Opt1 = GUICtrlCreateMenuItem("Пункт1", $OptionsContext)
;GUICtrlSetState(-1, $GUI_CHECKED) ; Устанавливаем чекбокс по умолчанию выбранным для элемента
Global $Opt2 = GUICtrlCreateMenuItem("Пункт2", $OptionsContext)
Global $Opt3 = GUICtrlCreateMenuItem("Пункт3", $OptionsContext)
Global $Opt4 = GUICtrlCreateMenuItem("Пункт4", $OptionsContext)
GUICtrlSetState(-1, $GUI_CHECKED)
Global $Opt5 = GUICtrlCreateMenuItem("Пункт5", $OptionsContext)
Global $Opt6 = GUICtrlCreateMenuItem("Пункт6", $OptionsContext)
Global $Opt7 = GUICtrlCreateMenuItem("Пункт7", $OptionsContext)

    GUISetState()

   While 1
   Local $msg = GUIGetMsg()

      Switch $msg
      Case $GUI_EVENT_CLOSE
            ExitLoop
      Case $hCombo
            ShowMenu($hGui, $msg, $OptionsContext)

   EndSwitch
   WEnd
    GUIDelete()

Но как бы не совсем тот результат, что хотелось бы. Где и что не так я делаю?
Сообщение автоматически объединено:

Слегка продвинулся, уже можно сделать множественный выбор, но не все решилось. Сделал так:
Код:
#include <GUIConstantsEx.au3>
#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <WindowsConstants.au3>

; Opt('MustDeclareVars', 1) ; принуждает объявлять все переменные

    Local $hGui, $OptionsBtn, $OptionsDummy, $OptionsContext, $msg

    $hGui = GUICreate("Мой GUI", 400, 400)

;    $OptionsBtn = GUICtrlCreateCombo("Выбор пункта", 10, 10, 100, 20, 0x3)
    $OptionsBtn = GUICtrlCreateButton("Выбор пункта", 10, 10, 100, 20, 0x3)
;    $OptionsBtn = GUICtrlCreateButton("Выбор пункта", 10, 10, 100, 20, $BS_FLAT)
;    $OptionsBtn = GUICtrlCreateCombo("Выбрать пункт", 10, 10, 100, 20, $WS_TABSTOP + $CBS_DROPDOWN)

    ; Сначала создан элемент - пустышка (Dummy) и контекстное меню для него, которое будет пристроено к кнопке "Выбрать пункт"
    $OptionsDummy = GUICtrlCreateDummy()
    ;Создаем контекстное меню
    $OptionsContext = GUICtrlCreateContextMenu($OptionsDummy)
    ; Создаем пункты меню
    $Opt1 = GUICtrlCreateMenuItem("Пункт1", $OptionsContext)
    ;GUICtrlSetState(-1, $GUI_CHECKED)
    $Opt2 = GUICtrlCreateMenuItem("Пункт2", $OptionsContext)
    $Opt3 = GUICtrlCreateMenuItem("Пункт3", $OptionsContext)
    $Opt4 = GUICtrlCreateMenuItem("Пункт4", $OptionsContext)
    $Opt5 = GUICtrlCreateMenuItem("Пункт5", $OptionsContext)
    $Opt6 = GUICtrlCreateMenuItem("Пункт6", $OptionsContext)
    $Opt7 = GUICtrlCreateMenuItem("Пункт7", $OptionsContext)

GUISetState()

    While 1
        $msg = GUIGetMsg()

        Switch $msg
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $OptionsBtn
                ShowMenu($hGui, $msg, $OptionsContext)

            Case $Opt1 To $Opt7
            If BitAND(GUICtrlRead($msg), $GUI_CHECKED) = $GUI_CHECKED Then
                GUICtrlSetState($msg, $GUI_UNCHECKED)
            Else
                $check = GUICtrlSetState($msg, $GUI_CHECKED)
                ConsoleWrite("Выбрано 1 " & GUICtrlRead($check, 1) & @CR)

            EndIf
; По идее тут должна была бы сработать проверка хотя бы на один выставленный чекбокс, но не срабатывает
                If _IsChecked($check) Then
                ConsoleWrite("Выбрано 2 " & GUICtrlRead($msg) & @CR)
                EndIf
        EndSwitch
    WEnd
GUIDelete()


 ;проверка чекбокса
 Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED

 EndFunc   ;==>_IsChecked

; Отображает меню в данном GUI окне, которое принадлежит данному элементу в GUI
Func ShowMenu($hWnd, $CtrlID, $nContextID)
   ConsoleWrite("функция ShowMenu" & @CR)
    Local $arPos, $x, $y
    Local $hMenu = GUICtrlGetHandle($nContextID)

    $arPos = ControlGetPos($hWnd, "", $CtrlID)

    $x = $arPos[0]
    $y = $arPos[1] + $arPos[3]

    ClientToScreen($hWnd, $x, $y)
    TrackPopupMenu($hWnd, $hMenu, $x, $y)
EndFunc

; Конвертирует клиентские координаты (GUI) в абсолютные координаты экрана (desktop)
Func ClientToScreen($hWnd, ByRef $x, ByRef $y)
   ConsoleWrite("функция ClientToScreen" & @CR)
    Local $stPoint = DllStructCreate("int;int")

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

    ; См. также _WinAPI_ClientToScreen
    DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $hWnd, "ptr", DllStructGetPtr($stPoint))

    $x = DllStructGetData($stPoint, 1)
    $y = DllStructGetData($stPoint, 2)
    ; обнулять структуру не обязательно, так как она локальна
    $stPoint = 0
EndFunc

; Показать контекстное меню (hMenu) принадлежащее GUI окну (hWnd) в указанных координатах (x, y)
Func TrackPopupMenu($hWnd, $hMenu, $x, $y)
   ConsoleWrite("функция TrackPopupMenu" & @CR)
    ; См. также _GUICtrlMenu_TrackPopupMenu
    DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $x, "int", $y, "hwnd", $hWnd, "ptr", 0)
 EndFunc
Оставшиеся проблемы:
1. Не могу сделать проверку на выставленный чекбокс и считать текст выбранного пункта, хотя бы одного.
2. Выпадающий список гаснет (скрывается) после клика на любом из пунктов.
Сообщение автоматически объединено:

3. Забыл про третью проблему. Пока не получается сделать тоже самое с Combo списком. В примере пробы есть. Можно посмотреть результаты, если раскомментировать строки.
Сообщение автоматически объединено:

Нашел вот такой способ отслеживать клики: https://autoit-script.ru/docs/userfunctions/wm_command.htm
В этом примере вроде считывает значение выбранных пунктов. Попробовал использовать у себя, но почему-то текст пункта не читает, только его позицию (или порядковый номер). Может быть нужно все выделенные пункты считывать в массив, но с другой стороны, отражается реакция на установку/снятие галочки, значит можно и без массива. Делаю так:
Код:
#include <GUIConstantsEx.au3>
#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <WindowsConstants.au3>
#include <GuiMenu.au3>

; Opt('MustDeclareVars', 1) ; принуждает объявлять все переменные

    Local $hGui, $OptionsBtn, $OptionsDummy, $OptionsContext, $msg

    $hGui = GUICreate("Мой GUI", 400, 400)
    GUIRegisterMsg(0x0111, "_WM_COMMAND")
; GUIRegisterMsg($WM_COMMAND, "WM_COMMAND") ; этот вариант не работает, пояснения дальше.


;    $OptionsBtn = GUICtrlCreateCombo("Выбор пункта", 10, 10, 100, 20, 0x3)
    $OptionsBtn = GUICtrlCreateButton("Выбор пункта", 10, 10, 100, 20, 0x3)
;    $OptionsBtn = GUICtrlCreateButton("Выбор пункта", 10, 10, 100, 20, $BS_FLAT)
;    $OptionsBtn = GUICtrlCreateCombo("Выбрать пункт", 10, 10, 100, 20, $WS_TABSTOP + $CBS_DROPDOWN)

    ; Сначала создан элемент - пустышка (Dummy) и контекстное меню для него, которое будет пристроено к кнопке "Выбрать пункт"
    $OptionsDummy = GUICtrlCreateDummy()
    ;Создаем контекстное меню
    $OptionsContext = GUICtrlCreateContextMenu($OptionsDummy)
    ; Создаем пункты меню
    $Opt1 = GUICtrlCreateMenuItem("Пункт1", $OptionsContext)
    ;GUICtrlSetState(-1, $GUI_CHECKED)
    $Opt2 = GUICtrlCreateMenuItem("Пункт2", $OptionsContext)
    $Opt3 = GUICtrlCreateMenuItem("Пункт3", $OptionsContext)
    $Opt4 = GUICtrlCreateMenuItem("Пункт4", $OptionsContext)
    $Opt5 = GUICtrlCreateMenuItem("Пункт5", $OptionsContext)
    $Opt6 = GUICtrlCreateMenuItem("Пункт6", $OptionsContext)
    $Opt7 = GUICtrlCreateMenuItem("Пункт7", $OptionsContext)

GUISetState()

    While 1
        $msg = GUIGetMsg()
   ;$hWnd = WinWaitActive("[CLASS:Мой GUI]")
        Switch $msg
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $OptionsBtn
                ShowMenu($hGui, $msg, $OptionsContext)

            Case $Opt1 To $Opt7
            If BitAND(GUICtrlRead($msg), $GUI_CHECKED) = $GUI_CHECKED Then
                GUICtrlSetState($msg, $GUI_UNCHECKED)
            Else
                $check = GUICtrlSetState($msg, $GUI_CHECKED)
                ConsoleWrite("Выбрано 1 " & GUICtrlRead($check) & GUICtrlRead($check, 0) & GUICtrlRead($check, 1) & @CR)
            EndIf
; По идее тут должна была бы сработать проверка хотя бы на один выставленный чекбокс, но не срабатывает
                If _IsChecked($check) Then
                ConsoleWrite("Выбрано 2 " & GUICtrlRead($msg) & @CR)
                EndIf
        EndSwitch
    WEnd
GUIDelete()

Func _WM_COMMAND($hWnd, $Msg, $wParam, $lParam)
   ConsoleWrite("функция _WM_COMMAND" & @CR)
   ConsoleWrite("Параметр $wParam = " & $wParam & @CR)
    Local $nNotifyCode = BitShift($wParam, 16)
   ConsoleWrite("Параметр $nNotifyCode = " & $nNotifyCode & @CR)
    Local $nID = BitAND($wParam, 0x0000FFFF)
   ConsoleWrite("Параметр $nID = " & $nID & ' = ' & GUICtrlRead($nID) & @CR)
;Local $nNotifyCode, $nID


    Return "GUI_RUNDEFMSG"
EndFunc   ;==>MY_WM_COMMAND

 ;проверка чекбокса
 Func _IsChecked($idControlID)
    Return BitAND(GUICtrlRead($idControlID), $GUI_CHECKED) = $GUI_CHECKED

 EndFunc   ;==>_IsChecked

; Отображает меню в данном GUI окне, которое принадлежит данному элементу в GUI
Func ShowMenu($hWnd, $CtrlID, $nContextID)
   ConsoleWrite("функция ShowMenu" & @CR)
    Local $arPos, $x, $y
    Local $hMenu = GUICtrlGetHandle($nContextID)

    $arPos = ControlGetPos($hWnd, "", $CtrlID)

    $x = $arPos[0]
    $y = $arPos[1] + $arPos[3]

    ClientToScreen($hWnd, $x, $y)
    TrackPopupMenu($hWnd, $hMenu, $x, $y)
EndFunc

; Конвертирует клиентские координаты (GUI) в абсолютные координаты экрана (desktop)
Func ClientToScreen($hWnd, ByRef $x, ByRef $y)
   ConsoleWrite("функция ClientToScreen" & @CR)
    Local $stPoint = DllStructCreate("int;int")

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

    ; См. также _WinAPI_ClientToScreen
    DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $hWnd, "ptr", DllStructGetPtr($stPoint))

    $x = DllStructGetData($stPoint, 1)
    $y = DllStructGetData($stPoint, 2)
    ; обнулять структуру не обязательно, так как она локальна
    $stPoint = 0
EndFunc

; Показать контекстное меню (hMenu) принадлежащее GUI окну (hWnd) в указанных координатах (x, y)
Func TrackPopupMenu($hWnd, $hMenu, $x, $y)
   ConsoleWrite("функция TrackPopupMenu" & @CR)
    ; См. также _GUICtrlMenu_TrackPopupMenu
    DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $x, "int", $y, "hwnd", $hWnd, "ptr", 0)
 EndFunc
И есть ещё одна проблема, странность. В оригинале функция WM_COMMAND запускается строкой:
Код:
GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

Но в таком виде список хоть и работает, но клики по нему не считываются. Посмотрел что такое $WM_COMMAND. Константа из включенного файла WindowsConstants.au3 равная 0x0111. Подставил значение напрямую и в таком виде работает. В своем примере нерабочий вариант закоментил, чтоб была возможность проверить тем, кто заинтересовался.
Так что хоть слегка и продвинулся, но проблемы всё те же.
 
Последнее редактирование:

dumoed

Знающий
Сообщения
34
Репутация
5
вот тут реализован рабочий выпадающий список
 

Вложения

  • wd_demo.au3
    17.8 КБ · Просмотры: 7
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
вот тут реализован рабочий выпадающий список
К сожалению в этом списке нельзя сделать выбор нескольких пунктов. Тут обычный выпадающий список. При этом куча дополнительных файлов. А мне хотелось бы без создания дополнительной udf сделать.
 
Автор
D

DyadyaGenya

Знающий
Сообщения
300
Репутация
10
Стандартный контрол из винды не поддерживает множественный выбор.
Это я знаю. Но ведь есть обходные пути. Один из них я привел в шапке темы. У него свои плюсы и минусы. В моем варианте тоже получается. Даже в таком виде можно придумать нативную функцию, которая бы составляла массив названий пунктов и составляла новый из него и массива значений контекстменю. Но это как-то не очень красиво. И по идее должен быть способ считать без сторонних udf значения/текст пунктов меню. Хотя сторонними это как раз делается. Можно подключить udf отсюда https://autoit-script.ru/threads/ui...tnyx-ehlementov-gui.16780/page-26#post-156474 и прочитать текст. Пробовал. Работает. Но опять же, это сторонняя udf. Но раз сторонняя может, то и нативные должны. Тем более ведь читает первый элемент ( у меня в примерах). Значит и остальные должно. Тут возможно вопрос стоит в том, чтобы прочитать в "чистом виде" сами пункты контекстного меню, напр, на этом отрезке:
Код:
; Создаем пункты меню
    $Opt1 = GUICtrlCreateMenuItem("Пункт1", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt1)
    ;GUICtrlSetState(-1, $GUI_CHECKED)
    $Opt2 = GUICtrlCreateMenuItem("Пункт2", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt2)
    $Opt3 = GUICtrlCreateMenuItem("Пункт3", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt3)
    $Opt4 = GUICtrlCreateMenuItem("Пункт4", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt4)
    $Opt5 = GUICtrlCreateMenuItem("Пункт5", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt5)
    $Opt6 = GUICtrlCreateMenuItem("Пункт6", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt6)
    $Opt7 = GUICtrlCreateMenuItem("Пункт7", $OptionsContext)
    _ArrayAdd($arrayContext, $Opt7)
_ArrayDisplay($arrayContext)

Но даже здесь читает не текст.
А определить какие выбраны легко. По значению $nID. 65-стоит галка, 68 - не стоит. И их легко сопоставлять с номером $nID.
 
Последнее редактирование:
Верх