Отслеживание кликов в ListView

Тема в разделе "WASM.WIN32", создана пользователем The Svin, 14 май 2005.

  1. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Возможно ли разрешение следующей проблемы

    (или хотя бы доказательство о невозможности разрешения)

    Есть ListView вида report с чек боксами (это как по началу казалось делается легко проверкой на NM_CLICK и определением какой item оказался выделенным).

    Нужно при клике по нему отслеживать по какому item'у щелкнули и проводить сообразно выделеному элементу определённые действия.

    Но поведение ListView с чек-боксами следующее (при SINGLESELECT)- если щёлкнули по ListView вне области чек-бокса то изменяется выделение, если в области чек-бокса то выделение не меняется, и это правильно и нормально за исключением того что и в том и другом случае посылается сообщение с NM_CLICK. И нужно чтобы различалось программой где NM_CLICK был послан при щелчке на чек-боксе (и программа должна игнорировать это) а где вне его т.е. щелчок "помечающий" элемент. Про LVN_ITEMCHANGED мне известно но использовать это в моей проблеме я представляю как. Можно конечно после щелчка ещё и отслеживать не изменилось ли состояния в чек-боксах но для этого нужно хранить копию состояний списка что кажется мне излишне громозким.

    Есть ли какие-то идеи как отследить был ли NM_CLICK вызван кликом по чек-боксу или вне его?
     
  2. kaspersky

    kaspersky New Member

    Публикаций:
    0
    Регистрация:
    18 май 2004
    Сообщения:
    3.006
    WM_MOUSEACTIVE -> MA_ATCIVE тебе поможет,

    генерится при переключении фокуса на окно
     
  3. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Непонимаю как это поможет.

    Окно то одно и тоже в обоих случаях.

    Чек-боксы - это чек-боксы в строках ListView это не отдельные контролы. При всём при том фокус то вообще может не изменятся даже item сам в ListView может быть один и тот же - и в этом случае тоже нужно обрабатывать, более того по специфике программы наибольшая часть работы как раз со стороны пользователя ожидается щёлканье часто по одному и тому же item'у . Вот пользователь щёлкнул по

    item'у в ListView item с индексом n, потом щёлкнул снова по этому же ListView по item'у с тем же индексом. Какой тут фокус где переключится?
     
  4. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia




    Сейчас мне уже кажется даже это не поможет - дело в том, что судя по тестам при клике на чек-боксе сначала посылается NM_CLICK а уже после LVN_ITEMCHANGED, иначе говоря на момент получения и обработки NM_CLICK state item'а ещё не успел изменится (state в отношении checked) и проверка в этом месте состояния чек-боксов ничего не даст. Пост проверкой тоже не обойтись - т.к. в случае клика того же item'а LVN_ITEMCHANGED вообще не посылается и ждать его бесполезно.

    Вообще логика посыла сообщений вызванных кликом - полный зоопарк.

    Если клик произошёл на другом item то пошлётся ТРИ LVN_ITEMCHANGED до NM_CLICK, если на том же item - вообще не пошлётся, если клик произошёл на чек боксе то пошлётся один LVN_ITEMCHANGED но теперь уже после - как в этом кумаре какую-то замкнутую логику выписать - ума не приложу. Одни состояния меняются до другие после клика, а иногда вообще ничего не меняется.
     
  5. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    может ptAction из структуры NMITEMACTIVATE поможет определиться?







    Нельзя ли для этой цели использовать уже имеющийся у каждого итема lParam? Т.е. хранить в нём предыдущее состояние чекбокса и изменять lParam (переключать его) при необходимости.
     
  6. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Если правильно понял вопрос, отследить, где был клик, на чекбоксе или на итеме, можно примерно так: при WM_NOTIFY если NMHDR.code =

    LVN_ITEMCHANGED, проверяется NMLISTVIEW.uNewState. Если uNewState = 4096 или 8192 - значит текущее

    выделение осталось без изменений, клик был на чекбоксе, и при этом NMLISTVIEW.iItem содержит номер итема, у которого изменился чекбокс (4096-unchecked,8192-checked).

    Если NMLISTVIEW.uNewState = 3, значит состояния чекбоксов не изменились, и при этом NMLISTVIEW.iItem

    содержит номер итема, который был выделен (клик был на самом итеме).

    На NM_CLICK можно забить. Практически всю информацию можно получить по LVN_ITEMCHANGED также, как и по NM_CLICK, и плюс дополнительно определиться с чекбоксом.

    Примерно так:

    [​IMG] 1561216864__fpu.zip
     
  7. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Нужно всё одно ведь начинать с какого-то события.

    Я не очень понял как использовать то, что ты предлогаешь.

    Не мог ли ты прояснить?

    Т.е. в каком месте (на каком событии) мы что делаем, чтобы отследить вызван щелчок по itemу или по чек-боксу?

    Повторюсь - может быть щелчок по тому же itemу, по другому itemу и по чек боксу.

    По каждому из этих случаем по разному происходит посылка других окружающих сообщений. Описано выше как.

    Можно хранить и в lParam, или ещё где хоть в битовой строке, вопрос как это всё завязать в замкнутую логику.

    Если не понятно что такое замкнутая логика, то поясню на фантастическом примере.

    В реальности когда кликается по чек боксу сначала посылается NM_CLICK (по которому(данным с ним связанным) ещё не поймёшь то ли по чекбоксу кликнули толи по itemу)

    После этого сообщения посылается ещё и ITEMCHANGED.

    Причём учтём что ITEMCHANGED может быть в свою очередь и без NM_CLICK (пробелом пользователь например может его поставить, тогда NM_CLICK не пошлётся)

    Если бы ITEMCHANGED (начинается фантастическая часть) посылался также и в любом случае когда щёлкается по itemу (вне зависимости уже выделенному или новому) то замкнутая логика обработки выглядела бы к примеру так

    При получении NM_CLICK от нужнго ListView установился бы в месте обработки этого сообщения флажок нужный для обработки ITEMCHANGED, в месте обработки ITEMCHANGED проверился бы этот флаг (предшествовал ли этому сообщению NM_CLICK) если он установлен - то далее проверилось бы какие атрибуты изменились и если атрибутом был check то сообщение проигнорировалось иначе выполнилась обработка клика. Флаг в любом случае сбрасывается после всех этих проверок если он был установлен.

    К сожалению такая схема не сработает так как после NM_CLICK ITEMCHANGED приходит только если клик был по чек-боксу, если он произошёл по другому itemy то прийдёт три сообщения и до а не после NM_CLICK а если по тому же itemу то ITEMCHANGED вообще не прийдёт ни до ни после.
     
  8. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    The Svin

    То, что предложено, основано на предположении, что тебя интересует само событие нажатия кнопки мыши и в каком месте находился курсор в этот момент - на чеке или вне его на самом итеме (или субитеме). НО не сообщение как таковое NM_CLICK. Если моё предположение о твоей задаче верно, то получить информацию о положении курсора мыши в момент нажатия мыши можно вышеописаным способом, причем для этого не требуется вообще реагировать на NM_CLICK. Ведь при нажатии мыши посылается несколько сообщений, и если не удаётся добиться определенности, обрабатывая одно из них - NM_CLICK, то можно отвернуться от него и обработать ITEMCHANGED, чтобы определить, где был курсор в момент нажатия.



    Если же у тебя есть только NM_CLICK, и надо определяться именно по NM_CLICK, тогда надо думать дальше, как упорядочить эту цепь сообщений NM_CLICK.







    Стоп, почему фантастическая? Это так и есть, ITEMCHANGED приходит всегда, когда кликаем на итеме, независимо от того, был он selected или нет, был checked или нет, и в том числе приходит также, если давить клавишу пробела. Он приходит всегда. Может ты не обратил внимание? Есть там один момент, который я не понял: если выполнить такую цепь нажатий:

    1. Нажали чек третьего итема

    2. Нажали сам третий итем

    3. Нажали чек второго итема

    то будем иметь два checked итема (2-й и 3-й) и один selected (3-й). И если в таком состоянии снова выполнить пункт 2 - нажать уже выделенный синим третий итем, то ITEMCHANGED придет снова для третьего итема, но с небольшой задержкой. Чем вызвана задержка - не знаю :dntknw: Но есть, может ты её проскочил? Причем в этом случае приходит 2 сообщения ITEMCHANGED - один раз видимо уведомляя, что снимается selected и второй раз чтобы тутже снова установить selected.







    Повторюсь: ITEMCHANGED приходит всегда, если кликали на чеке - один раз, если на самом итеме, то несколько.

    Да, придёт несколько сообщений, но их же можно фильтровать по состоянию uNewState, как я сделал в примере, и пропустить мимо те сообщения, которые не нужны (которые сообщают информацию о старом выделенном итеме). Обрабатывать только сообщение, для которого uNewState соответствует установлению чека, снятию чека и установлению нового выделения/select - соответственно uNewState=8192, uNewState=4096 или uNewState=3. Все остальные uNewState игнорировать, чтобы они не вносили сумятицу :)





    Но повторюсь если тебе нужно только по NM_CLICK, то надо будет мудрить с цепью сообщений NM_CLICK, возможно фильтруя те из них, которые не относятся к последнему кликнутому (из пачки этих сообщений NM_CLICK к активируемому итему относится последнее сообщение),разграничивая возможно флагами, состояниями .iItem. Можно например в качестве флага держать именно этот iItem, и сравнивать приходящий в lParam'е iItem с флагом. Если совпал - игнорировать сообщение, если нет - реагировать на сообщение и устанавливать флаг равным iItem.



    Надеюсь, что не запутал ещё больше :)))
     
  9. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia


    Дык вот наблюдения то говорят о другом.

    Я банально в код поставил проверку с выводом дебажных строк

    Результат уже два раза написал что за сообщения и в каком порядке приходят в зависимости от кликов на том же, не на том же, на чек боксе. Всё по разному.
     
  10. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Т.е. в каком месте (на каком событии) мы что делаем, чтобы отследить вызван щелчок по itemу или по чек-боксу?

    Повторюсь - может быть щелчок по тому же itemу, по другому itemу и по чек боксу.




    Вот код, однозначно отслеживающий, в каком месте был клик: на чекбоксе или на итеме. Если клик на чекбоксе, то дополнительно показывает установлен чек или снят. Если клик повторный по выделенному итему, сообщение также приходит, только с небольшой задержкой.


    Код (Text):
    1. WndProc proc uses ebx hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    2. LOCAL buffer[128]     :BYTE
    3.     mov eax,uMsg
    4.     ....
    5.     ....
    6.  
    7.     .elseif eax==WM_NOTIFY
    8.         mov ebx,lParam
    9.         mov eax,[ebx.NMHDR].hwndFrom
    10.         .if eax==hList
    11.             mov eax,[ebx.NMHDR].code
    12.             .if eax==LVN_ITEMCHANGED
    13.                 mov ebx,lParam
    14.                 assume ebx: ptr NMLISTVIEW
    15.                 .if [ebx].uNewState == 4096
    16.                     invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Unchecked")
    17.                     invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer
    18.                     PrintString buffer
    19.                 .elseif [ebx].uNewState == 8192
    20.                     invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Checked")
    21.                     invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer
    22.                     PrintString buffer
    23.                 .elseif [ebx].uNewState == 3
    24.                     invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Selected")
    25.                     invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer
    26.                     PrintString buffer
    27.                 .endif
    28.                 assume ebx:nothing
    29.             .endif
    30.         .endif
    31.     .endif
    32.     ....
    33.     ....






    Сейчас попробовал обработать NM_CLICK и по-моему начинаю понимать, почему мы не понимаем друг друга :dntknw: У меня не приходит несколько NM_CLICK, только одно сообщение на один клик, независимо по чекбоксу или по самому итему и в любой последовательности и независимо от предыдущего состояния итема :dntknw: Прицепив сюда твоё
    мне кажется, что может у нас разные версии Comctl32.dll и они по разному ведут себя? Поэтому такие недоразумения и получается диалог глухого и немого? У меня версия 5.82 (xpclient.010817-1148)



    Ещё раз аттач:



    [​IMG] 1888962549__fpu.zip
     
  11. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Вобщем ещё разок проверил. У тебя нет реакции при мультиселекте (что вобщем мне и не нужно) а понять реагирует ли при клике на том же благодоря интерфейсу вывода - непонятно как.

    Переписал обработку так чтобы реагировало у меня только на ITEMCHANGED при uNewState=3 результат тот который можно было предположить уже по дебажному выводу - если item другой - то сообщение отлавливается, на клики по чек боксам не реагируем что хорошо, но вот как и следовало ожидать - когда кликают на уже выделеном itemе - нифига ничего моему окну не приходит.

    Возможно это (посылка сообщений) ещё зависит и от стиля окна. Но факт остаётся фактом - если щёлкают по тому же itemу - ни фига моему окну ITEMCHANGED не идёт :dntknw:
     
  12. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia


    У меня тоже не приходило несколько NM_CLICK тоже одно на один клик хоть по чему. Я писал ведь про несколько сообщений ITEMCHANGED (3 сообщения приходят у меня до того как прийдёт наконец NM_CLICK если щёлкнули по другому itemу).
     
  13. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257




    Пробовал также сделать virtual listview (с LVS_OWNERDATA) - работает как из пушки :dntknw:

    ИМХО, это твоя версия Comctl32.dll как-то неправильно себя ведёт, пример в аттаче однозначно отслеживает где было кликнуто: на чеке или на самом итеме, независимо от предыдущих состояний как чека, так и итема.
     
  14. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia


    Это не страшно :)

    Спасибо за сообщения по крайней мере наступит критический момент и мы разбёмся.

    По поводу разных версий - у меня тоже подозрения на этот счёт, но пока просто чистая параноя. Просто сообщения приходили как то не очень систематично от ListView от этого создалось впечатления что создателей контрола особенно не заботило как решать задачи подобно моей, а если не заботило, то они и в разных версиях не очень будут обременять себя чтоб как-то это систематизировать, вплоть до того что могут и порядок изменить. Лишь бы вообще сообщения посылались а в каком порядке относительно друг друга - не важно. Но повторюсь - это пока не более чем параноидальный страх с моей стороны.



    По поводу пояснений где у тебя это отслеживается - я понял это после уже первого атача. Так что здесь полное понимание и вот именно

    [ebx].uNewState == 3

    у меня НЕ ПРОИСХОДИТ если щёлкнули по тому же itemу, вобщем как я писал не то что даже он не происходит - вообще ITEMCHANGED не приходит, лень ему что ли собаке? :)
     
  15. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    О NM_CLICK:

    Единственный вариант, который я вижу как по NM_CLICK можно определиться - это смотреть в структуре NMITEMACTIVATE поле ptAction, точнее ptAction.x, если оно меньше 16 - клик на чеке, если >=16 - на итеме. Но NM_CLICK не даёт информацию о состоянии чекбокса(checked или unchecked). В msdn указано, что есть с++ макрос ListView_GetCheckState, но соответствующего ему сообщения для посылки через SendMessage я не нашёл... Клик на чеке вычислить можно, а вот состояние чека получается только хранить в lParam итема и инвертировать постоянно. Либо попытаться как-то задействовать сей макрос.



    P.S.

    Элемент танца с бубном: случайно манифест не задействован? Эта штука иногда выдаёт разные казусы.
     
  16. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    Хе - разобрался я ;)

    Хотя легче не стало :dntknw:

    Вобщем как я и предположил - дело в стилях, если присутсвует LVS_SINGLESELECT то если клик произошёл на том же (уже выделеном) iteme - никакого ITEMCHANGED не посылается. Если убрать его - то начинает посылаться ITEMCHANGED даже при клике на том же выделеном. Так что версии dll тут оказались не при чём, при чём оказались стили.

    Но LVS_SINGLESELECT нужен. Чувствую дело идёт к сабклассингу, ломает писать его из-за такой ерунды :dntknw:
     
  17. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia


    А вот это очень хорошая идея!

    Только вот волнует есть ли гарантия в константе 16?

    Не может ли быть чек-бокс больше\меньше и если может как узнать его размеры?
     
  18. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Похоже, что гарантия есть: независимо от экранного разрешения, размера шрифта в итеме (и сответственно его высоты), размер самого чекбокса постоянен: 16х16. Также постоянно расстояние от левой границы листвью до начала квадратика чекбокса.



    Единственно, если использовать манифест, чекбокс смещается вправо, и эта цифра изменяется в сторону увеличения: до 20 включительно - чекбокс, 21 и более - итем.

    Слева от чекбокса имеется "мертвая зона", поэтому о запоминать состояния чека лучше не по координате, а по uNewState (4096/8912) при ITEMCHANGED.
     
  19. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    local Rct :RECT

    mov Rct.left,LVIR_LABEL

    invoke SendMessage,hList,LVM_GETITEMRECT,0,addr Rct

    sub Rct.left,2

    в Rct.left координата первого пикселя, входящего в итем.

    Независимо от наличия или отсутствия манифеста (16 или 21)
     
  20. The Svin

    The Svin New Member

    Публикаций:
    0
    Регистрация:
    6 июл 2003
    Сообщения:
    665
    Адрес:
    Russia
    А что такое манифест?