« MyBaseを試してみる。(インデックスを使わずに、行の追加、挿入、削除編) REVENGE(^o^) | トップページ | MyBaseを試してみる。(BLOBフィールドで画像の読み書き) »

DBGridを同期させる。

この時期、なぜ、XE2の新機能やFireMonkeyの試行錯誤をやらないの?と突っ込まれそうですが、古いプログラムの修正用にいろいろ調べて直しているからです(^-^)

図のように上下に配置したDBGridのタイトルの幅変更、移動、セルの移動そして横スクロールを同期させます。Excelのように上段のDBGridは横スクロールバー無し、下段のDBGridはタイトル無しとします。若干、問題が残っていますが、とりあえずは良しとしましょう。

設計時

Design

実行時

Runtime


問題点
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;

|

« MyBaseを試してみる。(インデックスを使わずに、行の追加、挿入、削除編) REVENGE(^o^) | トップページ | MyBaseを試してみる。(BLOBフィールドで画像の読み書き) »