14

バックグラウンド:

TCustomControlフォーカスを取得でき、内部要素がいくつかあるクラスから派生したコントロールに取り組んでいます。これらの内部要素は、ユーザーがカーソルでそれらをホバーすると強調表示され、それらを選択したり、移動したりできます。今問題に...

問題:

CTRLユーザーが、ALTまたはSHIFT修飾子を保持している場合、(たとえば)フォーカスされた要素でさまざまなアクションを実行しています。私が望むのは、ユーザーが要素をホバーしてインスタンスCTRLキーを保持している場合、マウスカーソルを変更することです。KeyDownKeyUpメソッドをオーバーライドして、それらのKeyパラメータが に等しいかどうかを確認するだけですVK_CONTROL。このようにコードで:

procedure TMyCustomControl.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if Key = VK_CONTROL then
    Screen.Cursor := crSizeAll;
end;

procedure TMyCustomControl.KeyUp(var Key: Word; Shift: TShiftState);
begin
  inherited;
  if Key = VK_CONTROL then
    Screen.Cursor := crDefault;
end;

CTRLキーが押されて離されたかどうかを確認する最良の方法ではない場合でも(たとえば、既存のShift状態パラメーターが原因で)、コントロールにフォーカスがある場合は期待どおりに機能し、取得することもできますが...

私の目的は、ユーザーがコントロール (または正確には、その中の特定の要素) をホバーしたときにマウスカーソルを変更し、CTRLコントロールにフォーカスがない場合でもそのキーを保持することです。MouseMove言うことができるので、メソッドをオーバーライドして、そこで修飾子の状態を要求するだけです。そしてそれは方法でしょうが...

ユーザーがコントロールの上にマウス カーソルを置いたまま、そのCTRLキーを押して離すとどうなりますか? それは私のコントロールに対してマウスの動きやキーを押すイベントを生成しませんか、それとも間違っていますか? さて、私の質問は非常に明白です...

質問:

コントロールにフォーカスがなく、ユーザーがマウスで動かない場合、修飾キーの変更を検出するにはどうすればよいですか? 私はこれらの 2 つのオプションについて考えていましたが、私が見逃したものがあることを願っています。

  • キーボード フック - 信頼性は高いですが、私にはやり過ぎに見えます
  • タイマーを使用したモディファイヤの状態の定期的なチェック - 遅れてはいられない

では、現在フォーカスされていないコントロールの修飾キーの変更をどのように検出しますか?

4

4 に答える 4

8

コントロールがフォーカスされていない場合、それ自体のキー イベントはトリガーされません。ただし、代わりにできることは、コントロールにプライベートTApplicationEventsコンポーネントを内部でインスタンス化し、そのOnMessageイベントを使用して、処理のためにコントロールにディスパッチされる前に、メイン メッセージ キューから取得されるキー イベントを検出することです。次に、マウスがコントロール上にあるかどうかを確認し (GetMessagePos()代わりに使用するGetCursorPos()Screen.CursorPos、メッセージが生成されたときにマウス座標を取得するために使用することをお勧めします。メッセージが遅延している場合に備えて)、コントロールの独自のCursorプロパティ (プロパティではありませんScreen.Cursor)を更新します。必要に応じて。

于 2013-10-24T17:03:22.953 に答える
3

レミーの答えはおそらくあなたの解決策ですが、コントロールにカプセル化するという制限なしでこれを実行しようとしていて、ここにいることに気付いた場合:

以下に示すように、3 段階のプロセスでこれを処理できます。

ここで重要なことは次のとおりです。

  1. 画面のカーソルではなく、コントロールのカーソルを設定する
  2. フォームのKeyPreviewプロパティを使用する
  3. カーソルの下のコントロールを見つける

プロセスを説明するためにボタンを使用しました。KeyPreview必ずフォームをに設定してくださいTrue

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  myControl: TControl;
begin
  // If they pressed CTRL while over the control
  if ssCtrl in Shift then
  begin
    myControl := ControlAtPos(ScreenToClient(Mouse.CursorPos), False, True);
    // is handles nil just fine
    if (myControl is TButton) then
    begin
      myControl.Cursor := crSizeAll;
    end;
  end;
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  myControl: TControl;
begin
  // If they released CTRL while over the control
  if not(ssCtrl in Shift) then
  begin
    myControl := ControlAtPos(ScreenToClient(Mouse.CursorPos), False, True);
    if (myControl is TButton) then
    begin
      myControl.Cursor := crDefault;
    end;
  end;
end;

procedure TForm1.Button1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  // If they move over the button, consider current CTRL key state
  if ssCtrl in Shift then
  begin
    Button1.Cursor := crSizeAll;
  end
  else
  begin
    Button1.Cursor := crDefault;
  end;
end;
于 2013-10-24T16:44:57.437 に答える
3

