戻る ホーム 上へ 進む

パネル系コンポーネントの不具合対処

Tip

Delphi のフォームはダイアログボックスと良く似た動きをしますが、矢印キーを 使ってフォーカスを移動するときに重大な不具合が有ります

Windows のダイアログではフォーカスの移動先がグループボックスの場合、グループボックス自体にはフォーカスは移動せず、グループボックスの「中」のコントロールにフォーカスが移動します。

ところが Delphi のフォームの場合は、移動先がパネル系/グループボックス系のコントロールの場合、パネルやグループボックスそのものにフォーカスが移動してしまいます。以下の図を見てください。フォーム上には Button コントロールと RadioGroup コントロールが有り、Button コントロールにフォーカスが有ります。

tips13-2
「Button にフォーカスがある状態」

この状態で矢印キーを押すと、下図のようにフォーカスがどこに有るか判らない不思議な状態になってしまいます。

tips13-2
「フォーカスはどこへ ?」

この問題を回避するには、パネル系/グループボックス系のコントロールの OnEnter イベントハンドラに、以下のようにコードを書いておく必要があります。

type
  TWinControlDummy = class(TWinControl);

procedure TForm1.RadioGroup1Enter(Sender: TObject);
var
  p: TWinControlDummy;
begin
  if ActiveControl = Sender then
  begin
    p := TWinControlDummy(ActiveControl.Parent);
    if p <> Nil then
      ActiveControl := p.FindNextControl(ActiveControl, True, True, True);
  end;
end;

解説

この問題はけっこう根が深いバグです。VCL はフォーカスの移動を FindNectControl メソッドを使って行いますが、FindNextControl は CanFocus メソッドが False を返すようなコントロールを選ばないのでパネル系コントロールの CanFocus が False なら問題ありません。実際パネル系コンポーネントはマウスでクリックしてもフォーカスを受け取らないので、CanFocus が False を返しても良いように思えます。しかし、TWinControl の CanFocus は Visible で Enabled な場合 True を返すので、パネル系のコントロールも CanFocus が True になってしまいます。

Delphi 4 までは CanFocus メソッドは virtual/dynamic では無かったので変更できませんでした。しかし Delphi 5 以降は dynamic になったので、コントロール側で自由に実装を変更できるようになりました。しかし残念ながら Delphi 6 でも未だにパネル系コントロールは Visible and Enabled を返します。

Tip のコードを説明します。

コントロールはフォーカスを受け取ると OnEnter イベントが起きますが、パネル系やグループ系など、自分の中にコントロールを含む事ができるコントロールでは、自分の子コントロールにフォーカスが移ったときも OnEnter イベントが発生します。従って、本当に自分にフォーカスが来ているかどうかは ActiveControl プロパティ でチェックしなければなりません。

TWinControl の FindNextControl メソッドは配下のコントロールのタブ順のリストを作り、指定されたコントロールの「次」のコントロールを探してくれます。FindNextControl は指定されたコントロールを起点にして指定された方向にリストを探索し、条件に合う最初のコントロールを返します。

function FindNextControl(CurControl: TWinControl; 
  GoForward, CheckTabStop, CheckParent: Boolean): TWinControl;

パラメータは上の通りですが、GoForward は探索の向きを表し True は次(Forward)、False は前(Backword)を指定します。CheckTabStop と CheckParent は隣のコントロールを選ぶときの条件で、チェックは以下のように行われます。

  1. コントロールの CanFocus メソッドが True を返すかチェックする。
  2. CheckTabStop = True ならば、コントロールの TabStop プロパティ が True であるかチェックする
  3. CheckParent = True の場合、親が自分自身(上の場合フォーム)かチェックする。

Tip のコード(OnEnter のイベントハンドラ)はまず、自分にフォーカスが移りつつあるかを確かめます。もしそうなら、自分の親のFindNextControlメソッドを使って、自分と同じ親お持ち、TabStop=True なコントロールにフォーカスをセットしなおしてもらいます。

戻る ホーム 上へ 進む inserted by FC2 system