Возможно ли разрешение следующей проблемы (или хотя бы доказательство о невозможности разрешения) Есть ListView вида report с чек боксами (это как по началу казалось делается легко проверкой на NM_CLICK и определением какой item оказался выделенным). Нужно при клике по нему отслеживать по какому item'у щелкнули и проводить сообразно выделеному элементу определённые действия. Но поведение ListView с чек-боксами следующее (при SINGLESELECT)- если щёлкнули по ListView вне области чек-бокса то изменяется выделение, если в области чек-бокса то выделение не меняется, и это правильно и нормально за исключением того что и в том и другом случае посылается сообщение с NM_CLICK. И нужно чтобы различалось программой где NM_CLICK был послан при щелчке на чек-боксе (и программа должна игнорировать это) а где вне его т.е. щелчок "помечающий" элемент. Про LVN_ITEMCHANGED мне известно но использовать это в моей проблеме я представляю как. Можно конечно после щелчка ещё и отслеживать не изменилось ли состояния в чек-боксах но для этого нужно хранить копию состояний списка что кажется мне излишне громозким. Есть ли какие-то идеи как отследить был ли NM_CLICK вызван кликом по чек-боксу или вне его?
Непонимаю как это поможет. Окно то одно и тоже в обоих случаях. Чек-боксы - это чек-боксы в строках ListView это не отдельные контролы. При всём при том фокус то вообще может не изменятся даже item сам в ListView может быть один и тот же - и в этом случае тоже нужно обрабатывать, более того по специфике программы наибольшая часть работы как раз со стороны пользователя ожидается щёлканье часто по одному и тому же item'у . Вот пользователь щёлкнул по item'у в ListView item с индексом n, потом щёлкнул снова по этому же ListView по item'у с тем же индексом. Какой тут фокус где переключится?
Сейчас мне уже кажется даже это не поможет - дело в том, что судя по тестам при клике на чек-боксе сначала посылается NM_CLICK а уже после LVN_ITEMCHANGED, иначе говоря на момент получения и обработки NM_CLICK state item'а ещё не успел изменится (state в отношении checked) и проверка в этом месте состояния чек-боксов ничего не даст. Пост проверкой тоже не обойтись - т.к. в случае клика того же item'а LVN_ITEMCHANGED вообще не посылается и ждать его бесполезно. Вообще логика посыла сообщений вызванных кликом - полный зоопарк. Если клик произошёл на другом item то пошлётся ТРИ LVN_ITEMCHANGED до NM_CLICK, если на том же item - вообще не пошлётся, если клик произошёл на чек боксе то пошлётся один LVN_ITEMCHANGED но теперь уже после - как в этом кумаре какую-то замкнутую логику выписать - ума не приложу. Одни состояния меняются до другие после клика, а иногда вообще ничего не меняется.
может ptAction из структуры NMITEMACTIVATE поможет определиться? Нельзя ли для этой цели использовать уже имеющийся у каждого итема lParam? Т.е. хранить в нём предыдущее состояние чекбокса и изменять lParam (переключать его) при необходимости.
Если правильно понял вопрос, отследить, где был клик, на чекбоксе или на итеме, можно примерно так: при 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, и плюс дополнительно определиться с чекбоксом. Примерно так: 1561216864__fpu.zip
Нужно всё одно ведь начинать с какого-то события. Я не очень понял как использовать то, что ты предлогаешь. Не мог ли ты прояснить? Т.е. в каком месте (на каком событии) мы что делаем, чтобы отследить вызван щелчок по 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 вообще не прийдёт ни до ни после.
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 придет снова для третьего итема, но с небольшой задержкой. Чем вызвана задержка - не знаю Но есть, может ты её проскочил? Причем в этом случае приходит 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. Надеюсь, что не запутал ещё больше ))
Дык вот наблюдения то говорят о другом. Я банально в код поставил проверку с выводом дебажных строк Результат уже два раза написал что за сообщения и в каком порядке приходят в зависимости от кликов на том же, не на том же, на чек боксе. Всё по разному.
Т.е. в каком месте (на каком событии) мы что делаем, чтобы отследить вызван щелчок по itemу или по чек-боксу? Повторюсь - может быть щелчок по тому же itemу, по другому itemу и по чек боксу. Вот код, однозначно отслеживающий, в каком месте был клик: на чекбоксе или на итеме. Если клик на чекбоксе, то дополнительно показывает установлен чек или снят. Если клик повторный по выделенному итему, сообщение также приходит, только с небольшой задержкой. Код (Text): WndProc proc uses ebx hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM LOCAL buffer[128] :BYTE mov eax,uMsg .... .... .elseif eax==WM_NOTIFY mov ebx,lParam mov eax,[ebx.NMHDR].hwndFrom .if eax==hList mov eax,[ebx.NMHDR].code .if eax==LVN_ITEMCHANGED mov ebx,lParam assume ebx: ptr NMLISTVIEW .if [ebx].uNewState == 4096 invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Unchecked") invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer PrintString buffer .elseif [ebx].uNewState == 8192 invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Checked") invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer PrintString buffer .elseif [ebx].uNewState == 3 invoke wsprintf,addr buffer,SADD("Item %lu %s"),[ebx].iItem,SADD("Selected") invoke SetDlgItemText,hWnd,IDC_STC1,addr buffer PrintString buffer .endif assume ebx:nothing .endif .endif .endif .... .... Сейчас попробовал обработать NM_CLICK и по-моему начинаю понимать, почему мы не понимаем друг друга У меня не приходит несколько NM_CLICK, только одно сообщение на один клик, независимо по чекбоксу или по самому итему и в любой последовательности и независимо от предыдущего состояния итема Прицепив сюда твоё мне кажется, что может у нас разные версии Comctl32.dll и они по разному ведут себя? Поэтому такие недоразумения и получается диалог глухого и немого? У меня версия 5.82 (xpclient.010817-1148) Ещё раз аттач: 1888962549__fpu.zip
Вобщем ещё разок проверил. У тебя нет реакции при мультиселекте (что вобщем мне и не нужно) а понять реагирует ли при клике на том же благодоря интерфейсу вывода - непонятно как. Переписал обработку так чтобы реагировало у меня только на ITEMCHANGED при uNewState=3 результат тот который можно было предположить уже по дебажному выводу - если item другой - то сообщение отлавливается, на клики по чек боксам не реагируем что хорошо, но вот как и следовало ожидать - когда кликают на уже выделеном itemе - нифига ничего моему окну не приходит. Возможно это (посылка сообщений) ещё зависит и от стиля окна. Но факт остаётся фактом - если щёлкают по тому же itemу - ни фига моему окну ITEMCHANGED не идёт
У меня тоже не приходило несколько NM_CLICK тоже одно на один клик хоть по чему. Я писал ведь про несколько сообщений ITEMCHANGED (3 сообщения приходят у меня до того как прийдёт наконец NM_CLICK если щёлкнули по другому itemу).
Пробовал также сделать virtual listview (с LVS_OWNERDATA) - работает как из пушки ИМХО, это твоя версия Comctl32.dll как-то неправильно себя ведёт, пример в аттаче однозначно отслеживает где было кликнуто: на чеке или на самом итеме, независимо от предыдущих состояний как чека, так и итема.
Это не страшно Спасибо за сообщения по крайней мере наступит критический момент и мы разбёмся. По поводу разных версий - у меня тоже подозрения на этот счёт, но пока просто чистая параноя. Просто сообщения приходили как то не очень систематично от ListView от этого создалось впечатления что создателей контрола особенно не заботило как решать задачи подобно моей, а если не заботило, то они и в разных версиях не очень будут обременять себя чтоб как-то это систематизировать, вплоть до того что могут и порядок изменить. Лишь бы вообще сообщения посылались а в каком порядке относительно друг друга - не важно. Но повторюсь - это пока не более чем параноидальный страх с моей стороны. По поводу пояснений где у тебя это отслеживается - я понял это после уже первого атача. Так что здесь полное понимание и вот именно [ebx].uNewState == 3 у меня НЕ ПРОИСХОДИТ если щёлкнули по тому же itemу, вобщем как я писал не то что даже он не происходит - вообще ITEMCHANGED не приходит, лень ему что ли собаке?
О NM_CLICK: Единственный вариант, который я вижу как по NM_CLICK можно определиться - это смотреть в структуре NMITEMACTIVATE поле ptAction, точнее ptAction.x, если оно меньше 16 - клик на чеке, если >=16 - на итеме. Но NM_CLICK не даёт информацию о состоянии чекбокса(checked или unchecked). В msdn указано, что есть с++ макрос ListView_GetCheckState, но соответствующего ему сообщения для посылки через SendMessage я не нашёл... Клик на чеке вычислить можно, а вот состояние чека получается только хранить в lParam итема и инвертировать постоянно. Либо попытаться как-то задействовать сей макрос. P.S. Элемент танца с бубном: случайно манифест не задействован? Эта штука иногда выдаёт разные казусы.
Хе - разобрался я Хотя легче не стало Вобщем как я и предположил - дело в стилях, если присутсвует LVS_SINGLESELECT то если клик произошёл на том же (уже выделеном) iteme - никакого ITEMCHANGED не посылается. Если убрать его - то начинает посылаться ITEMCHANGED даже при клике на том же выделеном. Так что версии dll тут оказались не при чём, при чём оказались стили. Но LVS_SINGLESELECT нужен. Чувствую дело идёт к сабклассингу, ломает писать его из-за такой ерунды
А вот это очень хорошая идея! Только вот волнует есть ли гарантия в константе 16? Не может ли быть чек-бокс больше\меньше и если может как узнать его размеры?
Похоже, что гарантия есть: независимо от экранного разрешения, размера шрифта в итеме (и сответственно его высоты), размер самого чекбокса постоянен: 16х16. Также постоянно расстояние от левой границы листвью до начала квадратика чекбокса. Единственно, если использовать манифест, чекбокс смещается вправо, и эта цифра изменяется в сторону увеличения: до 20 включительно - чекбокс, 21 и более - итем. Слева от чекбокса имеется "мертвая зона", поэтому о запоминать состояния чека лучше не по координате, а по uNewState (4096/8912) при ITEMCHANGED.
local Rct :RECT mov Rct.left,LVIR_LABEL invoke SendMessage,hList,LVM_GETITEMRECT,0,addr Rct sub Rct.left,2 в Rct.left координата первого пикселя, входящего в итем. Независимо от наличия или отсутствия манифеста (16 или 21)