Форум по Delphi программированию

 



Вернуться   Форум по Delphi программированию > Все о Delphi > Компоненты и классы
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 04.04.2020, 21:17
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 124
Версия Delphi: Delphi7
Репутация: 10
По умолчанию Подавление нажатия Alt для TMenu

Здравствуйте, форумчане!
В своей программе я реализовал механизм пользовательских горячих клавиш на разные типовые действия. В настройках для каждого такого действия задаётся сама клавиша, плюс, возможно, модификаторы (Alt, Ctrl, Shift). С двумя последними проблем нет, а вот нажатие Alt тут же перехватывается главным меню формы, и в результате:
- если клавиатурная комбинация соответствует вызову какого-либо пункта меню, одновременно вываливается это меню, и срабатывает код горячей клавиши.
- в противном случае просто срабатывает горячая клавиша, но с системным "бипом" - мол, ошибка тут у вас.
Обе эти ситуации меня не радуют, хотелось бы исправить этот момент.
Но никак не получается перехватить и подавить нажатие Alt - такое ощущение, что меню перехватывает коды клавиш до того, как их перехватывает форма, компонент ApplicationEvents и метод OnHook. Отследить факт нажатия Alt я могу (в частности, это делают мои обработчики OnKeyDown с целю отследить появление горячих комбинаций), но вот подавить его - никак. Либо давится всё и нет вообще реакции на клавиатуру, либо Alt обязательно добирается до главного меню, вызывая нежелательные действия.
Может кто-нибудь сталкивался и знает, как побороть всемогущий Alt? Проблема точно решаемая, во многих приложениях это делается, но моей квалификации никак не хватает, чтобы найти ответ самостоятельно. Сколько ни гуглил - всё вокруг да около, реально действующего кода так и не увидел...

Последний раз редактировалось Guaho, 04.04.2020 в 21:38.
Ответить с цитированием
  #2  
Старый 04.04.2020, 21:40
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 7,583
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Попробуй перехватить само сообщение нажатия кнопок. и если это альт, то обработать самому, а если нет, то пустиьт дальше. Перехват - что-то типа:
Код:
TForm1 = class(TForm)
...
procedure WMKEYDOWN(var Msg : TMessage); message WN_KEYDOWN;
...
Там внутри обработчика если установить Msg.Result = 1 (вроде так), то событие считается обработанным и дальше по списку окон не передается. Вроде так, подробнее надо читать в MSDN - какое конкретно событие, какой тип сообщения нужен и т.д.
Ответить с цитированием
  #3  
Старый 04.04.2020, 21:59
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 124
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Да что я уже только не пробовал... Во все дыры засовывал свои перехватчики. Проблема в том, что определить факт нажатия Alt я могу, но подавить именно Alt - не могу, он же вроде как флаг идёт. Получается, или давится вообще всё тотально, или Alt не давится.
Для обнаружения нажатия Alt использую такую функцию:
Код:
function Tdm.AltDown : Boolean;
var
  State : TKeyboardState; 
begin 
  GetKeyboardState(State); 
  Result := ((State[vk_Menu] and 128) <> 0); 
end; 
Ответить с цитированием
  #4  
Старый 04.04.2020, 22:51
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 124
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Попробовал такой подход: в обработчик OnKeyDown формы поставил такой код:
Код:
function Tdm.AltDown : Boolean;
var
  State : TKeyboardState;
begin
  GetKeyboardState(State);
  Result := ((State[vk_Menu] and 128) <> 0);
  if Result then
    begin
      beep;
      State[vk_Menu] := 0;           // (State[vk_Menu] and not 128);
      SetKeyboardState(State);
    end; 
end;
По идее, судя по коду определения нажатия Alt, это самое состояние задаётся битом 7 байта "vk_Menu". Как инверсия бита, так и обнуление всего байта с последующей записью обратно в массив с помощью ф-ции SetKeyboardState не дали эффекта - Alt не подавляется. Но подход новый, я такого ещё не пробовал, может это и правильное направление...

Последний раз редактировалось Guaho, 04.04.2020 в 22:54.
Ответить с цитированием
  #5  
