Что нового

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

DyadyaGenya

Знающий
Сообщения
284
Репутация
8
Доброго времени суток. Пробую создать выпадающий список, чтобы была возможность множественного выбора. Тоесть чтобы можно было выбрать несколько пунктов из выпадающего списка. Но пока у меня ничего не получается. На форуме нашел похожую тему 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

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

Вложения

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

DyadyaGenya

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

DyadyaGenya

Знающий
Сообщения
284
Репутация
8
Стандартный контрол из винды не поддерживает множественный выбор.
Это я знаю. Но ведь есть обходные пути. Один из них я привел в шапке темы. У него свои плюсы и минусы. В моем варианте тоже получается. Даже в таком виде можно придумать нативную функцию, которая бы составляла массив названий пунктов и составляла новый из него и массива значений контекстменю. Но это как-то не очень красиво. И по идее должен быть способ считать без сторонних 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.
 
Последнее редактирование:
Верх