フックを使用するよりもやり過ぎではないかどうかはわかりませんが、1 つのオプションは「生の入力」を使用することです。それに応じてコントロールを登録すると、アクティブでないときにも入力を受け取ります。決定するサンプル実装..:

type
  TMyCustomControl = class(TCustomControl)
    ..
  protected
    ..
    procedure CreateWindowHandle(const Params: TCreateParams); override;
    procedure WMInput(var Message: TMessage); message WM_INPUT;
  ..
  end;

uses
  types;

type
  tagRAWINPUTDEVICE = record
    usUsagePage: USHORT;
    usUsage: USHORT;
    dwFlags: DWORD;
    hwndTarget: HWND;
  end;
  RAWINPUTDEVICE = tagRAWINPUTDEVICE;
  TRawInputDevice = RAWINPUTDEVICE;
  PRawInputDevice = ^TRawInputDevice;
  LPRAWINPUTDEVICE = PRawInputDevice;
  PCRAWINPUTDEVICE = PRawInputDevice;

function RegisterRawInputDevices(
  pRawInputDevices: PCRAWINPUTDEVICE;
  uiNumDevices: UINT;
  cbSize: UINT): BOOL; stdcall; external user32;

const
  GenericDesktopControls: USHORT = 01;
  Keyboard: USHORT = 06;
  RIDEV_INPUTSINK = $00000100;

procedure TMyCustomControl.CreateWindowHandle(const Params: TCreateParams);
var
  RID: TRawInputDevice;
begin
  inherited;

  RID.usUsagePage := GenericDesktopControls;
  RID.usUsage := Keyboard;
  RID.dwFlags := RIDEV_INPUTSINK;
  RID.hwndTarget := Handle;
  Win32Check(RegisterRawInputDevices(@RID, 1, SizeOf(RID)));
end;

type
  HRAWINPUT = THandle;

function GetRawInputData(
  hRawInput: HRAWINPUT;
  uiCommand: UINT;
  pData: LPVOID;
  var pcbSize: UINT;
  cbSizeHeader: UINT): UINT; stdcall; external user32;

type
  tagRAWINPUTHEADER = record
    dwType: DWORD;
    dwSize: DWORD;
    hDevice: THandle;
    wParam: WPARAM;
  end;
  RAWINPUTHEADER = tagRAWINPUTHEADER;
  TRawInputHeader = RAWINPUTHEADER;
  PRawInputHeader = ^TRawInputHeader;

  tagRAWKEYBOARD = record
    MakeCode: USHORT;
    Flags: USHORT;
    Reserved: USHORT;
    VKey: USHORT;
    Message: UINT;
    ExtraInformation: ULONG;
  end;
  RAWKEYBOARD = tagRAWKEYBOARD;
  TRawKeyboard = RAWKEYBOARD;
  PRawKeyboard = ^TRawKeyboard;
  LPRAWKEYBOARD = PRawKeyboard;

//- !!! bogus declaration below, see winuser.h for the correct one
  tagRAWINPUT = record
    header: TRawInputHeader;
    keyboard: TRawKeyboard;
  end;
//-
  RAWINPUT = tagRAWINPUT;
  TRawInput = RAWINPUT;
  PRawInput = ^TRawInput;
  LPRAWINPUT = PRawInput;

const
  RIM_INPUT = 0;
  RIM_INPUTSINK = 1;
  RID_INPUT = $10000003;
  RIM_TYPEKEYBOARD = 1;
  RI_KEY_MAKE = 0;
  RI_KEY_BREAK = 1;

procedure TMyCustomControl.WMInput(var Message: TMessage);
var
  Size: UINT;
  Data: array of Byte;
  RawKeyboard: TRawKeyboard;
begin
  if (Message.WParam and $FF) in [RIM_INPUT, RIM_INPUTSINK] then
    inherited;

  if not Focused and
      (WindowFromPoint(SmallPointToPoint(SmallPoint(GetMessagePos))) = Handle) and
      (GetRawInputData(Message.LParam, RID_INPUT, nil, Size,
      SizeOf(TRawInputHeader)) = 0) then begin
    SetLength(Data, Size);
    if (GetRawInputData(Message.LParam, RID_INPUT, Data, Size,
        SizeOf(TRawInputHeader)) <> UINT(-1)) and
        (PRawInput(Data)^.header.dwType = RIM_TYPEKEYBOARD) then begin
      RawKeyboard := PRawInput(Data)^.keyboard;

      if (RawKeyboard.VKey = VK_CONTROL) then begin
        if RawKeyboard.Flags and RI_KEY_BREAK = RI_KEY_BREAK then
          Cursor := crDefault
        else
          Cursor := crSizeAll; // will call continously until key is released
      end;
      // might opt to reset the cursor regardless of pointer position...


      if (RawKeyboard.VKey = VK_MENU) then begin
        ....
      end;

    end;

  end;
end;
于 2013-10-27T02:57:04.727 に答える