Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "WinAPI";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];

Вниз

Вопрос про GetKeyState   Найти похожие ветки 

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

Добный день.

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-14 16:08) [1]


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


Можно узнать, зачем требуется определить, как попал фокус ?


 
Суслик ©   (2005-11-14 16:16) [2]

можно.
1 Есть грид (свой, но по мотивам TCustomGrid)
2 В гриде есть inplace editor.
3 Editor появляется только в момент редактирования ячейки.
4 Визуально ячейка, в которой может появить editor выглядит неотличимо от editor (т.е. рисуется с такой же рамкой).
5 Хочу сделать так, чтобы при попадании фокуса на грид выбиралась нужная ячейка - либо первая с конца, либо первая сначала.


 
Leonid Troyanovsky ©   (2005-11-14 16:28) [3]


> Суслик ©   (14.11.05 16:16) [2]

> 5 Хочу сделать так, чтобы при попадании фокуса на грид выбиралась
> нужная ячейка - либо первая с конца, либо первая сначала.


OnSelectCell (сработает, если мышью).
Хотя, все это смахивает на bad design.
Если, конечно, я чего-то понял.

--
Regards, LVT.


 
Суслик ©   (2005-11-14 16:32) [4]

OnSelectCell тут проде ни при чем. Мне как раз надо клавиатурой.

bad-bad design. Согласен.

Я даже почти от этого отказался.

Вообще вопрос стал уже более теоретический. С какой момент определяется результат выполнения GetKeyState. Насколько я понимаю все кроется в этой фразе

The key status returned from this function changes as a thread reads key messages from its message queue.

из описания GetKeyState. Т.е. значение GetKeyState определяется в момент вызова GetMessage или PeekMessage? И если поток не читает очередь, то значение GetKeyState не меняется?


 
Игорь Шевченко ©   (2005-11-14 16:38) [5]

Суслик ©   (14.11.05 16:16) [2]

Про то, что есть грид, я понял. А вот дальше увы не понял, какой именно смысл узнавания каким образом грид получил фокус.


 
Суслик ©   (2005-11-14 16:50) [6]

смысл в имитации, как будто редактируемые поля сделаны отдельностоящми editaми, тогда как они сделаны посредствов специальной прорисовки ячеек + inplace editor в момент перехода к ячейке.

Это сейчас уже есть - внешне не отличишь. Но вот то,что перебор по tab и shift+tab работает неверно выдает подмену.


 
Leonid Troyanovsky ©   (2005-11-14 16:55) [7]


> Суслик ©   (14.11.05 16:32) [4]


> OnSelectCell тут проде ни при чем. Мне как раз надо клавиатурой.

Дык, фокус перемещенный клавиатурой с другого контрола
OnSelectCell не вызовет.

> из описания GetKeyState. Т.е. значение GetKeyState определяется
> в момент вызова GetMessage или PeekMessage? И если поток
> не читает очередь, то значение GetKeyState не меняется?

Ну, если не читает, то и не меняется.
Только, то время, когда какие-то изменения имели место, например,
при изменении фокуса (путем нажатия Tab), как раз подходит для
GetKeyState (vs GetAsyncKeyState).

--
Regards, LVT.


 
Игорь Шевченко ©   (2005-11-14 16:56) [8]

Суслик ©   (14.11.05 16:50) [6]

А эта...если в обычном гриде поставить AlwaysShowEditor то вроде будет аналогичное поведение ? Заодно и посмотри, как там сделано.

Или объясни подробнее, а то, честно говоря, непонятно.


 
Leonid Troyanovsky ©   (2005-11-14 17:04) [9]


> Суслик ©   (14.11.05 16:50) [6]
> смысл в имитации, как будто редактируемые поля сделаны отдельностоящми
> editaми, тогда как они сделаны посредствов специальной прорисовки
> ячеек + inplace editor в момент перехода к ячейке.


А..
Может я опять не понял, но тебе нужна собс-ная реализация Editor.
А раз так, то надо погуглить CreateEditor TInplaceEdit and so on.

--
Regards, LVT.


 
GuAV ©   (2005-11-16 21:01) [10]

В WM_SETFOCUS передаётся бывший владелец фокуса как параметр.
Т.е. можно использовать разность TabOrder для определения направления смены фокуса.


 
Суслик ©   (2005-11-17 11:12) [11]

я сначала так и сделал :) рад был!

но если подумать, то это способ не работает в случае когда всего два контола, способных получать фокус.


 
GuAV ©   (2005-11-17 20:00) [12]


