Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Вопрос про GetKeyState [D6]


Страницы:   1 2

Суслик ©   (2005-11-14 15:45:00) 

Добный день.

1. При получении контролом фокуса мне нужно определить, каким образом к нему попал фокус. Мне нужно вычленить случаи получения фокуса по TAB и по SHIFT+TAB. Получение фокуса иными способами (мышь, CTRL+TAB, ALT+TAB и пр. не интересуют).

В обработчике сообщения WM_SETFOCUS я пытался делать так

if (GetKeyState(VK_TAB) < 0)
  and (GetKeyState(VK_CONTROL) >= 0)
  and (GetKeyState(VK_MENU) >= 0) then
begin
  Forward := GetKeyState(VK_SHIFT) >= 0; (**)
end;


В итогде переменная Forward содержит true, если фокус получен по tab и false - по shift+tab.

При тестировании обнаружил, что иногда (не всегда) управление доходит до строки (**) если я нажимал alt+tab. При детальном анализе действия своих рук понял, что я alt отпускаю несколько раньше, чем управление доходит до проверки GetKeyState(VK_MENU) >= 0.

2. Почитав про GetKeyState я не понял (наверное, трудности перевода) в какой момент определяется значение GetKeyState. Точно не в момент вызова (для этого есть другая функция GetAsyncKeyState).

Вопросы:
1. Как решить задачу из п. 1?
2. В какой момент определяется значение GetKeyState?

Спасибо.


Суслик   (2005-11-18 17:47:00) [1]


>  [33] GuAV ©   (18.11.05 17:38)

согласен, это тоже в общем случае вариант.
Но у меня была надежда, что можно как-то заставить
"правильно" работать GetKeyState.

В итоге у меня два вариант - твой и Игоря. Рассмотрю оба.


GuAV   (2005-11-18 17:38:00) [2]

Ха, ну даже если и

>ты не прав (об этом свидетельствует тест).
> Фокус приходит из другого приложения.

, свои окна отличаются через GetWindowThreadProcessId, так что для SDI можно рассматривать как вариант.


Суслик   (2005-11-18 16:55:00) [3]


> AttachThreadInput

Я не вызывал его


GuAV   (2005-11-18 16:54:00) [4]


>ты не прав (об этом свидетельствует тест).
> Фокус приходит из другого приложения.

Может, вызван AttachThreadInput ?
У меня wParam = 0 при Alt-Tab. Сtrl-Tab не проверял, ничего MDI по рукой нет...


Игорь Шевченко   (2005-11-18 16:47:00) [5]

GuAV ©   (18.11.05 16:35) [28]


> Клавиша Tab уже нажата для окна, получившего фокус и даёт
> сообщение WM_KEYUP получателю фокуса при отпускании


Век живи, век учись. Спасибо, не догадался посмотреть


Суслик   (2005-11-18 16:46:00) [6]


> Исключить реакцию на Alt-Tab и Cltr-Tab можно, т.к. в этом
> случае фокус приходит "ниоткуда", т.е. hwndLoseFocus = (HWND)
> wParam == 0.


ты не прав (об этом свидетельствует тест).
Фокус приходит из другого приложения.

------------
Игорь.

Спасибо. Нужно обдумать детали, но идея точно рабочая.


GuAV   (2005-11-18 16:35:00) [7]

Посмотрел ситуацию в SpyXX. Клавиша Tab уже нажата для окна, получившего фокус и даёт сообщение WM_KEYUP получателю фокуса при отпускании, сообщение WM_SETFOCUS синхронное, поэтому код [0] рабочий.

Исключить реакцию на Alt-Tab и Cltr-Tab можно, т.к. в этом случае фокус приходит "ниоткуда", т.е. hwndLoseFocus = (HWND) wParam == 0.


Игорь Шевченко   (2005-11-18 16:26:00) [8]


> 1. Грид делает GetParentForm(Self).WindowProc := NewWindowProc.
>
> 2. Также как и у тебя в NewWindowProc вызывается старое
> значение WindowProc.
> Т.е. в итоге грид не требует от формы ничего - он сам подменяет
> ей оконную функцию.
>
> Ты это имел в виду?


Да, разумеется. Собственно, ряд компонентов так и поступает, например тот, который меняет внешний вид формы (неклиентскую область). Я не могу сказать, что это хороший тон, но тем не менее, действенный.


Суслик   (2005-11-18 16:16:00) [9]

