VCL の TCustomGrid.KeyDown の実装

VCL カテゴリを作ろうかと思ったけれどたぶん続かないし、きっと面白みも無いのでやめておきます。ちょっと苦労した話。説明を一切省いているため VCL を知らない方には面白く無いと思います。

TStringGrid *1を使って、固定セルに対しての選択や編集を可能にする処理を実装していました。ここでいう固定セルとは FixedRows/FixedCols で指定したセルのことです。マウスでの編集や選択は、 OnMouseDown/Move/Up でそれぞれ自力で Selection プロパティを操作してやれば、固定セルの選択も実はできるので簡単でした。

で、次はキーボードからのセルの移動でも、固定セルに移動するよう OnKeyDown を実装してテストしてみたところ、なぜか移動しない。マウスで固定セルを選択状態にしてから、→などで隣に移ろうとした時も、固定セルから追い出されてしまう。

しばし悩むも、頭抱えているだけで問題は解決しないので、 VCL のソースを読むことに。最近しょっちゅう読んでいるので、 Pascal も扱えるんじゃないかと言う錯覚を覚えるようになってきてしまいましたが、もちろんただの錯覚です……。それはさておき、 TCustomGrid のソースを追いかけた結果、 TCusomGrid.KeyDown メソッドで以下のような記述を発見。

procedure TCustomGrid.KeyDown(var Key: Word; Shift: TShiftState);
var
  ... 略 ...
  procedure Restrict(var Coord: TGridCoord; MinX, MinY, MaxX, MaxY: Longint);
  begin
    with Coord do
    begin
      if X > MaxX then X := MaxX
      else if X < MinX then X := MinX;
      if Y > MaxY then Y := MaxY
      else if Y < MinY then Y := MinY;
    end;
  end;

begin
  .. 処理全略 ...
  Restrict(NewTopLeft, FixedCols, FixedRows, MaxTopLeft.X, MaxTopLeft.Y);
end

要するに Restrict 関数*2で、現在位置が固定セルに食い込んでいた場合はその外に追い出してしまう処理がグリッドの方のキーダウンに書かれていて、グリッドを使う側でどう頑張ってもキーボードが押された時点で固定セルの選択はどうあっても解除されてしまうと。

恐らくこれはもうどうしようもない、ということで、仕方が無いので TStringGrid を継承したクラスを作成し、 KeyDown メソッドをオーバーライドしてキーボードイベントを自分で実装してどうにか解決しましたが、なんだか思ったより面倒なことになってしまいました。まあ、グリッドを使う側での泥臭い実装を新しく作ったグリッドクラスに押し込むこともできたので、良しとするか……というか、たぶん、こちらが正攻法なんでしょうけれどね。

ちなみにコンポーネントにすると面倒なことになるのでコンポーネントにはしていません。デザインが大変だけれどまあ気にしません……。使いまわす予定もないし。

*1:TDrawGrid じゃないのは、要するに手抜き、というか、去年これを書いた時にはオーナードローなんてほとんどやらない人だったのだ

*2:正確には関数内関数