PDA

Просмотр полной версии : Keyboard input [в COMBOBOX]



AiK
18.04.2004, 14:50
Да, вспоминаю старые добрые времена и истошный крик в комнте клиентского отдела: кто реализовывал этот грёбаный COMBOBOX?
Хотя я тоже задавюсь подобным вопросом, но больше меня интересуют ответы на несколько другие вопросы :), а именно:

1. Как добавить обработчик нажатий клавиш? WM_KEYDOWN это конечно здорово, но если родительское окно, которое получает это сообщение, содержит несколько контролов способных получать keyboard input, то как узнать какой конкретно контрол в данный момент имеет фокус?

2. Как переопределить обработчик? В частности клавиши up и down ведут себя как ESC в нормальных контролах, что не радует

3. Обработка BackSpace - нужно откусывать по одному последнему символу или есть варианты? Вообще непонятно почему Del по умолчанию реализована, а BackSpace - нет...

4. Возможно с не совсем к keyboard input относится, но всё же: иногда самое первое значение, вводимое в COMBOBOX кириллицей отображается гречкой различной...

Спасибо за наставления на путь истинный.

З.Ы: комбик создаю при помощи CreateWindow со стилями
WS_CHILD WS_VISIBLE CBS_DROPDOWN CBS_AUTOHSCROLL и WS_VSCROLL.

Romeo
18.04.2004, 17:07
1 и 2: Для этого нужно использоваться subclassing (например CContainedWindow::SubclassWindow)
3: Я тоже так думаю. Но это можно сделаться только с помощью всё того же сабклассинга.

AiK
19.04.2004, 01:26
Romeo, один маленкий деталь: раздел у нас отвлечённый от языка реализации. Т.е. мне твой пример ровным счётом ничего не говорит.

Romeo
19.04.2004, 13:13
Подожди-ка, ты написал: "Как переопределить обработчик сообщений?". Значит речь всё-таки едёт о программном вмешательстве в COMBOBOX, а не о наборе тех хитростей, которыми может воспользоваться программер не закапываясь глубже редактора ресурсов и стилей окна :) А если у нас есть язык программирования - значит в любом случае есть понятие сабклассинга (не важно воспользуешься ты стандартными средствами готовых библиотек (MFC, ALT) или реализуешься всё ручками на Win32 API). если речь идёт о чистом Win32 API, то делается это так:
1. Получаем hWnd комбобокса.
2. Получаем с помощью GetWindowLong указатель на процедуру-обработчик и запоминаем её в переменной.
3. С помощью SetWindowLong устанавливаем новый обработчик, который должен выполнять некие специализированные действия, а потом вызывать старый обработчик заблаговременно сохранённый в промежуточной переменной.

Вот и всё.

AiK
19.04.2004, 13:45
Romeo, уже ближе к телу, спасибо :) Но, если я правильно понимаю, твоё решение даёт две возможности: либо добавить обработку нажатия ещё каких-то клавиш (т.е. "потом вызывать старый обработчик заблаговременно сохранённый в промежуточной переменной"), либо переписывать обработку всех клавиш (т.е. старый обработчик не вызывать).
А мне как-то не улыбается реализовывать всё то, что уже итак реализовано. Типа ctrl+v, shift+insert и всякие другие комбинации, которые я даже не все и знаю :) Т.е. другими словами я хочу изменить обработку одного единственного уведомления и то не целиком, а с конкретным параметром. Например: "нажата клавиша VK_UP". Выхода не может не быть :)

Andy
19.04.2004, 14:20
добавить обработку нажатия ещё каких-то клавиш (т.е. "потом вызывать старый обработчик заблаговременно сохранённый в промежуточной переменной")
Чем не вариант? В MSDN на эту тему и пример хорший есть (на С++).

как узнать какой конкретно контрол в данный момент имеет фокус
GetFocus()

ЗЫ. Вообще можно назначить Up горячей клавишей и в обратотчике проверять какре окно имеет фокус - а там по обстоятельствам.