Старый 05.04.2020, 08:41
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 7,583
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Тебе же надо в Memo?
Попробуй так.
Сабклассим TMemo. У нашего класса отлавливаем сообщение нажатия и отпускания клавиш. Проверяем KeyboardState. Если Alt зажат, то сразу ставим, что сообщение мы обработали (что бы оно не шло дальше) и уже думаем - нужна нам нажатая комбинация или нет.
Т.е. сообщение нажатия клавиш (не событие, которое от VCL, а именно сообщение Windows) ловим прямо в компоненте, который его получает. Там идеология такая, что сначала компонент (окно) получает сообщение и смотрит, нужно ли ему на него реагировать, если не нужно то он дальше отдает ему окну приложения, а уж оно начинает разбираться по всему стеку оконных компонентов. Т.е. наша задача что бы если Alt зажат, то это все остановилось на копоненте, который получил нажатие. Тогда, если нажали в Memo, то обработаем только мы, если в другом месте формы, то уже пойдет стандартная обработка сообщения.
Как засаблассить компонент - в юните, где у тебя Memo делаем примерно так:
Код:
type
  TMemo=class(StdCtrls.TMemo)
  protected
    procedure WmKeyDown(var Message : TMessage); message WM_KEYDOWN;
    ...
  end;

  TForm1 = class(TForm)
    Memo1 : TMemo;
    ...
Таким образом при создании формы будет создан твой компонент, а не тот, который стандартный. И редактор не поломается, и не надо свой компонент полностью писать/устанавливать.

ЗЫ. Может не прокатить, т.к. этот компонент есть просто обертка над стандартным виндовым. Но, по идее, должно.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Guaho (06.04.2020)
  #6  
Старый 05.04.2020, 10:26
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 124
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Спасибо за подробное разъяснение! Это новый подход, я такого ещё не встречал нигде.
Но у меня более общий случай. Сори, сразу не написал об этом.
У меня большая база данных. Обработчики горячих клавиш вставлены как на уровне форм (для действий общего характера), так и на уровне компонентов на этой форме (преимущественно гридов, но не только, там и DBMemo, и DBEditы могут быть, и что угодно). Форм у меня почти 30 в проекте, поэтому очень желательно не дробиться на мелочи, а по возможности решить этот вопрос глобально. Попробую ещё в Application.OnHook воткнуть тот код, который вчера пробовал...

Последний раз редактировалось Guaho, 05.04.2020 в 10:30.
Ответить с цитированием
  #7  
Старый 05.04.2020, 21:22
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 7,583
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну, если честно, я бы тогда пошел другим путем.
Просто налепил бы нужных TAction и уже в их обработчиках смотрел для какого компонента они вызваны. Это проще, чем бороть систему.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Guaho (06.04.2020)
  #8  
Старый 06.04.2020, 09:36
Аватар для Guaho
Guaho Guaho вне форума
Начинающий
 
Регистрация: 27.08.2017
Сообщения: 124
Версия Delphi: Delphi7
Репутация: 10
По умолчанию

Уффф... Кажись что-то наконец нарисовалось! Спасибо за помощь!
Метод такой: всю обработку клавиатуры надо перенести в OnShortCut формы. И далее примерно так:
Код:
procedure Tfm_komp.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  GlobalKeyBoardKey := dm.GetExtKey(Msg.CharCode); // Получение "расширенного" кода (свой формат) для последующего сравнения с кодами заданных в настройках горячих клавиш.
  GAlt := dm.AltDown; // определение факта нажатия Alt, код я приводил ранее.

  if GlobalKeyBoardKey = ukShowOperPanel then // пример обработчика (показ/скрытие панели)
    begin
      sbShowOperPanel.Down := not sbShowOperPanel.Down;
      sbShowOperPanelClick(Self);
      Handled := true;
      exit;
    end;

........ // (действия с другими клавишами)

  if GAlt then Handled := true; // если вдруг обнаружилась нажатая "неподавленная" Alt, "давим" её.
end;
В случаях, когда надо обрабатывать ГК, только если определённые контролы имеют фокус, здесь же это и проверяем.

Последний раз редактировалось Guaho, 06.04.2020 в 09:55.
Ответить с цитированием
Ответ



Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 08:29.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources", 2004-2020

ВКонтакте   Facebook   Twitter