Первая проблема, которая возникла у меня при попытке скомпилировать ваш код, заключается в том, что вы либо не компилируете для Unicode, либо используете функцию не-Unicode sprintf
для форматирования текста. Это первое, что нужно исправить: приложения Windows полностью Unicode уже более десяти лет. Замените каждый экземпляр char
объявлений переменных на wchar_t
(или TCHAR
), добавьте к строковым литералам префикс L
(или окружите их макросом TEXT()
) и быстро замените вызовы на sprintf
на звонки на wsprintf
. Как указывается в документации, функции, безусловно, лучше использовать, чем wsprintf
, но то же самое верно и для sprintf
, и это позволяет скомпилировать код с минимальными усилиями.
Другая вещь, которая выглядит для меня не идиоматичной, это использование вами функций Get
/ SetClassLong
и Get
/ SetWindowLong
. В настоящее время я всегда пишу код с учетом 64-битной переносимости, и поэтому я бы заменил их на Get
/ SetClassLongPtr
и * Макросы 1027 *Get
/ SetWindowLongPtr
, которые автоматически преобразуются в правильный вызов функции, в зависимости от того, выполняете ли вы компиляцию для x86 или x64. Это не является нарушителем соглашения.
Сначала вы спросили, существует ли способ обработки сообщения WM_NOTIFY
непосредственно из подклассифицированного элемента управления путем автоматической пересылки. К сожалению, это невозможно. Модель Win32 такова, что родители всегда владеют своими детьми, и поэтому они несут ответственность за управление событиями. Я согласен с вашей интуицией о разделении интересов, но единственный способ добиться этого - явная пересылка сообщения от родителя на соответствующий дочерний элемент управления самостоятельно. Фреймворки, такие как MFC (которые инкапсулируют Win32 API), делают это для вас, по-видимому, автоматически, но все еще должны пересылать уведомления от родителя к потомку. Они делают это, используя то, что называется «отражение сообщения», о котором вы можете прочитать здесь . Ничто не мешает вам реализовать нечто подобное в вашем собственном приложении, но в определенный момент вы должны остановиться и спросить себя, не стоит ли использовать одну из многих доступных структур GUI только для такого рода вещей.
В любом случае, насколько я понимаю, основной смысл вашего вопроса заключается в следующем:
Я хочу изменить это, чтобы сделать редактируемыми только подэлементы.
Это кажется довольно простым исправлением. Все, что вам нужно сделать, это проверить обработчик уведомлений LVN_BEGINLABELEDIT
, который пользователь фактически запросил для редактирования подпункта. Поскольку вы использовали его в другом месте своего кода, вы знаете, что LVITEM.iSubItem
член дает вам либо единичный индекс подпункта, либо 0, если структура ссылается на элемент , а не подпункт.
Вставьте эту строку, чтобы убедиться, что lvI.iSubItem
не равно 0 в верхней части обработчика LVN_BEGINLABELEDIT
:
if (lvI.iSubItem == 0) return TRUE; // prevent editing
Как указано в документации к сообщению LVN_BEGINLABELEDIT
, возвращение FALSE
позволяет пользователю редактировать метку, а возвращение TRUE
запрещает их редактирование. Поскольку мы возвращаем TRUE
, мы запрещаем редактирование чего-либо, кроме подпунктов, даже до того, как редактирование начинается.
Мне кажется, что вы уже пытались сделать нечто подобное в обработчике уведомлений LVN_ENDLABELEDIT
с этой строкой:
if (!lvI.iSubItem) return 1;
но уже слишком поздно! Если редактирование уже заканчивается , то у вас уже создалось впечатление, что он может редактировать основной элемент, чего вы не хотите делать. Возьмите эту строку, и она должна работать как положено.
Обратите внимание, что в вашей реализации есть по крайней мере одна явная ошибка: вы не запрещаете пользователю изменять содержимое подпункта в строку длиной более 32 символов, но ваш код, заполняющий элемент управления для редактирования, принимает только строку длиной до 32 символов:
TCHAR text[32];
// ... snip ...
lvI.pszText = text;
lvI.cchTextMax = 32;
SendMessage(hWnd, LVM_GETITEMTEXT, lvI.iItem, (long) &lvI);
SetWindowText(hEdit, lvI.pszText);
Написание этого кода правильным способом было бы (и я подозреваю, что именно поэтому вы сделали это неправильно) гигантской болью в заднице. Как правило, я создаю строковый буфер, который я считаю достаточно длинным, пытаюсь получить текст подпункта и проверяю возвращаемое значение сообщения LVM_GETITEMTEXT
. Возвращаемое значение говорит мне, сколько символов было скопировано в строковый буфер. Если количество скопированных символов указывает, что оно полностью заполнено доступным пространством в строковом буфере, я увеличу размер буфера (возможно, в два раза), а затем снова попытаюсь отправить сообщение LVM_GETITEMTEXT
, Насколько я помню, MFC делает нечто подобное. Сказал тебе, что это было больно, но стоит все исправить.
Более простое решение (хотя и более ограничивающее) состояло бы в том, чтобы пользователь никогда не задавал длину одного из подпунктов в виде строки текста длиннее , чем 32 символа. Тогда вам не придется беспокоиться об обработке длинного ввода, потому что вы знаете, что его никогда не будет, и пользователь никогда не будет смущен поведением вашего элемента управления. Для этого отправьте элементу управления редактирования EM_LIMITTEXT
сообщение в конце вашего обработчика LVN_BEGINLABELEDIT
:
case LVN_BEGINLABELEDIT:
{
// ... snip ...
lvI.cchTextMax = 32;
SendMessage(hWnd, LVM_GETITEMTEXT, lvI.iItem, (long) &lvI);
SetWindowText(hEdit, lvI.pszText);
// (begin new code)
SendMessage(hEdit, EM_LIMITTEXT, lvI.cchTextMax, 0);
}
Теперь пользователь не может ввести больше разрешенного количества символов, поэтому ваш код знает, что вам никогда не придется иметь дело с чем-то большим, чем , находящимся там (если вы не напишите код для их размещения). там сами, в таком случае ...).
Все это говорит, я думаю, что я согласен с Гансом:
Тьфу, ты будешь бороться с глюками вечно. Нет смысла использовать универсальные элементы управления сеткой.