![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Вопрос про GetKeyState [D6]
Суслик © (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]нет