Migaro. 技術Tips

                       

ミガロ. 製品の技術情報
IBMiの活用に役立つ情報を掲載!


【Delphi】DBGridの上位クラス参照処理

TDBGridを使ってグリッドの処理を記述する時、
「DBGridには無いけどあのプロパティがあれば開発が楽なのに……!」
「どうしてStringGridでは出来るあの処理がDBGridでは出来ないんだ……!」
といった悩みが時にはあることでしょう。

今回はそのような場合に、
上位クラスの処理を参照して解決する手順を紹介します。

 


(準備)継承クラスの宣言

継承クラスを宣言すると、継承元のクラスのprotected部にある
プロパティやメソッドが使用できるようになります。

今回はTDBGridには存在しないけれど、
上位クラスの「TCustomGrid」には存在するクラスを使用する例を紹介します。

まずはフォームの宣言部(type節)にて、
以下のようにTCustomGridクラスを継承した型を定義します。
本記事では「TDBGX」という名前にしていますが、
既存のクラス名と重複しなければ任意の名前で構いません。

// TCustomGridクラスを継承することで、その中のProtected宣言を使用可能にする。
type
  TDBGX = class(TCustomGrid)
end;

type //(ここからは通常の宣言)
  TForm1 = class(TForm)
  ・・・

ここで定義したクラスTDBGXはTCustomGridを継承して定義されていますが、
対象のTDBGridもTCustomGridを継承して定義されています。

TDBGrid側の処理と相反するものでない限りは、
このTDBGridをTDBGXでキャストすることで、
TDBGXに定義されたプロパティを利用することが可能です。

次項から例を2つ紹介します。

 


例①:対象TDBGridの入力中モード切り替え

TDBGridの上位クラスであるTCustomDBGridには、入力状態になっているかどうかを
示すプロパティ「EditorMode」が存在します。
(※EditorModeはPublic宣言のため、特に何もしなくても使用可能です)

しかし、このEditorModeのオンオフを強制的に切り替える
ShowEditor」「HideEditor」というメソッドは
さらに上位のTCustomGridクラスのProtected宣言に存在します。

これらを使いたい場合に、普通に「DBGrid1.ShowEditor;」といった
記述をすると、下図のようなコンパイルエラーになってしまいます。

[dcc32 エラー] Unit1.pas(833): E2362 プロテクトシンボル TCustomDBGrid.ShowEditor にアクセスできません

以下のソースのようにDBGrid1をTDBGXでキャストすることで、
コンパイルエラーを回避して処理を実行できます。

procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
  // DBGrid1が入力中ならメッセージを出す
  if (DBGrid1.EditorMode) then
  begin
    ShowMessage('編集中');
  end
  // そうでない場合は入力中にする
  else
  begin
    TDBGX(DBGrid1).ShowEditor;
  end;
end;

 


例②:対象TDBGridのマウス位置からレコード番号を調べる

マウスのイベント(OnMouseMoveなど)で、
TStringGridではマウスの位置から行番号や列番号を調べられるのに対して
TDBGridではそれが難しくなっています。

いずれもMouseCoord関数を使うことで、
OnMouseMoveなどのイベントで取得できる引数(X, Y: Integer)から
セル座標を取得できるのですが、その結果に差異があります。

<ロジック例>

procedure TForm1.DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  iRN: Integer; // 行番号の計算用
  ZZ: TGridCoord; // セル座標計算用
begin
  ZZ := DBGrid1.MouseCoord(X, Y);
  ・・・

上記の例でロジックを通った際に、マウスの位置から計算されて
変数「ZZ.X」には横セル座標、「ZZ.Y」には縦セル座標がセットされます。

しかし、TStringGridでは実際の縦セル座標が取得できるのに対して
TDBGridではデータセットのレコードの値を表示している都合上、
スクロールを無視した、今画面に見えている範囲内だけでの縦座標がセットされます。
(横座標は非表示列が無い限りは正しく表示されます)

また、TStringGridではRowプロパティで現在の選択セルの行番号が取得できますが、
TDBGridではRowを使用しようとすると先程と同様のコンパイルエラーになっていまいます。

(E2362 プロテクトシンボル TCustomGrid.Row にアクセスできません)

ここで先ほどと同じように、TDBGXでキャストすると……

先ほどの「ZZ.Y」と同じく、今画面に見えている範囲内だけでの選択セルの行番号が取得できます。
ここまでで得られた情報から、マウスの位置から行番号が取得可能です。

  • ZZ.Y(マウスカーソルがある位置の行番号)
  • TDBGX(DBGrid1).Row(今見えている選択状態のセルの行番号)
  • データセット.RecNo(選択状態のセルのRecNo=実際のレコード番号)

この3つを組み合わせて、以下のように記述します。
(※データセットは、DBGrid1に紐づいているCDSやFDQuery等を指します。)

// [マウスカーソルのある位置の行番号]-[今見えている選択セルの行番号]+[選択セルのRecNo]
iRN := ZZ.Y - TDBGX(DBGrid1).Row + データセット.RecNo;

すると、TDBGridのMouseDownやMouseMoveイベントにおいても、
マウスの直下にあるセルの実際の行番号が取得できるようになります。

 

以下はここまでのロジックを使って、MouseMove時に
マウス直下の行番号・列番号と、ついでにフィールドIDも取得するサンプルです。

{*******************************************************************************
  マウスムーブ時に(RowSelectであっても)マウス通過中のセル情報を取得できるサンプル
*******************************************************************************}
procedure TForm1.DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  ZZ: TGridCoord;
  FN, RN: String;
begin

  ZZ := DBGrid1.MouseCoord(X, Y);

  // マウスカーソルのある位置のセルの行番号を取得
  if (ZZ.Y >= 0) then
  begin
    // [マウスカーソルのある位置の行番号]-[今見えている選択セルの行番号]+[選択セルのRecNo]
    RN := IntToStr(ZZ.Y - TDBGX(DBGrid1).Row + ds.DataSet.RecNo);
  end
  else
  begin // セル以外の場所をマウス通過時はZZ.Yが-1になる
    RN := '';
  end;

  // 列番号と列名を取得
  if (ZZ.X >= 1) then
  begin
    FN := DBGrid1.Columns[ZZ.X - 1].FieldName;
    FN := IntToStr(ZZ.X) + '_' + FN;
  end
  else
  begin
    FN := '';
  end;

  // 結果表示(フォームのCaptionに表示)
  Self.Caption := Trim(RN + '  ' + FN);
end;


※実行時イメージ

 


参考リンク