Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年07月28日] [更新 2012年07月28日] Delphi サンプルプログラム集
062_TDBGrid のセル値のコピー
動作確認等 Windows 7 U64(SP1) + Delphi XE Pro ( BDE 5.2 + Paradox 7.0 )
062_DBGrid_CellCopy.zip [1,722 KB] 2012年07月28日版 (EXE 同梱)







TDBGrid のセル値のコピー


[050_TStringGrid とカスタム描画] で セルの値をクリップボードに出力したり,ディクスに保存するサンプルを掲載しました.本ページは,その TDBGrid 版です.
実際に操作するのは「セルの値」ではなく,データセットの値です.列に相当するのは,各レコードの各項目(フィールド)で,行がレコードに相当します.

取得可能な値は,Value プロパティと,DisplayText プロパティがあります.DisplayText の値は,デーベースコントロール等に表示する際の設定値です.これらの値は,TDBGrid にデータを表示する,しないにかかわらず取得できます.

データベースあるいはデータベースコントロールによっては,このようなデータの出力用のメソッド類を実装しているものがあります.





01_セルのデータをクリップボードにコピー


各フィールドの値を String 型で取り出し,TStringList のリストに格納し,そのテキストをクリップボードにコピーします.TStringList をファイルに保存すれば,ディスクへの保存となります.

ここでは,各セルに表示されているデータではなく,データセットの値のコピーとなっています.そのためメモ型の項目の内容もクリップボードにコピーします.これを裂けるには,項目の種類を判定してメモ型の項目等を除外する必要があります.
セルに表示されている値をコピーする場合は,以下のように DisplayText に変更するか,DataSet の代わりに DBGrid1 にします.対象を DBGrid1 にすれば,DBGrid1 の DisplayText の値に対する処理になります.

リスト1
Fields[i] が列に相当し,DisplayText がその列の値ということになる

    //データをデリミタ文字で区切ってリストに追加
    for i := 0 to Dataset.FieldCount - 1 do begin
      ColList.Add(DataSet.Fields[i].DisplayText);
    end;


取り出されるデータの順番は,TDBGrid に表示されている列の順となります.したがって,マウスのドラッグ操作で列を移動すると,移動後の例順となります.取り出す順番を固定するには,取り出す項目(フィールド) の名前,例えば,FieldByName で直接指定します.


[備考]
TDataSet.Fielss の各項目の DisplayText の値と,TDBGrid.Fields の各項目の DisplayText の値が必ずしも同じとは限りません.TQuery 等のデータセット関係のコンポーネントには項目の設定があり,TDBGrid にも各項目設定用のカラムエディタがあります.



図1
設計時画面
図2
実行画面
  • インジケータの行が現在のレコード
  • このレコードの内容をコピーする
  • タブ区切りのテキストは,エクセルのセルへの貼り付けが可能

リスト2
セルのデータをクリップボードにコピー
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Db, DBTables, Grids, DBGrids, ExtCtrls, Buttons;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Panel1: TPanel;
    Query1: TQuery;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses Clipbrd;

{$R *.DFM}

type
  TMyDBGrid = class(TDBGrid);

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Query1.Close;

  DataSource1.DataSet := Query1;
  DBGrid1.DataSource  := DataSource1;
  DBGrid1.Options     := DBGrid1.Options - [dgEditing];
  DBGrid1.Options     := DBGrid1.Options + [dgRowSelect];

  Query1.DatabaseName := 'DBDEMOS';
  Query1.SQL.Clear;
  Query1.SQL.Add('select * from customer.db');
  Query1.Open;
end;

//=============================================================================
//  [タブ形式 現レコード]
//  TQueryの現在のレコードのデータをクリップボードにコピー
//  タブ形式はエクセルのセルに直接貼り付けることができる
//=============================================================================
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  Dataset : TDataSet;
  SL      : TStringList;
  ColList : TStringList;
  i       : Integer;
begin
  //TDBGridに表示しているデータセット取得
  Dataset := DBGrid1.DataSource.DataSet;

  SL      := TStringList.Create;
  ColList := TStringList.Create;

  //デリミタの設定
  ColList.Delimiter := Chr(VK_TAB);

  try
    //データをデリミタ文字で区切ってリストに追加
    for i := 0 to Dataset.FieldCount - 1 do begin
      ColList.Add(Dataset.Fields[i].AsString);
    end;
    SL.Add(ColList.DelimitedText);
    Clipboard.AsText := SL.Text;
  finally
    FreeAndNil(SL);
    FreeAndNil(ColList);
  end;

  DBGrid1.SetFocus;
end;

//=============================================================================
//  [タブ形式 全レコード]
//  TQueryの全レコードのデータをクリップボードにコピー
//  タブ形式はエクセルのセルに直接貼り付けることができる
//=============================================================================
procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  RowCount     : Integer;
  RN           : Integer;
  td-dateRecNo  : Integer;
  CurrentRowNo : Integer;
  Dataset      : TDataSet;
  SL           : TStringList;
  ColList      : TStringList;
  i            : Integer;