Спасибо за ответ.

Тут дело такое. Про перекрытие в родительской форме CM_DIALOGKEY я знаю.
О чем я и писал в [13]. Но понимаешь ли - т.к. грид в общем-то отчуждаемый
контрол, ну т.е. хочется, чтобы он был наименее связан с текущим приложением,
есть желание определять tab и shift+tab силами только грида.

Перекрывать CM_DIALOGKEY я уже пробовал. Работает. Но это же такой удар по
повторному использованию кода (т.е. гриду). Т.е. грид предоставляет

Если ничего не придумаю буду перектывать CM_DIALOGKEY у формы.

Дописав до этого места понял, что в общем-то все не плохо :)
Можно сделать так:
1. Грид делает GetParentForm(Self).WindowProc := NewWindowProc.
2. Также как и у тебя в NewWindowProc вызывается старое значение WindowProc.
Т.е. в итоге грид не требует от формы ничего - он сам подменяет ей оконную функцию.

Ты это имел в виду?

В общем пока буду отталкиваться от этого решения!


Игорь Шевченко   (2005-11-18 16:00:00) [10]


> Grid.TabOrder = 0
> AnotherControl.TabOrder = 1


Кажется сообразил. Ты хочешь узнать, в случае двух контролов, какая комбинация была нажата, Tab или Shift+Tab, при этом фокус в любом случае попадает на грид, а ты хочешь выбирать разный столбец в гриде.

Перекрой у родительской формы WindowProc, в обработке CM_DialogKey  проверяй GetKeyState(VK_SHIFT) и вызывай inherited (порядок лучше такой)

Например, так:

unit main;

interface
uses
 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TForm1 = class(TForm)
   Edit1: TEdit;
   Edit2: TEdit;
   ListBox: TListBox;
   procedure Edit1Enter(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   procedure Edit1Exit(Sender: TObject);
 private
   FMovingBackwards: Boolean;
   FOldWindowProc: TWndMethod;
   procedure NewWindowProc (var Message: TMessage);
 end;

var
 Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Edit1Enter(Sender: TObject);
const
 Colors: array[Boolean] of TColor = (clYellow, clRed);
begin
 Edit1.Color := Colors[FMovingBackwards];
 FMovingBackwards := false;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 FOldWindowProc := WindowProc;
 WindowProc := NewWindowProc;
end;

procedure TForm1.NewWindowProc(var Message: TMessage);
begin
 if Message.Msg = CM_DIALOGKEY then
   FMovingBackwards := (GetKeyState(VK_SHIFT) and $8080) <> 0;
 FOldWindowProc (Message);
end;

procedure TForm1.Edit1Exit(Sender: TObject);
begin
 Edit1.Color := clWhite;
end;

end.


Суслик   (2005-11-18 15:26:00) [11]

Grid.TabOrder = 0
AnotherControl.TabOrder = 1

В Grid в обработчике WM_SETFOCUS Msg.FocusedWnd = AnotherControl.Handle.
Следовательно TabOrder пред. контрола равен 1.

И как тут понять спереди или сзади пришел фокус?

Если бы контролов было 3 и более, то все ясно. А в случае двух?

--------

Согласен - если анализировать нажатие мышки, то проблем вроде нет.

На самом деле можно действительно сделать фиктивный контрольчик,
у которого TabOrder больше, чем у грида. Если на контрольчик попадет фокус,
то если раньше фокус был у грида, то предать фокус дальше.
Если раньше фокус был не у грида, то предать фокус гриду.


Игорь Шевченко   (2005-11-18 15:13:00) [12]

Суслик ©   (18.11.05 15:09) [22]


> А! Вспомнил! TabOrder не подойдет - если анализировать ТОЛЬКО
> его,
> то не понятно, как отделять случаи, когда фокус пришел с
> помощью
> мышки.


Какая разница ? По-моему, ты тратишь время на реализацию совершенно ненужной функциональности. При фокусе мышкой можешь смотреть, в какую область грида ткнуто на WM_LBUTTONDOWN, если после этого пришел WM_SETFOCUS, то сам определяй, к чему оласть тыканья ближе, к началу, к концу, к середине. Разумеется, на SetFocus координаты тыканья надо обнулять.


> Но! Только если контрлов > 2.
> Если их 2, то непонятно как пришел фокус, т.к. пред.
> контрол всегда один и тот же.


Так Taborder-то у них разный будет. В том-то и дело.


Суслик   (2005-11-18 15:09:00) [13]

А! Вспомнил! TabOrder не подойдет - если анализировать ТОЛЬКО его,
то не понятно, как отделять случаи, когда фокус пришел с помощью
мышки.

В итоге хотелось бы "добить" вариант с анализом vk_tab и Shift


Суслик   (2005-11-18 15:02:00) [14]


> Насколько я понимаю, тебе нужно ответ на вопрос, как в гриде
> определить, с какого контрола пришел фокус, с "правого"
> или с "левого", чтобы понять, на какой столбец (последний
> или первый, соответственно) устанавливать фокус ?

Ура! Я понят!


> В этом случае может помочь сравнение TabOrder грида и контрола


Знаешь, это решение было самым первым.
Я его даже написал! Сделал ровно как говоришь ты.
Все прекрасно работает. Но! Только если контрлов > 2.
Если их 2, то непонятно как пришел фокус, т.к. пред.
контрол всегда один и тот же.

Поэтому в итоге я остановлися на анализе tab или shift+tab.
Но чую, что этот путь беспреспективен.

Так, что я думаю пойти по пути анализа TabOrder, но для
корректной работы в случае 2 контролов использовать
метод из [12] GuAV


Игорь Шевченко   (2005-11-18 14:57:00) [15]

Суслик ©   (18.11.05 14:12) [19]

Насколько я понимаю, тебе нужно ответ на вопрос, как в гриде определить, с какого контрола пришел фокус, с "правого" или с "левого", чтобы понять, на какой столбец (последний или первый, соответственно) устанавливать фокус ?

В этом случае может помочь сравнение TabOrder грида и контрола, который был перед этим в фокусе, в обработке сообщения WM_SETFOCUS гридом. wParam этого сообщения содержит handle окна, на котором был фокус, найти VCL Control и определить его TabOrder думаю, труда не составит. Тем более, что из грида на нужный контрол ты умеешь уходить :)


