DBGridを同期させる。
この時期、なぜ、XE2の新機能やFireMonkeyの試行錯誤をやらないの?と突っ込まれそうですが、古いプログラムの修正用にいろいろ調べて直しているからです(^-^)
図のように上下に配置したDBGridのタイトルの幅変更、移動、セルの移動そして横スクロールを同期させます。Excelのように上段のDBGridは横スクロールバー無し、下段のDBGridはタイトル無しとします。若干、問題が残っていますが、とりあえずは良しとしましょう。
設計時
問題点
DBGridのOnMouseDownでは、タイトル間のクリックが拾えません。そのため、下段のDBGridへのタイトル幅変更時のライン描画用フラグがややこしくなっています。又、サブクラスには、いつものようにMr.XRAYさんところのコンポーネントを利用させて頂きました。
サブクラス用コンポーネント
Delphi Library [Mr.XRAY]
SubClassUnit
http://homepage2.nifty.com/Mr_XRAY/Halbow/Notes/N004.html
20111005追記
DBGrid1のMouseUp、FormResizeの処理を下記に入れ替えると、そこそこ希望の動作になりました。スクロールバーは表示されるもののUpdateScrollBarを通していないので、レコード数が反映されたものではないですけどね。まあ、見た目的には問題ないです。
図のように上下に配置したDBGridのタイトルの幅変更、移動、セルの移動そして横スクロールを同期させます。Excelのように上段のDBGridは横スクロールバー無し、下段のDBGridはタイトル無しとします。若干、問題が残っていますが、とりあえずは良しとしましょう。
設計時
問題点
1.上段DBGridの縦スクロールバーがタイトルの幅を変えたり、移動させたりすると 消えてしまいます。マウスでスクロールさせると再び表示されるのですけど、 見た目に統一感がないですね。 2.タイトル幅の変更時のラインを下段のDBGridでも描画しています。しかしタイト ル間のクリック位置によって微妙にラインがずれることがあります。 3.カラム移動のラインまでは対応していません(^-^)
DBGridのOnMouseDownでは、タイトル間のクリックが拾えません。そのため、下段のDBGridへのタイトル幅変更時のライン描画用フラグがややこしくなっています。又、サブクラスには、いつものようにMr.XRAYさんところのコンポーネントを利用させて頂きました。
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Bde.DBTables, Vcl.ExtCtrls, Vcl.Grids, Vcl.DBGrids, SubClassUnit, Vcl.StdCtrls; type TForm1 = class(TForm) DBGrid1: TDBGrid; DBGrid2: TDBGrid; Splitter1: TSplitter; Table1: TTable; Table2: TTable; DataSource1: TDataSource; DataSource2: TDataSource; procedure FormCreate(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormResize(Sender: TObject); procedure DBGrid1ColEnter(Sender: TObject); procedure DBGrid2ColEnter(Sender: TObject); procedure DBGrid1ColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer); procedure DBGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure DBGrid1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormDestroy(Sender: TObject); private RowHeight: Integer; SubClass1: TSubClass; SubClass2: TSubClass; procedure SubClass1MessageAfter(Sender: TObject; var message: TMessage); procedure SubClass2MessageAfter(Sender: TObject; var message: TMessage); public end; var Form1: TForm1; implementation {$R *.dfm} type TDummyDBGrid = class(TCustomDBGrid); // 初期設定 procedure TForm1.FormCreate(Sender: TObject); begin // Tableの設定 Table1.DatabaseName := 'DBDEMOS'; Table1.TableName := 'country.db'; Table2.DatabaseName := 'DBDEMOS'; Table2.TableName := 'country.db'; Table1.Open; Table2.Open; // 常に編集モード DBGrid1.Options := DBGrid1.Options + [dgAlwaysShowEditor]; DBGrid2.Options := DBGrid2.Options + [dgAlwaysShowEditor] - [dgTitles]; // 縦スクロールバーのみ表示させます。 TDummyDBGrid(DBGrid1).ScrollBars := ssVertical; // サブクラスの設定 SubClass1:= TSubClass.Create(Self); SubClass1.TargetControl := DBGrid2; SubClass1.OnMessageAfter := SubClass1MessageAfter; // サブクラスの設定 - MouseDownの取得だけ SubClass2:= TSubClass.Create(Self); SubClass2.TargetControl := DBGrid1; SubClass2.OnMessageAfter := SubClass2MessageAfter; // 行の高さ RowHeight := TDummyDBGrid(DBGrid1).DefaultRowHeight; end; procedure TForm1.FormDestroy(Sender: TObject); begin Table1.Close; Table2.Close; end; // サイズ変更時の処理 procedure TForm1.FormResize(Sender: TObject); begin DBGrid1ColEnter(Self); end; // 起動時に強引に縦スクロールを表示させます。 procedure TForm1.FormShow(Sender: TObject); begin // 縦スクロールバーを表示させるための処理 // これをしないとマウスでスクロールさせるまで // スクロールバーが表示されません。 SendMessage(DBGrid1.Handle, WM_VSCROLL, SB_LINEDOWN, 0); SendMessage(DBGrid1.Handle, WM_VSCROLL, SB_LINEUP, 0); end; // カラムの同期 procedure TForm1.DBGrid1ColEnter(Sender: TObject); begin if (TDummyDBGrid(DBGrid2).LeftCol<> TDummyDBGrid(DBGrid1).LeftCol) then TDummyDBGrid(DBGrid2).LeftCol := TDummyDBGrid(DBGrid1).LeftCol; end; procedure TForm1.DBGrid2ColEnter(Sender: TObject); begin if (TDummyDBGrid(DBGrid1).LeftCol<> TDummyDBGrid(DBGrid2).LeftCol) then TDummyDBGrid(DBGrid1).LeftCol := TDummyDBGrid(DBGrid2).LeftCol; end; procedure TForm1.DBGrid1ColumnMoved(Sender: TObject; FromIndex, ToIndex: Integer); begin TDummyDBGrid(DBGrid2).ColumnMoved(FromIndex+1, ToIndex+1); { DBGrid2.Columns.BeginUpdate; try DBGrid2.Columns.Assign(DBGrid1.Columns); finally DBGrid2.Columns.EndUpdate; end; } end; var AX: Integer =0; Drawing: Boolean = False; IsFirstDrawing: Boolean = False; IsColumnMoving: Boolean = False; procedure TForm1.DBGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin // タイトル間のクリックは、ここでは取得できません。 // タイトルのクリックは、取得できます。 IsColumnMoving := True; end; // タイトル幅変更時のライン描画 procedure TForm1.DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); var OldPen: TPen; begin if Drawing then begin OldPen := TPen.Create; try with DBGrid2.Canvas do begin OldPen.Assign(Pen); try Pen.Style := psDot; Pen.Mode := pmXor; Pen.Width := 1; if not IsFirstDrawing then begin // 一度目はこの処理をさせません。 MoveTo(AX, 0); LineTo(AX, 0 + DBGrid2.Height); end else if IsFirstDrawing then IsFirstDrawing := False; MoveTo(X, 0); LineTo(X, 0 + DBGrid2.Height); AX := X; finally Pen := OldPen; end; end; finally OldPen.Free; end; end; end; // カラム幅の同期 - DBGrid1のカラム幅の変更をDBGrid2に反映させます。 procedure TForm1.DBGrid1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var I: Integer; Cell: TGridCoord; begin IsColumnMoving := False; // MouseDownでなければ常にOK Drawing := False; IsFirstDrawing := False; // カラム幅の同期 // カラムをクリックされた場合のみチェックします。 // 本来、Cell.Yは0が返されるべきですが、時々1が返ってきます。 // そのためCell.Y in [0,1]としています。 Cell := DBGrid1.MouseCoord(X, Y); if Cell.Y in [0,1] then begin for I := 0 to DBGrid1.Columns.Count -1 do begin if DBGrid2.Columns[I].Width <> DBGrid1.Columns[I].Width then DBGrid2.Columns[I].Width := DBGrid1.Columns[I].Width; end; end; end; // 横スクロールバーの同期 - DBGrid2の変更をDBGrid1に反映します。 procedure TForm1.SubClass1MessageAfter(Sender: TObject; var message: TMessage); begin case Message.Msg of WM_HSCROLL: DBGrid1.Perform(Message.Msg, Message.WParam, Message.LParam); end; end; // こちらは単純にDBGrid1のMouseDownを取得しているだけです。 // カラム間のクリックは、DBGridのOnMouseDownでは取得できなかった。 procedure TForm1.SubClass2MessageAfter(Sender: TObject; var message: TMessage); begin case Message.Msg of WM_LBUTTONDOWN: begin if (Message.LParamHi < RowHeight) and (not IsColumnMoving) then begin Drawing := True; IsFirstDrawing:=True; end; end; end; end; end.
サブクラス用コンポーネント
Delphi Library [Mr.XRAY]
SubClassUnit
http://homepage2.nifty.com/Mr_XRAY/Halbow/Notes/N004.html
20111005追記
DBGrid1のMouseUp、FormResizeの処理を下記に入れ替えると、そこそこ希望の動作になりました。スクロールバーは表示されるもののUpdateScrollBarを通していないので、レコード数が反映されたものではないですけどね。まあ、見た目的には問題ないです。
// カラム幅の同期 - DBGrid1のカラム幅の変更をDBGrid2に反映させます。 procedure TForm1.DBGrid1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var I: Integer; Cell: TGridCoord; begin IsFirstDrawing := False; // カラム移動の終了 if IsColumnMoving then begin ShowScrollBar(DBGrid1.Handle, SB_VERT, True); IsColumnMoving := False; DBGrid1ColEnter(Self); // DBGrid2の横スクロールバーが動くから end; // カラム幅の同期 if Drawing then begin Drawing := False; // カラム幅を変更-終了 DBGrid2.Columns.BeginUpdate; try for I := 0 to DBGrid1.Columns.Count -1 do begin if DBGrid2.Columns[I].Width <> DBGrid1.Columns[I].Width then DBGrid2.Columns[I].Width := DBGrid1.Columns[I].Width; end; finally DBGrid2.Columns.EndUpdate; DBGrid1ColEnter(Self); // DBGrid2の横スクロールバーが動くから end; ShowScrollBar(DBGrid1.Handle, SB_VERT, True); end; end; // サイズ変更時の処理 procedure TForm1.FormResize(Sender: TObject); begin DBGrid2ColEnter(Self); ShowScrollBar(DBGrid1.Handle, SB_VERT, True); end;
| 固定リンク