begin
  //TDBGridに表示しているデータセット取得
  Dataset := DBGrid1.DataSource.DataSet;

  //表示更新を止める
  Dataset.DisableControls;

  //----- DBGridのスクロール状態を元に戻すための変数設定 ---------------------
  CurrentRowNo := TMyDBGrid(DBGrid1).Row - 1;
  td-dateRecNo  := Dataset.RecNo - CurrentRowNo;
  //---------------------------------------------------------------------------


  SL      := TStringList.Create;
  ColList := TStringList.Create;

  //デリミタの設定
  ColList.Delimiter := Chr(VK_TAB);

  try
    //全てのレコードに対して操作
    while (Dataset.Eof = False) do begin
      //データをデリミタ文字で区切って行単位でリストに追加
      ColList.Clear;
      for i := 0 to Dataset.FieldCount - 1 do begin
        ColList.Add(Dataset.Fields[i].AsString);
      end;
      SL.Add(ColList.DelimitedText);
      Dataset.Next;
    end;
    Clipboard.AsText := SL.Text;


    //----- DBGridのスクロール状態を元に戻すための処理 ---------------------
    RowCount     := TMyDBGrid(DBGrid1).VisibleRowCount;
    Dataset.RecNo := td-dateRecNo;
    Dataset.MoveBy(RowCount);

    while Dataset.BOF = False do begin
      RN := Dataset.RecNo;
      if RN = td-dateRecNo then begin
        Break;
      end else begin
        Dataset.Prior;
      end;
    end;
    Dataset.MoveBy(CurrentRowNo);
    //-------------------------------------------------------------------------

  finally
    FreeAndNil(SL);
    FreeAndNil(ColList);
  end;
  //表示更新再開
  Dataset.EnableControls;

  DBGrid1.SetFocus;
end;

//=============================================================================
//  [CSV形式 現レコード]
//  TQueryの現在のレコードのデータをクリップボードにコピー
//=============================================================================
procedure TForm1.SpeedButton3Click(Sender: TObject);
var
  Dataset : TDataSet;
  SL      : TStringList;
  ColList : TStringList;
  i       : Integer;
begin
  //TDBGridに表示しているデータセット取得
  Dataset := DBGrid1.DataSource.DataSet;

  SL      := TStringList.Create;
  ColList := TStringList.Create;

  //デリミタの設定
  ColList.Delimiter := ',';

  try
    //データをデリミタ文字で区切ってリストに追加
    for i := 0 to Dataset.FieldCount - 1 do begin
      ColList.Add(Dataset.Fields[i].AsString);
    end;
    SL.Add(ColList.DelimitedText);
    Clipboard.AsText := SL.Text;
  finally
    FreeAndNil(SL);
    FreeAndNil(ColList);
  end;

  DBGrid1.SetFocus;
end;

end.




02_現レコードの値をコピーしてレコード追加


現在のレコードの値を複写しておき,レコードを追加した時に,追加したレコードの各フィールド(項目)値にセットするサンプルです.
一度にセットするのではなく,入力項目を移動する度に,前のレコードの値を参考用に表示するという用途にも利用できます.データの種類には,数値型もありますから,全てを文字列型で扱うというわけにはいきません.そこで,バリアント配列を作成して操作しています.


図3
設計時画面
図4
適当なレコードを選択
  • インジケータの行が選択レコード
図5
[レコード追加]をクリック
  • 選択レコードと同じデータのレコードが追加される
  • ただし,キー項目の値はコードで変更している
図6
[レコード削除]をクリック
  • テストなので,追加したレコードを削除してからプログラムを閉じる

リスト3
現レコードの値をコピーしてレコード追加
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Db, DBTables, Grids, DBGrids, ExtCtrls;

type
  TForm1 = class(TForm)
    DataSource1: TDataSource;
    Panel1: TPanel;
    Button1: TButton;
    Query1: TQuery;
    DBGrid1: TDBGrid;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Query1.Close;
  //Query1を編集,更新可能にしておく
  Query1.RequestLive := True;

  DataSource1.DataSet := Query1;
  DBGrid1.DataSource  := DataSource1;

  Query1.DatabaseName := 'DBDEMOS';
  Query1.SQL.Clear;
  Query1.SQL.Add('select * from customer.db');
  Query1.Open;

  ActiveControl := DBGrid1;
end;

//=============================================================================
//  フォームを閉じる時の処理
//=============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  Query1.Close;
end;

//-----------------------------------------------------------------------------
//  引数のDataSetにレコードを追加する関数
//  現在のレコードのフィードの値をコピーして,それを新しいレコードの値とする
//  バリアント型であれば,変数の型を意識する必要がない
//  ただし,キー項目等,重複する値はデータの更新が出来ないので注意
//
//  (1) 現在のレコードの値をバリアント型の配列に代入
//  (2) レコードを追加
//  (3) バリアント配列に格納した値を,追加レコードの各フィールドに代入
//-----------------------------------------------------------------------------
procedure AppendCurrentData(Dataset: TDataset);
var
  FieldList : Variant;
  i         : Integer;
begin
  //バリアント型の配列を作成

  FieldList := VarArrayCreate([0, DataSet.FieldCount - 1], VarVariant);

  for i := 0 to DataSet.FieldCount - 1 do begin
    FieldList[i] := DataSet.Fields[i].Value;
  end;

  DataSet.Append;
  for i := 0 to DataSet.FieldCount -1 do begin
    DataSet.Fields[i].Value := FieldList[i];
  end;
end;

//=============================================================================
//  [レコード追加]
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Dataset : TDataSet;
begin
  //TDBGridに表示しているデータセット取得
  Dataset := DBGrid1.DataSource.DataSet;

  AppendCurrentData(Dataset);
  //キー項目は重複して登録できないので変更
  Dataset.FieldByName('CustNo').Value := '100000';
  Dataset.Last;
  DBGrid1.SetFocus;
end;

//=============================================================================
//  [レコード削除]
//  テストなので,追加したレコードは削除しておいた方がいいだろう
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Dataset : TDataSet;
begin
  //TDBGridに表示しているデータセット取得
  Dataset := DBGrid1.DataSource.DataSet;

  Dataset.Delete;
  DBGrid1.SetFocus;
end;

end.