> но если подумать, то это способ не работает в случае
> когда всего два контола, способных получать фокус.


В продолжение извращений, можно создать невидимый контрол, в его обработчике SETFOCUS передавать фокус далее, направление определять этим же способом. Чтобы это работало при наличии только одного грида, делать таких контролов два.

Вообще, если только два контрола, следет ли отличать направление ?

=======
- Что это вы мне принесли - борщ или помои ?
- А вы что, сами не можете определить ?
- Нет !
- Тогда какая вам разница ?


 
Суслик ©   (2005-11-18 11:35) [13]


> Вообще, если только два контрола, следет ли отличать направление
> ?

Надо.

ДЛЯ ВСЕХ поясню еще раз задачу.

1. Есть некое извращение, в котором прощу коллег помочь.
2. Представьте себе контрол (некий СВОЙ грид), который некоторые ячейки прорисовывает функцией winapi DrawFrame таким образом, что он внешне НЕ ОТЛИЧИМЫ от TEdit.
3. Т.е. при взгляде на grid кажется как будто на нем набросано несколько TEdit, хотя реально TWinControl один - сам Grid.
4. В отношении редактирования ячеек мой грид организован также как TStringGrid - есть InplaceEdit. При входе на ячейку, которая редактируема (и, как было сказано в п.2 внешне неотличима от TEdit) появлятеся InplaceEdit.
5. При этом есть желание клавиши TAB и SHIFT+TAB заставить работать внутри грида таким образом, что как будто редактируемые ячейки не просто прорисованы как TEdit, а TEdit"ом и является, т.е. получают фокус по TAB и SHIFT+TAB.
6. Перемещение по TAB не представляет проблем. Для этого гриду достаточно обрабатывать:
  * WM_GETDLGCODE, возвращая DLGC_WANTTAB,
  * KeyDown - при нажатии на vk_tab искать следующую (вправо вниз или влево вврех, если shift+tab) редактируему ячейку. Если дошли до конца то вызывать SelectNext.
7. В итоге получает полнейшая имитация TEdit"ов. Т.е. внешне отличить мое творчество от группы TEdit"ов сложно.
8. Но есть одно НО. В случае получения gridом фокуса хотелось бы знать спереди или сзади он пришел. Если спереди, то нужно фокусировать редактируемую первую ячейку, если сзади, то последнюю.

Задача состоит в том, чтобы как гарантировано понять, что фокус пришел спереди или сзади, т.е. по TAB или по SHIFT+TAB?

PS. Т.к. смену фокуса делает родительская форма в CMDialogKey, то можно было бы написать своего потомка формы. Но хотелось бы обойтись без этих извращений. Т.е. направление фокуса должен уметь определять сам грид.

Заранее огромное спасибо за ответ.


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

Суслик ©   (18.11.05 11:35) [13]

А разве обычный грид с включенными опциями goTabs и goAlwaysShowEditor ведет себя не так, как тебе надо ?


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

нет


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

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

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


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

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


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

Суслик ©   (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 14:12) [19]

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

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

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

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


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

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

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

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


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


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

Ура! Я понят!


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


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

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

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


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

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

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


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

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


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


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


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


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


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

Grid.TabOrder = 0
AnotherControl.TabOrder = 1

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

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

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

--------

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

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


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


> 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 16:16) [26]

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

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

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

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

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

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

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


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


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


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


 
GuAV ©   (2005-11-18 16:35) [28]

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

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


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


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


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

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

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


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

GuAV ©   (18.11.05 16:35) [28]


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


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


 
GuAV ©   (2005-11-18 16:54) [31]


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

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


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


> AttachThreadInput

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


 
GuAV ©   (2005-11-18 17:38) [33]

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

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

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


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


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

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

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



Страницы: 1 вся ветка

Форум: "WinAPI";
Текущий архив: 2006.01.29;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.035 c
15-1136277637
PARUS
2006-01-03 11:40
2006.01.29
Горячие клавиши


1-1135742321
Ярослав
2005-12-28 06:58
2006.01.29
Создание инсталлятора


15-1136072053
Cerberus
2006-01-01 02:34
2006.01.29
Помогите найти человека.


2-1137328158
Ell
2006-01-15 15:29
2006.01.29
Как сымитировать нажатие клавиши?


15-1136883578
Чапаев
2006-01-10 11:59
2006.01.29
SQL для MDB





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский