標記の件、
マウスホイールのイベント時に、ctrlキーが押下かどうかで処理を分けようとしています。
(Delphi IDEとかでもshift、ctrl、何も無し、ではエディタのマウスホイールスクロール動作が異なるように。)
ところがRadioGroupを置くと誤動作するようになります。
(ctrlキーを押していてもctrlキー無しと判定されるようになる)
対処法をご存じないでしょうか?
以下再現コードです。
RadioGroupあり、なしで動作が変わります。
(RadioGroupのItemIndexは-1以外でないと再現できません。)
procedure TForm1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
if ssCtrl in shift then
Label1.Caption := 'Ctrl + down'
else
Label1.Caption := ' down';
end;
procedure TForm1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState;
MousePos: TPoint; var Handled: Boolean);
begin
if ssCtrl in shift then
Label1.Caption := 'Ctrl + up'
else
Label1.Caption := ' up';
end;
フォームにパネルをはりつけて
その上にラジオボタンを貼り付ける
というのはどうでしょうか?
原因はわかりませんがたぶん仕様ですよね。
GetAsyncKeyState(VK_CONTROL)とかGetKeyboardStateで調べればいいのでは?
VCLの問題なのかは調べてません。
ちょっと試した感じでは、
RadioGroup特融ではなくRadioButton,CheckButton,Editで同じ動作を確認しました。
早速の回答ありがとうございます。
どやささん、Panelの上にあるかどうかは関係ないみたいです。
monaaさん、
GetKeyboardStateでできました。ありがとございます。
http://www.swissdelphicenter.ch/torry/showcode.php?id=1002
var
State: TKeyboardState;
begin
GetKeyboardState(State);
if ((State[VK_CONTROL] and 128) <> 0) then
・・・・
当方、Delphi5です。
バグなのか仕様なのかわかりませんが、
FormMouseWheelDownとFormMouseWheelUp で、以下の一行を追加してみてください。
Handled := True;
これで、'Ctrl + up'、'Ctrl + down' が表示されるでしょう。
で、どんなメッセージの流れになっているのか追ってみると・・・
(説明が通じるのかわかりませんが)
1)フォーカスのあるコントロールでWM_MOUSEWHEEL を受け取ります
(TWinControl.WMMouseWheel)
2)受け取ったコントロールは、まず自分が乗っているフォームに
WM_MOUSEWHEELを投げます(TWinControl.MouseWheelHandler)。
このとき、CM_MOUSEWHEELを投げるために、ShiftState の変換を行います。
TCMMouseWheel(Message).ShiftState := KeysToShiftState(Message.Keys);
これで、Message.WParamが変更されます。
3)フォームは、フォーカスのあるコントロールにCM_MOUSEWHEELを送信します
(TCustomForm.MouseWheelHandler)。
WParam,LParamは、WM_MOUSEWHEELで受け取った値をそのまま使用。
4)CM_MOUSEWHEELを受け取ったコントロールは、OnMouseWheel〜イベントを
発生させたりして、処理されたか判定(イベント引数のHandledね)
Handled = True にならない場合、Parent に、CM_MOUSEWHEELを送ります。
5)ここでParentが無くなるか、Handled=Trueになるまで処理します。
フォームのOnMouseWheel〜が発生するのは、ココ
ここでは、正常にShiftStateが入ってきています。
6)以上が、フォーカスのあるコントロールでWM_MOUSEWHEELを受け取った時の、VCLの処理。
ここまででHandled = True にしていない場合、TWinControl.DefaultHandler から、
CallWindowProcを呼んで、OS側に処理をおまかせします。
7)OSは、メッセージ処理が終わっていないと判断して、(たぶん)Parentに
向けてWM_MOUSEWHEELを投げます。
(もしかしたら、最上位Parentのフォームにいきなり投げるのかも)
8)Parentは、1)からの処理を、同じように行いますが、ここで注意すべきなのが、
2)で、WParamを書き換えている点。
書き換えられたWParamを、CM_MOUSEWHEELを投げるために、再度書き換えを
行うため、ShiftStateがおかしくなります。
おかしくなったShiftStateで、OnMouseWheel〜イベントが発生するから、
ShiftStateが正しくないのは当然。
2回目(以降)のイベントで、誤った結果を表示し、それしか見えないから
正しく入ってきていないと思ってしまう。
TWinControl.WMMouseWheel が、以下のようになっていたら、大丈夫と思う。
TCMMouseWheel(Message).ShiftState := KeysToShiftState(Message.Keys);
MouseWheelHandler(TMessage(Message));
if Message.Result = 0 then inherited;
↓
TCMMouseWheel(Message).ShiftState := KeysToShiftState(Message.Keys);
MouseWheelHandler(TMessage(Message));
if Message.Result = 0 then
begin
//逆変換(ShiftStateToKey)の関数は自作するしかないのかな?
TCMMouseWheel(Message).ShiftState := ShiftStateToKey(Message.Keys);
inherited;
end;
ofzさん、詳しく調べていただき有難うございました。
>で、どんなメッセージの流れになっているのか追ってみると・・・
>(説明が通じるのかわかりませんが)
・・・すみません。実はほとんど付いて行けません(汗
ただ、「Handled をtrueにするだけ」の方が副作用?が少なそうでした。
これまた理由は良く判りませんが
GetKeyboardState で判定すると1スクロールで複数回(3回程度)イベントが発生します。
これはまたこれで、すごく困りました・・・
Handled をtrueにする方法だと1スクロール1イベントにきちんとなります。
ツイート | ![]() |