AiK
19.04.2004, 14:27
Чем не вариант?
Да тем, что старый обработчик выполнит то самое действие, которое я хочу заменить. Или я не прав?

Andy
19.04.2004, 15:08
Для действия, которое заменяешь просто не вызывай старую WindowProc. А для всего остального вызывай по умолчанию.

Но тут по-моему и без субклассинга можно обойтись.

Andy
19.04.2004, 15:10
ЗЫ.

Вот что мне только что форум показал.

Failed sending email :: PHP ::

DEBUG MODE

Line : 234
File : /home/developingru/www/forum/includes/emailer.php

ЗЫЗЫ. Кстати, как только мыло сменил уведомления стали опять приходить. Дело точно было в мыле :)

AiK
19.04.2004, 15:12
Для действия, которое заменяешь просто не вызывай старую WindowProc.
М-да. Понедельник день тяжёлый :). Тормозю со страшной силой. Кстати, а почему это всё субклассингом называется? Причём тут классы вообще?

AiK
19.04.2004, 15:30
Да, с отправкой почты какие-то проблемы непонятного происхождения :(

Andy
19.04.2004, 17:13
Кстати, а почему это всё субклассингом называется? Причём тут классы вообще?
В том смысле, что ты модифицируешь зарегистрированные классы окон типа "combobox".

Тут, думаю, можно сделать и попроще - через акселераторы например.
Хотя субклассинг - здесь "хороший тон" (видимо).

Romeo
20.04.2004, 11:30
Да именно "хороший тон". Тем более в Винде предусмотрена специальная функция для вызова старого обработчика, значит это - "классический" приём.

P.S. А при ответе на форуме действительно вылазят какие-то лаги. Aik - разберись :)

AiK
20.04.2004, 12:03
А при ответе на форуме действительно вылазят какие-то лаги
Эт вчера было. А сегодня ты ещё и не отвечал :)

Eugie
20.04.2004, 18:37
AiK, народ уже ответил на большую часть вопросов, так что я просто суммирую по пунктам:

1. Добавить обработку клавиатурных сообщений в оконную функцию.

Узнать, где фокус ввода: GetFocus()

2. Добавить обработчик - subclassing: пишешь свою процедуру, в которой обрабатываешь сообщения как тебе нужно, а все остальные посылаешь в старый обработчик через ф-цию CallWindowProc.

Дальше 2 варианта: либо регистрировать свой класс окна, либо подменять процедуру для конкретного окна.

Рекомендую 1-й:
- получить адрес старого обработчика: GetClassInfo(NULL, "COMBOBOX", &wndclass)
- установить новый:


wndclass.lpszClassName = "MYCOMBOBOX";
wndclass.lpfnWndProc = MyComboboxProc;

- зарегистрить свой локальный комбобокс: RegisterClass(&wndclass).

Насчет того, что Up и Down работают как Esc - непонятно. Если фокус в поле связанном editbox - должны двигать каретку вперед-назад, если в списке - прокручивать вверх-вниз.

3. ??? - BackSpace в комбо (точнее, в связанном editbox) работает как обычно. Уж его точно перегружать не надо :)

4. Кракозябры - здесь комбо не при чем. Ищи другую причину...

AiK
20.04.2004, 18:51
работает как обычно
А как обычно? :) У меня он просто не работает. UP и Down работают как ESC, если в списке ничего нет, и начинают переходить на предыдущий/следующий элементы списка, если он не пуст.

Eugie
20.04.2004, 19:06
Как обычно - при нажатии на BackSpace удаляется символ перед кареткой :) Почему у тебя не работает - это есть биг квесчон.
Насчет Up/Down - на пустом списке не проверял, а на непустом - все правильно. А если фокус в поле ввода, что происходит?

AiK
21.04.2004, 12:46
Кстати, ещё вопрос: как заставить этот COMBOBOX получить фокус при перемещении TAB'ом? Типа TABSTOP ему выставить. И, раз уж пошла такая пьянка, заодно как порядок обхода задаётся?

Romeo
21.04.2004, 16:24
Порядком расположения элементов диалога в rc файле :)

Eugie
21.04.2004, 16:46
Да, и задать среди прочих стиль WS_TABSTOP

AiK
22.04.2004, 15:45
Вот такая ситуация: если создавать тулбар без указания стиля WS_CHILD, то комбик на нём расположенный ведёт себя как положено, т.е. BackSpace к примеру отрабатывает. А вот если указать WS_CHILD, то перестаёт. Где-то тут собака порылась..

Romeo
22.04.2004, 16:48
Rebar ты имел в виду, наверное. Ну не важно, всё равно я с таким глюками не сталкивался.

AiK
26.04.2004, 16:16
Rebar ты имел в виду, наверное
Да нет, если я вызываю CreateWindowEx c именем класса TOOLBARCLASSNAME aka ToolbarWindow32, то вряд ли тулбар ребаром становится.

С backspace вроде как всё заборол - в реализации интерфейса IInputObject небольшой косяк был. Но зато в полный рост проявилась проблема с русскими буквами. Выводится не гречка, а английские буквы и цифры. Причём при отслеживании сообщений, получаемых комбиком видно, что WM_CHAR приходит какое-то левое именно для русских букв...

Romeo
27.04.2004, 12:29
А что, на обычный Toolbar можно спокойно кинуть Combo Box? Научи, о учитель. :)

AiK
27.04.2004, 12:38
Romeo, См. MSDN. В статье про тулбары есть раздел: Embedding Nonbutton Controls in Toolbars. В двух словах - под окно резервируется место тулбаровым сепаратором, и поверх него кладётся это окно.

Romeo
27.04.2004, 12:40
Классно, не знал.

AiK
15.08.2004, 03:10
Опять возвращаюсь к своим баранам :(

Казалось бы нашёл решение, которое, увы, полечило не совсем все проблемы.

К сожалению, всё кухню понимаю о-о-чень приблизительно, поэтому объясняю на пальцах:
мой комбик живёт внутри IE. Т.е. моё приложение - это COM объект, реализующий несколько обязательных интерфейсов.
В данном случае, интерес представляет метод TranslateAcceleratorIO интерфейса IInputObject. Соответственно у меня нет привычного цикла


while (GetMessage()) do
begin
TranslateMessage();
DispatchMessage();
end;

Зато IE вызывает мой метод TranslateAcceleratorIO в своём подобном цикле.

Опытным путём я установил, что я должен транслировать и диспетчеризовать нажатие клавиш типа VK_DELETE, VK_INSERT и т.п. чтобы мой комбик нормально обрабатывал стандартные сочетания вроде shift-insert, ctrl-insert, ctrl-c, ctrl-v, ctrl-x и т.д.

Итого, у меня есть такая имплементация TranslateAcceleratorIO :


if (lpMsg.WParam in [VK_BACK,
VK_ESCAPE,
VK_LEFT,
VK_RIGHT,
VK_HOME,
VK_END,
VK_UP,
VK_DOWN,
VK_DELETE,
VK_INSERT,
VK_RETURN,
VK_CONTROL]) then begin
TranslateMessage(lpMSg);
DispatchMessage(lpMsg);
Result := S_OK;
end
else
begin
Result := S_FALSE;
end;
end;


Всё вроде бы работает как надо. За исключением единственного сочетания ctrl-c.
Это меня просто убивает: ctrl-x работает, ctrl-insert тоже, а ctrl-c - не желает.
Как бы это печальное событие отрихтовать?

Да, вот ещё нашли такое сочетание: ctrl+back. Должно удалять последнее введённое слово... Тоже не пашет :(
И где бы полный список глянуть всех этих сочетаний?

Заранее 10x.

AiK
15.08.2004, 14:19
Нашёл затычку: ловлю конструкцию CTRL+C и отправляю комбику WM_COPY. Чую, что бесовщина, а обосновать не могу © :(