Суслик   (2005-11-18 14:12:00) [16]

Игорь.
Спасибо за внимание к моему вопросу.

Но ты не на тот вопрос отвечаешь. Вот смотри [13] п 6. Внутри грида у меня замечательно получается перемащаться. Более того, при достижении конца у меня получается перемащаться к следующему (или предыдущему) контролу. Это я делают так GetParentForm(Self).SelectNext. Т.е. ровно то, что ты написал в [18].

ПРОБЛЕМОЙ является: определение КАК ГРИДОМ получен фокус - по TAB или SHIFT+TAB. В первом случае нужно спозиционироваться на первую слева сверху ячейку, во втором случае - на последнюю.

В исходном вопросе мной был приведен код, которые работает не совсем верно. Я там объяснил почему.


Игорь Шевченко   (2005-11-18 13:23:00) [17]

Суслик ©   (18.11.05 12:58) [17]

Вместо перехода на следующую строку в гриде ?

Так посмотри, как в grid.Keydown сделано

     VK_TAB:
       if not (ssAlt in Shift) then
       repeat
         if ssShift in Shift then
         begin
           Dec(NewCurrent.X);
           if NewCurrent.X < FixedCols then
           begin
            //Вот в этом месте тебе надо передать событие родительскому компоненту             
             NewCurrent.X := ColCount - 1;
             Dec(NewCurrent.Y);
             if NewCurrent.Y < FixedRows then NewCurrent.Y := RowCount - 1;
           end;
           Shift := [];
         end
         else
         begin
           Inc(NewCurrent.X);
           if NewCurrent.X >= ColCount then
           begin
            //Вот в этом месте тебе надо передать событие родительскому компоненту             
             NewCurrent.X := FixedCols;
             Inc(NewCurrent.Y);
             if NewCurrent.Y >= RowCount then NewCurrent.Y := FixedRows;
           end;
         end;
       until TabStops[NewCurrent.X] or (NewCurrent.X = FCurrent.X);


Суслик   (2005-11-18 12:58:00) [18]

Различия в том, что при достижении конца списка редактируемых ячеек происходит переход к следующему за гридом контролу.


Игорь Шевченко   (2005-11-18 12:56:00) [19]

Суслик ©   (18.11.05 12:47) [15]

Просто в приведенном мной примере грид определяет, какую ячейку надо выбрать после табуляции. Ты можешь объяснить, в чем различия ?


Суслик   (2005-11-18 12:47:00) [20]

нет


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо